card 1.19.1 → 1.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -3
  3. data/VERSION +1 -1
  4. data/db/migrate_core_cards/20160908154210_add_trash_card.rb +7 -0
  5. data/lib/card.rb +2 -2
  6. data/lib/card/act_manager.rb +143 -0
  7. data/lib/card/act_manager/stage.rb +56 -0
  8. data/lib/card/act_manager/stage_director.rb +313 -0
  9. data/lib/card/act_manager/subdirector_array.rb +43 -0
  10. data/lib/card/set.rb +1 -0
  11. data/lib/card/set/i18n_scope.rb +52 -0
  12. data/lib/card/subcards.rb +2 -3
  13. data/lib/cardio.rb +4 -4
  14. data/mod/admin/set/self/admin.rb +1 -2
  15. data/mod/admin/set/self/trash.rb +29 -0
  16. data/mod/core/set/abstract/code_file.rb +5 -25
  17. data/mod/core/set/all/actify.rb +35 -0
  18. data/mod/core/set/all/fetch.rb +12 -15
  19. data/mod/core/set/all/i18n.rb +9 -0
  20. data/mod/core/set/all/phases.rb +1 -1
  21. data/mod/core/set/all/stages.rb +2 -2
  22. data/mod/developer/set/all/event_viz.rb +2 -1
  23. data/mod/email/set/all/notify.rb +1 -1
  24. data/mod/history/lib/card/act.rb +1 -1
  25. data/mod/history/lib/card/action.rb +1 -1
  26. data/mod/history/set/all/history.rb +1 -1
  27. data/mod/machines/set/abstract/machine.rb +1 -1
  28. data/mod/machines/spec/set/type/coffeescript_spec.rb +1 -1
  29. data/mod/machines/spec/set/type/css_spec.rb +1 -1
  30. data/mod/machines/spec/set/type/javascript_spec.rb +1 -1
  31. data/mod/machines/spec/set/type/scss_spec.rb +1 -1
  32. data/mod/machines/spec/set/type/skin_spec.rb +1 -1
  33. data/mod/machines/spec/{lib/shared_machine_examples.rb → shared_examples/machine.rb} +15 -24
  34. data/mod/machines/spec/{lib/shared_machine_input_examples.rb → shared_examples/machine_input.rb} +0 -0
  35. data/mod/settings/spec/set/right/script_spec.rb +1 -1
  36. data/mod/settings/spec/set/right/style_spec.rb +1 -4
  37. data/mod/solid_cache/set/abstract/solid_cache.rb +1 -1
  38. data/spec/lib/card/content_spec.rb +118 -132
  39. data/spec/lib/card/i18n_spec.rb +6 -7
  40. data/spec/lib/card/query_spec.rb +5 -4
  41. data/spec/lib/card/stage_director_spec.rb +1 -1
  42. data/spec/spec_helper.rb +3 -4
  43. data/tmpsets/set/{mod002-core → mod001-core}/abstract/code_file.rb +0 -0
  44. data/tmpsets/set/{mod002-core → mod001-core}/all/actify.rb +0 -0
  45. data/tmpsets/set/{mod002-core → mod001-core}/all/active_card.rb +0 -0
  46. data/tmpsets/set/{mod002-core → mod001-core}/all/collection.rb +0 -0
  47. data/tmpsets/set/{mod002-core → mod001-core}/all/content.rb +0 -0
  48. data/tmpsets/set/{mod002-core → mod001-core}/all/erb.rb +0 -0
  49. data/tmpsets/set/{mod002-core → mod001-core}/all/event.rb +0 -0
  50. data/tmpsets/set/{mod002-core → mod001-core}/all/export.rb +0 -0
  51. data/tmpsets/set/{mod002-core → mod001-core}/all/fetch.rb +12 -14
  52. data/tmpsets/set/{mod002-core → mod001-core}/all/haml.rb +0 -0
  53. data/tmpsets/set/{mod002-core → mod001-core}/all/initialize.rb +0 -0
  54. data/tmpsets/set/{mod002-core → mod001-core}/all/location_history.rb +0 -0
  55. data/tmpsets/set/{mod002-core → mod001-core}/all/name.rb +0 -0
  56. data/tmpsets/set/{mod002-core → mod001-core}/all/name_validations.rb +0 -0
  57. data/tmpsets/set/{mod002-core → mod001-core}/all/pattern.rb +0 -0
  58. data/tmpsets/set/{mod002-core → mod001-core}/all/permissions.rb +0 -0
  59. data/tmpsets/set/{mod002-core → mod001-core}/all/phases.rb +0 -0
  60. data/tmpsets/set/{mod002-core → mod001-core}/all/references.rb +0 -0
  61. data/tmpsets/set/{mod002-core → mod001-core}/all/rules.rb +0 -0
  62. data/tmpsets/set/{mod002-core → mod001-core}/all/stages.rb +0 -0
  63. data/tmpsets/set/{mod002-core → mod001-core}/all/states.rb +0 -0
  64. data/tmpsets/set/{mod002-core → mod001-core}/all/subcards.rb +0 -0
  65. data/tmpsets/set/{mod002-core → mod001-core}/all/templating.rb +0 -0
  66. data/tmpsets/set/{mod002-core → mod001-core}/all/tracked_attributes.rb +0 -0
  67. data/tmpsets/set/{mod002-core → mod001-core}/all/trash.rb +0 -50
  68. data/tmpsets/set/{mod002-core → mod001-core}/all/type.rb +0 -0
  69. data/tmpsets/set/{mod002-core → mod001-core}/all/utils.rb +33 -0
  70. data/tmpsets/set/{mod002-core → mod001-core}/all/view_cache.rb +0 -0
  71. data/tmpsets/set/{mod003-history → mod002-history}/all/actions.rb +0 -0
  72. data/tmpsets/set/{mod003-history → mod002-history}/all/content_history.rb +0 -0
  73. data/tmpsets/set/{mod003-history → mod002-history}/all/history.rb +0 -0
  74. data/tmpsets/set/{mod004-basic_formats → mod003-basic_formats}/all/all_css.rb +0 -0
  75. data/tmpsets/set/{mod004-basic_formats → mod003-basic_formats}/all/all_csv.rb +0 -0
  76. data/tmpsets/set/{mod004-basic_formats → mod003-basic_formats}/all/all_js.rb +0 -0
  77. data/tmpsets/set/{mod004-basic_formats → mod003-basic_formats}/all/base.rb +0 -0
  78. data/tmpsets/set/{mod004-basic_formats → mod003-basic_formats}/all/file.rb +0 -0
  79. data/tmpsets/set/{mod004-basic_formats → mod003-basic_formats}/all/json.rb +0 -0
  80. data/tmpsets/set/{mod004-basic_formats → mod003-basic_formats}/all/rss.rb +0 -0
  81. data/tmpsets/set/{mod004-basic_formats → mod003-basic_formats}/all/text.rb +0 -0
  82. data/tmpsets/set/{mod004-basic_formats → mod003-basic_formats}/self/01_head/javascript.rb +0 -0
  83. data/tmpsets/set/{mod004-basic_formats → mod003-basic_formats}/self/head.rb +1 -1
  84. data/tmpsets/set/{mod005-pointer → mod004-pointer}/abstract/01_pointer.rb +0 -0
  85. data/tmpsets/set/{mod005-pointer → mod004-pointer}/abstract/01_pointer/edit.rb +0 -0
  86. data/tmpsets/set/{mod005-pointer → mod004-pointer}/abstract/code_pointer.rb +0 -0
  87. data/tmpsets/set/{mod005-pointer → mod004-pointer}/self/script_editors.rb +0 -0
  88. data/tmpsets/set/{mod005-pointer → mod004-pointer}/self/script_mods.rb +0 -0
  89. data/tmpsets/set/{mod005-pointer → mod004-pointer}/self/script_pointer_config.rb +0 -0
  90. data/tmpsets/set/{mod005-pointer → mod004-pointer}/type/pointer.rb +0 -0
  91. data/tmpsets/set/{mod006-ace_editor → mod005-ace_editor}/abstract/ace_editor.rb +0 -0
  92. data/tmpsets/set/{mod006-ace_editor → mod005-ace_editor}/self/script_ace.rb +0 -0
  93. data/tmpsets/set/{mod006-ace_editor → mod005-ace_editor}/self/script_ace_config.rb +0 -0
  94. data/tmpsets/set/{mod007-prosemirror_editor → mod006-prosemirror_editor}/abstract/prosemirror_editor.rb +0 -0
  95. data/tmpsets/set/{mod007-prosemirror_editor → mod006-prosemirror_editor}/self/script_prosemirror.rb +0 -0
  96. data/tmpsets/set/{mod007-prosemirror_editor → mod006-prosemirror_editor}/self/script_prosemirror_config.rb +0 -0
  97. data/tmpsets/set/{mod009-solid_cache → mod008-solid_cache}/abstract/solid_cache.rb +1 -1
  98. data/tmpsets/set/{mod009-solid_cache → mod008-solid_cache}/all/solid_cache.rb +0 -0
  99. data/tmpsets/set/{mod009-solid_cache → mod008-solid_cache}/right/solid_cache.rb +0 -0
  100. data/tmpsets/set/{mod010-basic_types → mod009-basic_types}/type/html.rb +0 -0
  101. data/tmpsets/set/{mod010-basic_types → mod009-basic_types}/type/plain_text.rb +0 -0
  102. data/tmpsets/set/{mod011-machines → mod010-machines}/abstract/script.rb +0 -0
  103. data/tmpsets/set/{mod011-machines → mod010-machines}/all/reset_machines.rb +0 -0
  104. data/tmpsets/set/{mod011-machines → mod010-machines}/right/machine_cache.rb +0 -0
  105. data/tmpsets/set/{mod011-machines → mod010-machines}/right/machine_input.rb +0 -0
  106. data/tmpsets/set/{mod011-machines → mod010-machines}/right/machine_output.rb +1 -9
  107. data/tmpsets/set/{mod011-machines → mod010-machines}/self/script_card_menu.rb +0 -0
  108. data/tmpsets/set/{mod011-machines → mod010-machines}/self/script_html5shiv_printshiv.rb +0 -0
  109. data/tmpsets/set/{mod011-machines → mod010-machines}/self/script_jquery.rb +0 -0
  110. data/tmpsets/set/{mod011-machines → mod010-machines}/self/script_jquery_helper.rb +0 -0
  111. data/tmpsets/set/{mod011-machines → mod010-machines}/self/script_slot.rb +0 -0
  112. data/tmpsets/set/{mod011-machines → mod010-machines}/self/style_bootstrap_compatible.rb +0 -0
  113. data/tmpsets/set/{mod011-machines → mod010-machines}/self/style_cards.rb +0 -0
  114. data/tmpsets/set/{mod011-machines → mod010-machines}/self/style_jquery_ui_smoothness.rb +0 -0
  115. data/tmpsets/set/{mod011-machines → mod010-machines}/type/coffee_script.rb +6 -2
  116. data/tmpsets/set/{mod011-machines → mod010-machines}/type/css.rb +2 -2
  117. data/tmpsets/set/{mod011-machines → mod010-machines}/type/java_script.rb +2 -2
  118. data/tmpsets/set/{mod011-machines → mod010-machines}/type/scss.rb +0 -0
  119. data/tmpsets/set/{mod011-machines → mod010-machines}/type/skin.rb +2 -2
  120. data/tmpsets/set/{mod012-settings → mod011-settings}/abstract/permission.rb +0 -0
  121. data/tmpsets/set/{mod012-settings → mod011-settings}/right/add_help.rb +0 -0
  122. data/tmpsets/set/{mod012-settings → mod011-settings}/right/comment.rb +0 -0
  123. data/tmpsets/set/{mod012-settings → mod011-settings}/right/create.rb +0 -0
  124. data/tmpsets/set/{mod012-settings → mod011-settings}/right/default.rb +0 -0
  125. data/tmpsets/set/{mod012-settings → mod011-settings}/right/delete.rb +0 -0
  126. data/tmpsets/set/{mod012-settings → mod011-settings}/right/help.rb +0 -0
  127. data/tmpsets/set/{mod012-settings → mod011-settings}/right/read.rb +0 -0
  128. data/tmpsets/set/{mod012-settings → mod011-settings}/right/script.rb +1 -1
  129. data/tmpsets/set/{mod012-settings → mod011-settings}/right/structure.rb +0 -0
  130. data/tmpsets/set/{mod012-settings → mod011-settings}/right/style.rb +1 -1
  131. data/tmpsets/set/{mod012-settings → mod011-settings}/right/update.rb +0 -0
  132. data/tmpsets/set/{mod012-settings → mod011-settings}/self/accountable.rb +0 -0
  133. data/tmpsets/set/{mod012-settings → mod011-settings}/self/add_help.rb +0 -0
  134. data/tmpsets/set/{mod012-settings → mod011-settings}/self/autoname.rb +0 -0
  135. data/tmpsets/set/{mod012-settings → mod011-settings}/self/captcha.rb +0 -0
  136. data/tmpsets/set/{mod012-settings → mod011-settings}/self/create.rb +0 -0
  137. data/tmpsets/set/{mod012-settings → mod011-settings}/self/default.rb +0 -0
  138. data/tmpsets/set/{mod012-settings → mod011-settings}/self/default_html_view.rb +0 -0
  139. data/tmpsets/set/{mod012-settings → mod011-settings}/self/delete.rb +0 -0
  140. data/tmpsets/set/{mod012-settings → mod011-settings}/self/follow_fields.rb +0 -0
  141. data/tmpsets/set/{mod012-settings → mod011-settings}/self/help.rb +0 -0
  142. data/tmpsets/set/{mod012-settings → mod011-settings}/self/input.rb +0 -0
  143. data/tmpsets/set/{mod012-settings → mod011-settings}/self/layout.rb +0 -0
  144. data/tmpsets/set/{mod012-settings → mod011-settings}/self/on_create.rb +0 -0
  145. data/tmpsets/set/{mod012-settings → mod011-settings}/self/on_delete.rb +0 -0
  146. data/tmpsets/set/{mod012-settings → mod011-settings}/self/on_update.rb +0 -0
  147. data/tmpsets/set/{mod012-settings → mod011-settings}/self/options.rb +0 -0
  148. data/tmpsets/set/{mod012-settings → mod011-settings}/self/options_label.rb +0 -0
  149. data/tmpsets/set/{mod012-settings → mod011-settings}/self/read.rb +0 -0
  150. data/tmpsets/set/{mod012-settings → mod011-settings}/self/recent_settings.rb +0 -0
  151. data/tmpsets/set/{mod012-settings → mod011-settings}/self/script.rb +0 -0
  152. data/tmpsets/set/{mod012-settings → mod011-settings}/self/structure.rb +0 -0
  153. data/tmpsets/set/{mod012-settings → mod011-settings}/self/style.rb +0 -0
  154. data/tmpsets/set/{mod012-settings → mod011-settings}/self/table_of_contents.rb +0 -0
  155. data/tmpsets/set/{mod012-settings → mod011-settings}/self/thanks.rb +0 -0
  156. data/tmpsets/set/{mod012-settings → mod011-settings}/self/update.rb +0 -0
  157. data/tmpsets/set/{mod012-settings → mod011-settings}/type/setting.rb +0 -0
  158. data/tmpsets/set/{mod013-email → mod012-email}/all/email_html.rb +0 -0
  159. data/tmpsets/set/{mod013-email → mod012-email}/all/email_text.rb +0 -0
  160. data/tmpsets/set/{mod013-email → mod012-email}/all/follow.rb +0 -0
  161. data/tmpsets/set/{mod013-email → mod012-email}/all/notify.rb +0 -0
  162. data/tmpsets/set/{mod013-email → mod012-email}/all/observer.rb +0 -0
  163. data/tmpsets/set/{mod013-email → mod012-email}/right/bcc.rb +0 -0
  164. data/tmpsets/set/{mod013-email → mod012-email}/right/cc.rb +0 -0
  165. data/tmpsets/set/{mod013-email → mod012-email}/right/follow.rb +0 -0
  166. data/tmpsets/set/{mod013-email → mod012-email}/right/follow_fields.rb +0 -0
  167. data/tmpsets/set/{mod013-email → mod012-email}/right/followers.rb +0 -0
  168. data/tmpsets/set/{mod013-email → mod012-email}/right/following.rb +0 -0
  169. data/tmpsets/set/{mod013-email → mod012-email}/right/from.rb +0 -0
  170. data/tmpsets/set/{mod013-email → mod012-email}/right/html_message.rb +0 -0
  171. data/tmpsets/set/{mod013-email → mod012-email}/right/to.rb +0 -0
  172. data/tmpsets/set/{mod013-email → mod012-email}/self/always.rb +0 -0
  173. data/tmpsets/set/{mod013-email → mod012-email}/self/created.rb +0 -0
  174. data/tmpsets/set/{mod013-email → mod012-email}/self/edited.rb +0 -0
  175. data/tmpsets/set/{mod013-email → mod012-email}/self/follow.rb +0 -0
  176. data/tmpsets/set/{mod013-email → mod012-email}/self/follow_defaults.rb +0 -0
  177. data/tmpsets/set/{mod013-email → mod012-email}/self/never.rb +0 -0
  178. data/tmpsets/set/{mod013-email → mod012-email}/type/email_template.rb +0 -0
  179. data/tmpsets/set/{mod013-email → mod012-email}/type_plus_right/user/follow.rb +8 -4
  180. data/tmpsets/set/mod016-standard/all/links.rb +2 -2
  181. data/tmpsets/set/mod018-bootstrap/all/bootstrap/table.rb +1 -1
  182. data/tmpsets/set/mod018-bootstrap/self/bootswatch_shared.rb +1 -1
  183. metadata +149 -163
  184. data/lib/card/director_register.rb +0 -101
  185. data/lib/card/stage.rb +0 -106
  186. data/lib/card/stage_director.rb +0 -310
  187. data/lib/card/subdirector_array.rb +0 -43
  188. data/tmpsets/set/mod001-admin/self/admin.rb +0 -130
  189. data/tmpsets/set/mod001-admin/self/admin_info.rb +0 -110
  190. data/tmpsets/set/mod001-admin/self/version.rb +0 -15
  191. data/tmpsets/set/mod002-core/all/debug.rb +0 -30
  192. data/tmpsets/set/mod002-core/all/update_read_rules.rb +0 -91
  193. data/tmpsets/set/mod011-machines/abstract/machine.rb +0 -277
  194. data/tmpsets/set/mod011-machines/abstract/machine_input.rb +0 -83
  195. data/tmpsets/set/mod014-developer/all/event_viz.rb +0 -59
  196. data/tmpsets/set/mod014-developer/all/view_viz.rb +0 -30
  197. data/tmpsets/set/mod014-developer/right/debug.rb +0 -96
  198. data/tmpsets/set/mod015-carrierwave/abstract/attachment.rb +0 -118
  199. data/tmpsets/set/mod015-carrierwave/abstract/attachment/paths.rb +0 -57
  200. data/tmpsets/set/mod015-carrierwave/abstract/attachment/storage_type.rb +0 -326
  201. data/tmpsets/set/mod015-carrierwave/abstract/attachment/upload_cache.rb +0 -84
  202. data/tmpsets/set/mod015-carrierwave/all/file_utils.rb +0 -35
  203. data/tmpsets/set/mod015-carrierwave/self/admin.rb +0 -30
  204. data/tmpsets/set/mod015-carrierwave/type/file.rb +0 -158
  205. data/tmpsets/set/mod015-carrierwave/type/image.rb +0 -97
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 17c71bfd48263a99742c5d53bf8be447d93eed8b
4
- data.tar.gz: 3ee523e8b559f892c45012a03e1ebc9bf3389e01
3
+ metadata.gz: c530f9f078a90e9de4c239d7f5222e670b10b98a
4
+ data.tar.gz: 735604a8c31fe1247d23e3bbc5c90d495c83593a
5
5
  SHA512:
