Class: Card::Director
- Extended by:
- ClassMethods
- Defined in:
- card/lib/card/director.rb,
card/lib/card/director/all.rb,
card/lib/card/director/run.rb,
card/lib/card/director/store.rb,
card/lib/card/director/phases.rb,
card/lib/card/director/stages.rb,
card/lib/card/director/card_class.rb,
card/lib/card/director/event_delay.rb,
card/lib/card/director/class_methods.rb,
card/lib/card/director/subdirector_array.rb
Overview
Directs the symphony of a card act.
Each act is divided into actions: one action for each card. There are three action types: create, update, and delete.
Each action is divided into three phases: validation, storage, and integration.
Each phase is divided into three stages, as follows:
Validation Stages
- VI: initialize
- VP: prepare_to_validate
- VV: validate
Storage Stages
- SP: prepare_to_store
- SS: store
- SF: finalize
Integration Stages
- II: integrate
- IA: after_integrate
- ID: integrate_with_delay
And each stage can have many events, each of which is defined using the Event API.
The table below gives you an overview events can/should do in each stage:
Phase: | validation | storage | integration |
---|---|---|---|
Stage: | VI - VP - VV | SP - SS - SF | II - IA - ID |
tasks | |||
attach subcard | yes! yes! yes | yes yes yes | yes yes no |
detach subcard | yes! yes! yes | yes no no! | no! |
validate | yes yes yes! | no | no |
insecure change 1 | yes yes! no | no! | no! |
secure change 2 | yes | yes! no! no! | no! |
abort | yes! | yes | yes |
add errors | yes! | no! | no! |
subsave | yes | yes | yes! |
has id (new card) | no | no no? yes | yes |
within web request | yes | yes | yes yes no |
within transaction 3 | yes | yes | no |
values | |||
dirty attributes | yes | yes | yes |
params | yes | yes | yes |
success | yes | yes | yes |
session | yes | yes | yes yes no |
Understanding the Table
- yes! the recommended stage to do that
- yes ok to do it here
- no not recommended; risky but not guaranteed to fail
- no! never do it here. it won’t work or will break things
If there is only a single entry in a phase column it counts for all stages of that phase
Director, Directors, and Subdirectors
Only one act can be performed at a time in any given Card process. Information about that act is managed by Director class methods. Every act is associated with a single “main” card.
The act, however, may involve many cards/actions. Each action has its own Director instance that leads the card through all its stages. When a card action (A1) initiates a new action on a different card (A2), a new Director object is initialized. The new A2 subdirector’s @parent is the director of the A1 card. Conversely, the A1 card stores a SubdirectorArray in @subdirectors to give it access to A2’s Director and any little Director babies to which it gave birth.
Subdirectors follow one of two distinct patterns:
- Subcards. When a card is altered using the subcards API, the director follows a “breadth-first” pattern. For each stage a card runs its stage events and then triggers its subcards to run that stage before proceeding to the next stage. If a subcard is added in a stage then by the end of that stage the director will catch it up to the current stage.
- Subsaves. When a card is altered by a direct save (
Card.create(!)
,card.update(!)
,card.delete(!)
,card.save(!)
…), then the validation and storage phases are executed immediately (depth-first), returning the saved card. The integration phase, however, is executed following the same pattern as with subcards.
Let’s consider a subcard example. Suppose you define the following event on self/bar.rb
event :met_a_foo_at_the_bar, :prepare_to_store, on: :update do
subcard "foo"
end
And then you run Card[:bar].update!({})
.
When bar reaches the event in its prepare_to_store
stage, the “foo” subcard will be
added. After that stage ends, the stages initialize
, prepare_to_validate
,
validate
, and prepare_to_store
are executed for foo so that it is now caught
up with Bar at the prepare_to_store
stage.
If you have subcards within subcards, stages are executed preorder depth-first.
Eg, assuming:
- A has subcards AA and AB
- AA has subcard AAA
- AB has subcard ABA
…then the order of execution is:
- A
- AA
- AAA
- AB
- ABA
A special case can happen in the store stage when a supercard needs a subcard’s id (for example as left_id or as type_id) and the subcard doesn’t have an id yet (because it gets created in the same act). In this case the subcard’s store stage is executed BEFORE the supercard’s store stage.
-
‘insecure’ means a change that might make the card invalid to save ↩
-
‘secure’ means you’re sure that the change won’t invalidate the card ↩
-
If an exception is raised in the validation or storage phase everything will rollback. If an integration event fails, db changes of the other two phases will remain persistent, and other integration events will continue to run. ↩
Defined Under Namespace
Modules: All, CardClass, ClassMethods, EventDelay, Phases, Run, Stages, Store Classes: SubdirectorArray
Constant Summary
Constants included from Stages
Stages::INDECES, Stages::SYMBOLS
Instance Attribute Summary collapse
-
#act ⇒ Object
Returns the value of attribute act.
-
#card ⇒ Object
Returns the value of attribute card.
-
#current_stage_index ⇒ Object
Returns the value of attribute current_stage_index.
-
#head ⇒ Object
Returns the value of attribute head.
-
#parent ⇒ Object
Returns the value of attribute parent.
-
#running ⇒ Object
(also: #running?)
readonly
Returns the value of attribute running.
-
#subdirectors ⇒ Object
Returns the value of attribute subdirectors.
Attributes included from ClassMethods
Instance Method Summary collapse
-
#abort ⇒ Object
-
#appoint(card) ⇒ Object
-
#delete ⇒ Object
-
#head? ⇒ Boolean
-
#initialize(card, parent) ⇒ Director
constructor
A new instance of Director.
-
#main? ⇒ Boolean
-
#main_director ⇒ Object
-
#need_act ⇒ Object
-
#register ⇒ Object
-
#replace_card(card) ⇒ Object
-
#to_s(level = 1) ⇒ Object
-
#unregister ⇒ Object
-
#update_card(card) ⇒ Object
Methods included from ClassMethods
act_director, add, card_changed, clear, deep_delete, directors, expire, expirees, fetch, include?, include_id?, run_act
Methods included from EventDelay
#contextualize_delayed_event, #delaying?, #run_job_with_act, #with_delay_act, #with_env_and_auth
Methods included from Store
Methods included from Run
#catch_up_to_stage, #delay!, #restart, #run_delayed_event
Methods included from Phases
#integration_phase, #integration_phase_callback?, #prepare_for_phases, #storage_phase, #validation_phase, #validation_phase_callback?
Methods included from Stages
#finished_stage?, #reset_stage, #stage_index, #stage_ok?, #stage_symbol
Constructor Details
#initialize(card, parent) ⇒ Director
Returns a new instance of Director.
147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'card/lib/card/director.rb', line 147 def initialize card, parent @card = card @card.director = self # for read actions there is no validation phase # so we have to set the action here @current_stage_index = nil @running = false @prepared = false @parent = parent @subdirectors = SubdirectorArray.initialize_with_subcards(self) register end |
Instance Attribute Details
#act ⇒ Object
Returns the value of attribute act.
143 144 145 |
# File 'card/lib/card/director.rb', line 143 def act @act end |
#card ⇒ Object
Returns the value of attribute card.
143 144 145 |
# File 'card/lib/card/director.rb', line 143 def card @card end |
#current_stage_index ⇒ Object
Returns the value of attribute current_stage_index.
143 144 145 |
# File 'card/lib/card/director.rb', line 143 def current_stage_index @current_stage_index end |
#head ⇒ Object
Returns the value of attribute head.
143 144 145 |
# File 'card/lib/card/director.rb', line 143 def head @head end |
#parent ⇒ Object
Returns the value of attribute parent.
143 144 145 |
# File 'card/lib/card/director.rb', line 143 def parent @parent end |
#running ⇒ Object (readonly) Also known as: running?
Returns the value of attribute running.
144 145 146 |
# File 'card/lib/card/director.rb', line 144 def running @running end |
#subdirectors ⇒ Object
Returns the value of attribute subdirectors.
143 144 145 |
# File 'card/lib/card/director.rb', line 143 def subdirectors @subdirectors end |
Instance Method Details
#abort ⇒ Object
191 192 193 |
# File 'card/lib/card/director.rb', line 191 def abort @abort = true end |
#appoint(card) ⇒ Object
185 186 187 188 189 |
# File 'card/lib/card/director.rb', line 185 def appoint card reset_stage update_card card @head = true end |
#delete ⇒ Object
176 177 178 179 180 181 182 183 |
# File 'card/lib/card/director.rb', line 176 def delete @parent&.subdirectors&.delete self @card.director = nil @subdirectors.clear @current_stage_index = nil @action = nil @running = false end |
#head? ⇒ Boolean
164 165 166 |
# File 'card/lib/card/director.rb', line 164 def head? @head || main? end |
#main? ⇒ Boolean
160 161 162 |
# File 'card/lib/card/director.rb', line 160 def main? parent.nil? end |
#main_director ⇒ Object
202 203 204 205 206 |
# File 'card/lib/card/director.rb', line 202 def main_director return self if main? Director.act_director || @parent&.main_director end |
#need_act ⇒ Object
195 196 197 198 199 200 |
# File 'card/lib/card/director.rb', line 195 def need_act act_director = main_director raise Card::Error, "act requested without a main director" unless act_director @act = act_director.act ||= Director.need_act end |
#register ⇒ Object
168 169 170 |
# File 'card/lib/card/director.rb', line 168 def register Director.add self end |
#replace_card(card) ⇒ Object
217 218 219 220 221 222 223 |
# File 'card/lib/card/director.rb', line 217 def replace_card card card.action = @card.action card.director = self @card = card reset_stage catch_up_to_stage @current_stage_index if @current_stage_index end |
#to_s(level = 1) ⇒ Object
208 209 210 211 212 213 214 215 |
# File 'card/lib/card/director.rb', line 208 def to_s level=1 str = @card.name.to_s.clone if @subdirectors.present? subs = subdirectors.map { |d| " " * level + d.to_s(level + 1) }.join "\n" str << "\n#{subs}" end str end |
#unregister ⇒ Object
172 173 174 |
# File 'card/lib/card/director.rb', line 172 def unregister Director.delete self end |
#update_card(card) ⇒ Object
225 226 227 228 229 |
# File 'card/lib/card/director.rb', line 225 def update_card card old_card = @card @card = card Director.card_changed old_card end |