Class: Card::Action

Inherits:
ApplicationRecord show all
Extended by:
Admin
Includes:
Differ
Defined in:
mod/history/lib/card/action.rb,
mod/history/lib/card/action/admin.rb,
mod/history/lib/card/action/differ.rb,
mod/history/lib/card/action/action_renderer.rb

Overview

An action is a group of changes to a single card that is recorded during an act. Together, acts, actions, and changes comprise a comprehensive card history tracking system.

For example, if a given web submission changes both the name and type of a given card, that would be recorded as one action with two changes. If there are multiple cards changed, each card would have its own action, but the whole submission would still comprise just one single act.

An Action records:

  • the card_id of the card acted upon
  • the card_act_id of the act of which the action is part
  • the action_type (create, update, or delete)
  • a boolean indicated whether the action is a draft
  • a comment (where applicable)

Defined Under Namespace

Modules: Admin, Differ Classes: ActionRenderer

Constant Summary collapse

TYPE_OPTIONS =

these are the three possible values for action_type

%i[create update delete].freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Admin

delete_cardless, delete_old, make_current_state_the_initial_state

Methods included from Differ

#cardtype_diff, #content_diff, #green?, #name_diff, #new_content?, #new_name?, #new_type?, #raw_view, #red?, #summary_diff_omits_content?

Class Method Details

.all_viewableObject



65
66
67
# File 'mod/history/lib/card/action.rb', line 65

def all_viewable
  all_with_cards.where Query::CardQuery.viewable_sql
end

.all_with_cardsObject



61
62
63
# File 'mod/history/lib/card/action.rb', line 61

def all_with_cards
  joins :ar_card
end

.cacheCard::Cache

cache object for actions

Returns:



57
58
59
# File 'mod/history/lib/card/action.rb', line 57

def cache
  Card::Cache[Action]
end

.fetch(id) ⇒ Action?

retrieve action from cache if available

Parameters:

Returns:



49
50
51
52
53
# File 'mod/history/lib/card/action.rb', line 49

def fetch id
  cache.fetch id.to_s do
    find id.to_i
  end
end

Instance Method Details

#action_typeSymbol

retrieve action_type (create, update, or delete)

Returns:

  • (Symbol)


107
108
109
110
111
# File 'mod/history/lib/card/action.rb', line 107

def action_type
  return :draft if draft

  TYPE_OPTIONS[read_attribute(:action_type)]
end

#action_type=(value) ⇒ Integer

assign action_type (create, update, or delete)

Parameters:

  • value (Symbol)

Returns:

  • (Integer)


101
102
103
# File 'mod/history/lib/card/action.rb', line 101

def action_type= value
  write_attribute :action_type, TYPE_OPTIONS.index(value)
end

#all_changesObject



158
159
160
161
162
163
# File 'mod/history/lib/card/action.rb', line 158

def all_changes
  self.class.cache.fetch("#{id}-changes") do
    # using card_changes causes caching problem
    Card::Change.where(card_action_id: id).to_a
  end
end

#cardCard

each action is associated with on and only one card

Returns:



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'mod/history/lib/card/action.rb', line 72

def card
  Card.fetch card_id, look_in_trash: true

  # I'm not sure what the rationale for the following was/is, but it was causing
  # problems in cases where slot attributes are overridden (eg see #wrap_data in
  # sources on wikirate).  The problem is the format object had the set modules but
  # the card didn't.
  #
  # My guess is that the need for the following had something to do with errors
  # associated with changed types. If so, the solution probably needs to handle
  # including the set modules associated with the type at the time of the action
  # rather than including no set modules at all.
  #
  # What's more, we _definitely_ don't want to hard code special behavior for
  # specific types in here!

  # , skip_modules: true
  # return res unless res && res.type_id.in?([Card::FileID, Card::ImageID])
  # res.include_set_modules
end

#change(field) ⇒ Change

action's Change object for given field



139
140
141
# File 'mod/history/lib/card/action.rb', line 139

def change field
  changes[interpret_field field]
end

#changed_valuesObject

all changed values in hash form. { field1: new_value }



179
180
181
182
183
# File 'mod/history/lib/card/action.rb', line 179

def changed_values
  @changed_values ||= changes.each_with_object({}) do |(key, change), h|
    h[key] = change.value
  end
end

#changesHash

all action changes in hash form. { field1: Change1 }

Returns:

  • (Hash)


167
168
169
170
171
172
173
174
175
176
# File 'mod/history/lib/card/action.rb', line 167

def changes
  @changes ||=
    if sole?
      current_changes
    else
      all_changes.each_with_object({}) do |change, hash|
        hash[change.field.to_sym] = change
      end
    end
end

#current_changesHash

Returns:

  • (Hash)


186
187
188
189
190
191
192
193
194
195
# File 'mod/history/lib/card/action.rb', line 186

def current_changes
  return {} unless card

  @current_changes ||=
    Card::Change::TRACKED_FIELDS.each_with_object({}) do |field, hash|
      hash[field.to_sym] = Card::Change.new field: field,
                                            value: card.send(field),
                                            card_action_id: id
    end
end

#expireObject

remove action from action cache



94
95
96
# File 'mod/history/lib/card/action.rb', line 94

def expire
  self.class.cache.delete id.to_s
end

#interpret_field(field) ⇒ Symbol

translate field into fieldname as referred to in database

Parameters:

  • field (Symbol)

    can be :type_id, :cardtype, :db_content, :content, :name, :trash

Returns:

  • (Symbol)

See Also:



202
203
204
205
206
207
208
# File 'mod/history/lib/card/action.rb', line 202

def interpret_field field
  case field
  when :content then :db_content
  when :cardtype then :type_id
  else field.to_sym
  end
end

#interpret_value(field, value) ⇒ Integer, ...

value in form prescribed for specific field name

Parameters:

Returns:

  • (Integer)

    for :type_id

  • (String)

    for :name, :db_content, :content, :cardtype

  • (True/False)

    for :trash



215
216
217
218
219
220
221
222
223
# File 'mod/history/lib/card/action.rb', line 215

def interpret_value field, value
  case field.to_sym
  when :type_id
    value&.to_i
  when :cardtype
    Card.fetch_name(value&.to_i)
  else value
  end
end

#previous_actionObject



113
114
115
# File 'mod/history/lib/card/action.rb', line 113

def previous_action
  Card::Action.where("id < ? AND card_id = ?", id, card_id).last
end

#previous_change(field) ⇒ Change

most recent change to given field before this one



146
147
148
149
150
151
152
153
154
155
156
# File 'mod/history/lib/card/action.rb', line 146

def previous_change field
  return nil if action_type == :create

  field = interpret_field field
  if @previous_changes&.key?(field)
    @previous_changes[field]
  else
    @previous_changes ||= {}
    @previous_changes[field] = card.last_change_on field, before: self
  end
end

#previous_value(field) ⇒ Object

value of field set by most recent Change before this one



129
130
131
132
133
134
# File 'mod/history/lib/card/action.rb', line 129

def previous_value field
  return if action_type == :create
  return unless (previous_change = previous_change field)

  interpret_value field, previous_change.value
end

#sole?Boolean

Returns:

  • (Boolean)


225
226
227
228
# File 'mod/history/lib/card/action.rb', line 225

def sole?
  all_changes.empty? &&
    (action_type == :create || Card::Action.where(card_id: card_id).count == 1)
end

#value(field) ⇒ Object

value set by action's Change to given field



120
121
122
123
124
# File 'mod/history/lib/card/action.rb', line 120

def value field
  return unless (change = change field)

  interpret_value field, change.value
end