6
- metadata.gz: d14d3a8d117e2b34cd4e830ba5ac3a60dd3f177b0864dc68cc93ef0455b324d43b4d336985415311d55341caf1569fe7e3b8de018f35d0adb5b69bb5ff265abc
7
- data.tar.gz: 5d2e6b59ee7acfb5aff106683581ae5331b65a893c5c85af1b60273e0c84fd792805f2c7f6c11e7e2dbca4f0e7c3cbfb4f9336dc4bacf1b1d7c9279b92072baf
6
+ metadata.gz: 43e1f744865904f81aec258a25a0f823693b2b8b418491e451594a18f345fb4b79e796ece4ca46e4c9ac604c9622646faf137801353348802d7b5c790d14e270
7
+ data.tar.gz: 6224a80edc261e7b8636dce40fdb1357ee1d94e08c4fc32208587f6470b413c4e3af370156bfb1d9553159a5872245cc10cf1fb304d94d7413d50da90c8a4969
data/.yardopts CHANGED
@@ -1,6 +1,6 @@
1
- **/lib/**/*.rb \
2
- **/mod/*/format/*.rb \
3
- **/mod/*/chunk/*.rb \
1
+ lib/**/*.rb \
2
+ mod/*/format/*.rb \
3
+ mod/*/chunk/*.rb \
4
4
  tmpsets/set*/**/*.rb - \
5
5
  **/*.rdoc \
