Add postponable concern

This commit is contained in:
Jorge Manrubia
2025-09-24 14:37:14 +02:00
parent f1da9a6c5f
commit b63a6b3e97
9 changed files with 119 additions and 138 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
class Card < ApplicationRecord
include Assignable, Attachments, Cacheable, Closeable, Colored, Engageable, Entropic, Eventable,
Golden, Mentions, Multistep, Pinnable, Promptable, Readable, Searchable,
Golden, Mentions, Multistep, Pinnable, Postponable, Promptable, Readable, Searchable,
Staged, Stallable, Statuses, Taggable, Watchable
belongs_to :collection, touch: true
-75
View File
@@ -1,75 +0,0 @@
module Card::Engageable
extend ActiveSupport::Concern
included do
has_one :engagement, dependent: :destroy, class_name: "Card::Engagement"
scope :considering, -> { published_or_drafted_by(Current.user).open.where.missing(:engagement) }
scope :on_deck, -> { published.open.joins(:engagement).where(engagement: { status: "on_deck" }) }
scope :doing, -> { published.open.joins(:engagement).where(engagement: { status: "doing" }) }
scope :by_engagement_status, ->(status) do
case status.to_s
when "considering" then considering.with_golden_first
when "on_deck" then on_deck.with_golden_first
when "doing" then doing.with_golden_first
end
end
end
def doing?
open? && published? && engagement&.status == "doing"
end
def on_deck?
open? && published? && engagement&.status == "on_deck"
end
def considering?
open? && published? && engagement.blank?
end
def engagement_status
if doing?
"doing"
elsif on_deck?
"on_deck"
elsif considering?
"considering"
end
end
def engage
unless doing?
reengage(status: "doing")
end
end
def move_to_on_deck
unless on_deck?
reengage(status: "on_deck")
end
end
def reconsider
transaction do
reopen
engagement&.destroy
activity_spike&.destroy
update!(stage: nil)
touch_last_active_at
end
end
private
def reengage(status:)
transaction do
reopen
engagement&.destroy
create_engagement!(status:)
if status == "doing" && stage.blank?
update!(stage: collection.initial_workflow_stage)
end
end
end
end
+3
View File
@@ -0,0 +1,3 @@
class Card::NotNow < ApplicationRecord
belongs_to :card, class_name: "::Card", touch: true
end
+30
View File
@@ -0,0 +1,30 @@
module Card::Postponable
extend ActiveSupport::Concern
included do
has_one :not_now, dependent: :destroy, class_name: "Card::NotNow"
scope :not_now, -> { joins(:not_now) }
scope :active, -> { where.missing(:not_now) }
end
def postponed?
not_now.present?
end
def active?
!postponed?
end
def postpone
unless postponed?
create_not_now!
end
end
def resume
if postponed?
not_now.destroy
end
end
end
@@ -0,0 +1,8 @@
class CreateCardNotNows < ActiveRecord::Migration[8.1]
def change
create_table :card_not_nows do |t|
t.references :card, null: false, foreign_key: true, index: { unique: true }
t.timestamps
end
end
end
Generated
+9 -1
View File
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.1].define(version: 2025_09_24_100149) do
ActiveRecord::Schema[8.1].define(version: 2025_09_24_123001) do
create_table "accesses", force: :cascade do |t|
t.datetime "accessed_at"
t.integer "collection_id", null: false
@@ -131,6 +131,13 @@ ActiveRecord::Schema[8.1].define(version: 2025_09_24_100149) do
t.index ["card_id"], name: "index_card_goldnesses_on_card_id", unique: true
end
create_table "card_not_nows", force: :cascade do |t|
t.integer "card_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["card_id"], name: "index_card_not_nows_on_card_id", unique: true
end
create_table "cards", force: :cascade do |t|
t.integer "collection_id", null: false
t.integer "column_id"
@@ -519,6 +526,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_09_24_100149) do
add_foreign_key "ai_quotas", "users"
add_foreign_key "card_activity_spikes", "cards"
add_foreign_key "card_goldnesses", "cards"
add_foreign_key "card_not_nows", "cards"
add_foreign_key "cards", "columns"
add_foreign_key "cards", "workflow_stages", column: "stage_id"
add_foreign_key "closures", "cards"
+25 -1
View File
@@ -433,6 +433,11 @@ columns:
- *5
- *6
- *9
card_not_nows:
- *23
- *5
- *6
- *9
cards:
- *24
- !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
@@ -1514,6 +1519,7 @@ primary_keys:
card_activity_spikes: id
card_engagements: id
card_goldnesses: id
card_not_nows: id
cards: id
closers_filters:
closure_reasons: id
@@ -1569,6 +1575,7 @@ data_sources:
card_activity_spikes: true
card_engagements: true
card_goldnesses: true
card_not_nows: true
cards: true
closers_filters: true
closure_reasons: true
@@ -2000,6 +2007,23 @@ indexes:
nulls_not_distinct:
comment:
valid: true
card_not_nows:
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
table: card_not_nows
name: index_card_not_nows_on_card_id
unique: true
columns:
- card_id
lengths: {}
orders: {}
opclasses: {}
where:
type:
using:
include:
nulls_not_distinct:
comment:
valid: true
cards:
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
table: cards
@@ -3252,4 +3276,4 @@ indexes:
comment:
valid: true
workflows: []
version: 20250924100149
version: 20250924123001
-60
View File
@@ -1,60 +0,0 @@
require "test_helper"
class Card::EngageableTest < ActiveSupport::TestCase
setup do
Current.session = sessions(:david)
end
test "check the engagement status of a card" do
assert cards(:logo).doing?
assert_not cards(:text).doing?
assert_not cards(:logo).considering?
assert cards(:text).considering?
assert_not cards(:logo).on_deck?
assert_not cards(:text).on_deck?
assert_equal "doing", cards(:logo).engagement_status
assert_equal "considering", cards(:text).engagement_status
end
test "change the engagement" do
assert_changes -> { cards(:text).reload.doing? }, to: true do
cards(:text).engage
end
assert_changes -> { cards(:logo).reload.doing? }, to: false do
cards(:logo).reconsider
end
end
test "engaging with closed cards" do
cards(:text).close
assert_not cards(:text).considering?
assert_not cards(:text).doing?
assert_not cards(:text).on_deck?
cards(:text).engage
assert_not cards(:text).reload.closed?
assert cards(:text).doing?
cards(:text).close
cards(:text).reconsider
assert_not cards(:text).reload.closed?
assert cards(:text).considering?
end
test "scopes" do
assert_includes Card.doing, cards(:logo)
assert_not_includes Card.doing, cards(:text)
assert_includes Card.considering, cards(:text)
assert_not_includes Card.considering, cards(:logo)
cards(:text).move_to_on_deck
assert_includes Card.on_deck, cards(:text)
assert_not_includes Card.on_deck, cards(:logo)
end
end
+43
View File
@@ -0,0 +1,43 @@
require "test_helper"
class Card::PostponableTest < ActiveSupport::TestCase
setup do
Current.session = sessions(:david)
end
test "check the postponed status of a card" do
card = cards(:logo)
assert_not card.postponed?
assert card.active?
card.postpone
assert card.postponed?
assert_not card.active?
end
test "postpone and resume a card" do
card = cards(:text)
assert_changes -> { card.reload.postponed? }, to: true do
card.postpone
end
assert_changes -> { card.reload.postponed? }, to: false do
card.resume
end
end
test "scopes" do
logo = cards(:logo)
text = cards(:text)
logo.postpone
assert_includes Card.not_now, logo
assert_not_includes Card.not_now, text
assert_includes Card.active, text
assert_not_includes Card.active, logo
end
end