6
6
  GPL \
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.19.1
1
+ 1.19.2
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class AddTrashCard < Card::Migration::Core
4
+ def up
5
+ create_or_update name: "*trash", codename: "trash"
6
+ end
7
+ end
@@ -42,8 +42,8 @@ class Card < ActiveRecord::Base
42
42
  require_dependency "card/reference"
43
43
  require_dependency "card/subcards"
44
44
  require_dependency "card/cache/view_cache"
45
- require_dependency "card/stage_director"
46
- require_dependency "card/director_register"
45
+ require_dependency "card/act_manager"
46
+ require_dependency "card/act_manager/stage_director"
47
47
 
48
48
  has_many :references_in, class_name: :Reference, foreign_key: :referee_id
49
49
  has_many :references_out, class_name: :Reference, foreign_key: :referer_id
@@ -0,0 +1,143 @@
1
+ class Card
2
+ # Manages the whole process of creating an {act Card::Act} ie. changing
3
+ # a card and attached subcards.
4
+ #
5
+ # For every card that is part of the act the ActManager creates a
6
+ # {StageDirector} that leads the card through all the stages.
7
+ # Because cards sometimes get expired and reloaded during an act we need
8
+ # this global object to ensure that the stage information doesn't get lost
9
+ # until the act is finished.
10
+ #
11
+ # The process of creating an act/writing a card change to the database
12
+ # is divided into 8 stages that are grouped in 3 phases.
13
+ #
14
+ # 'validation phase'
15
+ # * initialize stage (I)
16
+ # * prepare_to_validate stage (P2V)
17
+ # * validate stage (V)
18
+ #
19
+ # 'storage phase'
20
+ # * prepare_to_store stage (P2S)
21
+ # * store stage (S)
22
+ # * finalize stage (F)
23
+ #
24
+ # 'integration phase'
25
+ # * integrate stage (IG)
26
+ # * integrate_with_delay stage (IGwD)
27
+ #
28
+ #
29
+ # The table below gives you an overview what you can do in which stage:
30
+ #
31
+ # validation | storage | integration
32
+ # I P2V V | P2S S F | IG IGwD
33
+ #-------------------------------------------------------------------------
34
+ # attach subcard yes! yes! yes | yes yes yes | yes
35
+ # detach subcard yes! yes! yes | yes no no! | no!
36
+ # validate yes yes yes! | no | no
37
+ # 1 insecure change yes yes! no | no! | no!
38
+ # 2 secure change yes | yes! no! no! | no!
39
+ # abort yes! | yes | yes?
40
+ # add errors yes! | no! | no!
41
+ # 3 create other cards yes | yes | yes
42
+ # has id no | no no? yes | yes
43
+ # within web request yes | yes | yes no
44
+ # 4 within transaction yes | yes | no
45
+
46
+ # available values:
47
+ # dirty attributes yes | yes | yes
48
+ # params yes | yes | yes
49
+ # success yes | yes | yes no
50
+ # session yes | yes | yes no
51
+ #
52
+ #
53
+ # Explanation:
54
+ # yes! the recommended stage to do that
55
+ # yes ok to do it here
56
+ # no not recommended; chance to mess things up
57
+ # but if something forces you to do it here you can try
58
+ # no! never do it here. it won't work or will break things
59
+ #
60
+ # if there is only a single entry in a phase column it counts for all stages
61
+ # of that phase
62
+ #
63
+ # 1) 'insecure' means a change of a card attribute that can possibly make
64
+ # the card invalid to save
65
+ # 2) 'secure' means you are sure that the change doesn't affect the validation
66
+ # 3) If you call 'create', 'update_attributes' or 'save' the card will become
67
+ # part of the same act and all stage of the validation and storage phase
68
+ # will be executed immediately for that card. The integration phase will be
69
+ # executed together with the act card and its subcards
70
+ # 4) This means if an exception is raised in the validation or storage phase
71
+ # everything will rollback. If the integration phase fails the db changes
72
+ # of the other two phases will remain persistent.
73
+ class ActManager
74
+ cattr_accessor :act_card
75
+
76
+ class << self
77
+ def act_director
78
+ return unless act_card
79
+ act_card.director
80
+ end
81
+
82
+ def directors
83
+ @directors ||= {}
84
+ end
85
+
86
+ def clear
87
+ ActManager.act_card = nil
88
+ directors.each_pair do |card, _dir|
89
+ card.director = nil
90
+ end
91
+ @directors = nil
92
+ end
93
+
94
+ def fetch card, opts={}
95
+ return directors[card] if directors[card]
96
+ directors.each_key do |dir_card|
97
+ return dir_card.director if dir_card.name == card.name
98
+ end
99
+ directors[card] = new_director card, opts
100
+ end
101
+
102
+ def new_director card, opts={}
103
+ if opts[:parent]
104
+ StageSubdirector.new card, opts
105
+ elsif act_card && act_card != card && running_act?
106
+ act_card.director.subdirectors.add card
107
+ else
108
+ StageDirector.new card
109
+ end
110
+ end
111
+
112
+ def add director
113
+ directors[director.card] = director
114
+ end
115
+
116
+ def card_changed old_card
117
+ return unless (director = @directors.delete old_card)
118
+ add director
119
+ end
120
+
121
+ def delete director
122
+ return unless @directors
123
+ @directors.delete director.card
124
+ director.delete
125
+ end
126
+
127
+ def deep_delete director
128
+ director.subdirectors.each do |subdir|
129
+ deep_delete subdir
130
+ end
131
+ delete director
132
+ end
133
+
134
+ def running_act?
135
+ (dir = act_director) && dir.running?
136
+ end
137
+
138
+ def to_s
139
+ directors.values.map(&:to_s).join "\n"
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,56 @@
1
+ class Card
2
+ class ActManager
3
+ module Stage
4
+ STAGES = [:initialize, :prepare_to_validate, :validate, :prepare_to_store,
5
+ :store, :finalize, :integrate, :integrate_with_delay].freeze
6
+ stage_index = {}
7
+ STAGES.each_with_index do |stage, i|
8
+ stage_index[stage] = i
9
+ end
10
+ STAGE_INDEX = stage_index.freeze
11
+
12
+ def stage_symbol index
13
+ case index
14
+ when Symbol
15
+ return index if STAGE_INDEX[index]
16
+ when Integer
17
+ return STAGES[index] if index < STAGES.size
18
+ end
19
+ raise Card::Error, "not a valid stage index: #{index}"
20
+ end
21
+
22
+ def stage_index stage
23
+ case stage
24
+ when Symbol then
25
+ return STAGE_INDEX[stage]
26
+ when Integer then
27
+ return stage
28
+ else
29
+ raise Card::Error, "not a valid stage: #{stage}"
30
+ end
31
+ end
32
+
33
+ def stage_ok? opts
34
+ stage && (
35
+ (opts[:during] && in?(opts[:during])) ||
36
+ (opts[:before] && before?(opts[:before])) ||
37
+ (opts[:after] && after?(opts[:after])) ||
38
+ true # no phase restriction in opts
39
+ )
40
+ end
41
+
42
+ def before? allowed_phase
43
+ STAGE_INDEX[allowed_phase] > STAGE_INDEX[stage]
44
+ end
45
+
46
+ def after? allowed_phase
47
+ STAGE_INDEX[allowed_phase] < STAGE_INDEX[stage]
48
+ end
49
+
50
+ def in? allowed_phase
51
+ (allowed_phase.is_a?(Array) && allowed_phase.include?(stage)) ||
52
+ allowed_phase == stage
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,313 @@
1
+
2
+ class Card
3
+ def restore_changes_information
4
+ return unless @previously_changed
5
+ @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
6
+ @previously_changed.each do |k, (old, _new)|
7
+ @changed_attributes[k] = old
8
+ end
9
+ end
10
+
11
+ def clean_after_stage_fail
12
+ @action = nil
13
+ expire_pieces
14
+ subcards.each(&:expire_pieces)
15
+ end
16
+
17
+ class ActManager
18
+ # A 'StageDirector' executes the stages of a card when the card gets
19
+ # created, updated or deleted.
20
+ # For subcards, i.e. other cards that are changed in the same act, a
21
+ # StageDirector has StageSubdirectors that take care of the stages for
22
+ # those cards
23
+ #
24
+ # In general a stage is executed for all involved cards before the
25
+ # StageDirector proceeds with the next stage.
26
+ # Only exception is the finalize stage.
27
+ # The finalize stage of a subcard is executed immediately after its store
28
+ # stage. When all subcards are finalized the supercard's finalize stage is
29
+ # executed.
30
+ #
31
+ # If a subcard is added in a stage then it catches up at the end of the
32
+ # stage to the current stage.
33
+ # For example if you add a subcard in a card's :prepare_to_store stage then
34
+ # after that stage the stages :initialize, :prepare_to_validate,
35
+ # :validate and :prepare_to_store are executed for the subcard.
36
+ #
37
+ # Stages are executed with pre-order depth-first search.
38
+ # That means if A has subcards AA and AB; AAA is subcard of AA and ABA
39
+ # subcard of AB then the order of execution is
40
+ # A -> AA -> AAA -> AB -> ABA
41
+ #
42
+ # A special case can happen in the store phase.
43
+ # If the id of a subcard is needed for a supercard
44
+ # (for example as left_id or as type_id) and the subcard doesn't
45
+ # have an id yet (because it gets created in the same act)
46
+ # then the subcard's store stage is executed before the supercard's store
47
+ # stage
48
+ class StageDirector
49
+ include Stage
50
+
51
+ attr_accessor :prior_store, :act, :card, :stage, :parent, :main,
52
+ :subdirectors, :transact_in_stage
53
+ attr_reader :running
54
+ alias_method :running?, :running
55
+ alias_method :main?, :main
56
+
57
+ def initialize card, opts={}, main=true
58
+ @card = card
59
+ @card.director = self
60
+ # for read actions there is no validation phase
61
+ # so we have to set the action here
62
+ @card.identify_action
63
+
64
+ @stage = nil
65
+ @running = false
66
+ @prepared = false
67
+ @parent = opts[:parent]
68
+ # has card to be stored before the supercard?
69
+ @prior_store = opts[:priority]
70
+ @call_after_store = []
71
+ @main = main
72
+ @subdirectors = SubdirectorArray.initialize_with_subcards(self)
73
+ register
74
+ end
75
+
76
+ def register
77
+ ActManager.add self
78
+ end
79
+
80
+ def unregister
81
+ ActManager.delete self
82
+ end
83
+
84
+ def delete
85
+ @card.director = nil
86
+ @subdirectors.clear
87
+ @stage = nil
88
+ @action = nil
89
+ end
90
+
91
+ def prepare_for_phases
92
+ @card.prepare_for_phases unless @prepared
93
+ @prepared = true
94
+ @subdirectors.each(&:prepare_for_phases)
95
+ end
96
+
97
+ def validation_phase
98
+ run_single_stage :initialize
99
+ run_single_stage :prepare_to_validate
100
+ run_single_stage :validate
101
+ @card.expire_pieces if @card.errors.any?
102
+ @card.errors.empty?
103
+ end
104
+
105
+ def storage_phase &block
106
+ catch_up_to_stage :prepare_to_store
107
+ run_single_stage :store, &block
108
+ run_single_stage :finalize
109
+ ensure
110
+ @from_trash = nil
111
+ end
112
+
113
+ def integration_phase
114
+ return if @abort
115
+ @card.restore_changes_information
116
+ run_single_stage :integrate
117
+ run_single_stage :integrate_with_delay
118
+ rescue => e # don't rollback
119
+ Card::Error.current = e
120
+ @card.notable_exception_raised
121
+ return false
122
+ ensure
123
+ @card.changes_applied unless @abort
124
+ ActManager.clear if main? && !@card.only_storage_phase
125
+ end
126
+
127
+ def catch_up_to_stage next_stage
128
+ if @transact_in_stage
129
+ return if @transact_in_stage != next_stage
130
+ next_stage = :integrate_with_delay
131
+ end
132
+ upto_stage(next_stage) do |stage|
133
+ run_single_stage stage
134
+ end
135
+ end
136
+
137
+ def reset_stage
138
+ @stage = -1
139
+ end
140
+
141
+ def abort
142
+ @abort = true
143
+ end
144
+
145
+ def call_after_store &block
146
+ @call_after_store << block
147
+ end
148
+
149
+ def need_act
150
+ act_director = main_director
151
+ unless act_director
152
+ raise Card::Error, "act requested without a main stage director"
153
+ end
154
+ act_director.act ||= Card::Act.create(ip_address: Env.ip)
155
+ @card.current_act = @act = act_director.act
156
+ end
157
+
158
+ def main_director
159
+ if main?
160
+ self
161
+ else
162
+ ActManager.act_director || (@parent && @parent.main_director)
163
+ end
164
+ end
165
+
166
+ def to_s
167
+ str = @card.name.to_s.clone
168
+ if @subdirectors
169
+ subs = subdirectors.map(&:card)
170
+ .map { |card| " #{card.name}" }.join "\n"
171
+ str << "\n#{subs}"
172
+ end
173
+ str
174
+ end
175
+
176
+ def update_card card
177
+ old_card = @card
178
+ @card = card
179
+ ActManager.card_changed old_card
180
+ end
181
+
182
+ private
183
+
184
+ def upto_stage stage
185
+ @stage ||= -1
186
+ (@stage + 1).upto(stage_index(stage)) do |i|
187
+ yield stage_symbol(i)
188
+ end
189
+ end
190
+
191
+ def valid_next_stage? stage
192
+ new_stage = stage_index(stage)
193
+ @stage ||= -1
194
+ return if @stage >= new_stage
195
+ if @stage < new_stage - 1
196
+ raise Card::Error, "stage #{stage_symbol(new_stage - 1)} was " \
197
+ "skipped for card #{@card}"
198
+ end
199
+ @card.errors.empty? || new_stage > stage_index(:validate)
200
+ end
201
+
202
+ def run_single_stage stage, &block
203
+ return true unless valid_next_stage? stage
204
+ # puts "#{@card.name}: #{stage} stage".red
205
+
206
+ @stage = stage_index stage
207
+ if stage == :initialize
208
+ @running ||= true
209
+ prepare_for_phases
210
+ end
211
+ # in the store stage it can be necessary that
212
+ # other subcards must be saved before we save this card
213
+ if stage == :store
214
+ store(&block)
215
+ else
216
+ run_stage_callbacks stage
217
+ run_subdirector_stages stage
218
+ end
219
+ rescue => e
220
+ @card.clean_after_stage_fail
221
+ raise e
222
+ end
223
+
224
+ def run_stage_callbacks stage
225
+ Rails.logger.debug "#{stage}: #{@card.name}"
226
+ # we use abort :success in the :store stage for :save_draft
227
+ if stage_index(stage) <= stage_index(:store) && !main?
228
+ @card.abortable do
229
+ @card.run_callbacks :"#{stage}_stage"
230
+ end
231
+ else
232
+ @card.run_callbacks :"#{stage}_stage"
233
+ end
234
+ end
235
+
236
+ def run_subdirector_stages stage
237
+ @subdirectors.each do |subdir|
238
+ subdir.catch_up_to_stage stage
239
+ end
240
+ ensure
241
+ @card.handle_subcard_errors
242
+ end
243
+
244
+ # handle the store stage
245
+ # The tricky part here is to preserve the dirty marks on the subcards'
246
+ # attributes for the finalize stage.
247
+ # To achieve this we can't just call the :store and :finalize callbacks on
248
+ # the subcards as we do in the other phases.
249
+ # Instead we have to call `save` on the subcards
250
+ # and use the ActiveRecord :around_save callback to run the :store and
251
+ # :finalize stages
252
+ def store &save_block
253
+ if main? && !block_given?
254
+ raise Card::Error, "need block to store main card"
255
+ end
256
+ # the block is the ActiveRecord block from the around save callback that
257
+ # saves the card
258
+ if block_given?
259
+ run_stage_callbacks :store
260
+ store_with_subcards(&save_block)
261
+ else
262
+ trigger_storage_phase_callback
263
+ end
264
+ end
265
+
266
+ def store_with_subcards
267
+ store_pre_subcards
268
+ yield
269
+ @call_after_store.each { |handle_id| handle_id.call(@card.id) }
270
+ store_post_subcards
271
+ true.tap { @virtual = false } # TODO: find a better place for this
272
+ ensure
273
+ @card.handle_subcard_errors
274
+ end
275
+
276
+ # store subcards whose ids we need for this card
277
+ def store_pre_subcards
278
+ @subdirectors.each do |subdir|
279
+ next unless subdir.prior_store
280
+ subdir.catch_up_to_stage :store
281
+ end
282
+ end
283
+
284
+ def store_post_subcards
285
+ @subdirectors.each do |subdir|
286
+ next if subdir.prior_store
287
+ subdir.catch_up_to_stage :store
288
+ end
289
+ end
290
+
291
+ # trigger the storage_phase, skip the other phases
292
+ # At this point the :prepare_to_store stage was already executed
293
+ # by the parent director. So the storage phase will only run
294
+ # the :store stage and the :finalize stage
295
+ def trigger_storage_phase_callback
296
+ @stage = stage_index :prepare_to_store
297
+ @card.only_storage_phase = true
298
+ @card.save! validate: false
299
+ end
300
+ end
301
+
302
+ class StageSubdirector < StageDirector
303
+ def initialize card, opts={}
304
+ super card, opts, false
305
+ end
306
+
307
+ def delete
308
+ @parent.subdirectors.delete self if @parent
309
+ super
310
+ end
311
+ end
312
+ end
313
+ end