aasm 4.12.3 → 5.5.0

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 (218) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +284 -119
  3. data/lib/aasm/aasm.rb +30 -27
  4. data/lib/aasm/base.rb +61 -11
  5. data/lib/aasm/configuration.rb +3 -0
  6. data/lib/aasm/core/event.rb +26 -30
  7. data/lib/aasm/core/invoker.rb +129 -0
  8. data/lib/aasm/core/invokers/base_invoker.rb +75 -0
  9. data/lib/aasm/core/invokers/class_invoker.rb +52 -0
  10. data/lib/aasm/core/invokers/literal_invoker.rb +49 -0
  11. data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
  12. data/lib/aasm/core/state.rb +16 -14
  13. data/lib/aasm/core/transition.rb +8 -69
  14. data/lib/aasm/dsl_helper.rb +24 -22
  15. data/lib/aasm/errors.rb +5 -3
  16. data/lib/aasm/instance_base.rb +28 -5
  17. data/lib/aasm/localizer.rb +13 -3
  18. data/lib/aasm/persistence/active_record_persistence.rb +25 -5
  19. data/lib/aasm/persistence/base.rb +14 -3
  20. data/lib/aasm/persistence/core_data_query_persistence.rb +2 -1
  21. data/lib/aasm/persistence/dynamoid_persistence.rb +1 -1
  22. data/lib/aasm/persistence/mongoid_persistence.rb +1 -1
  23. data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
  24. data/lib/aasm/persistence/orm.rb +23 -19
  25. data/lib/aasm/persistence/plain_persistence.rb +2 -1
  26. data/lib/aasm/persistence/redis_persistence.rb +1 -1
  27. data/lib/aasm/persistence/sequel_persistence.rb +0 -1
  28. data/lib/aasm/persistence.rb +3 -0
  29. data/lib/aasm/rspec/allow_event.rb +5 -1
  30. data/lib/aasm/rspec/allow_transition_to.rb +5 -1
  31. data/lib/aasm/rspec/transition_from.rb +5 -1
  32. data/lib/aasm/version.rb +1 -1
  33. data/lib/aasm.rb +5 -2
  34. data/lib/generators/aasm/orm_helpers.rb +7 -1
  35. data/lib/generators/active_record/aasm_generator.rb +3 -1
  36. data/lib/generators/active_record/templates/migration.rb +1 -1
  37. data/lib/generators/nobrainer/aasm_generator.rb +28 -0
  38. data/lib/motion-aasm.rb +1 -0
  39. metadata +42 -344
  40. data/.document +0 -6
  41. data/.gitignore +0 -20
  42. data/.travis.yml +0 -52
  43. data/API +0 -34
  44. data/Appraisals +0 -43
  45. data/CHANGELOG.md +0 -370
  46. data/CODE_OF_CONDUCT.md +0 -13
  47. data/CONTRIBUTING.md +0 -24
  48. data/Gemfile +0 -7
  49. data/Gemfile.lock_old +0 -151
  50. data/HOWTO +0 -12
  51. data/PLANNED_CHANGES.md +0 -11
  52. data/README_FROM_VERSION_3_TO_4.md +0 -240
  53. data/Rakefile +0 -31
  54. data/TESTING.md +0 -25
  55. data/aasm.gemspec +0 -35
  56. data/callbacks.txt +0 -51
  57. data/gemfiles/rails_3.2.gemfile +0 -13
  58. data/gemfiles/rails_4.0.gemfile +0 -15
  59. data/gemfiles/rails_4.2.gemfile +0 -16
  60. data/gemfiles/rails_4.2_mongoid_5.gemfile +0 -11
  61. data/gemfiles/rails_5.0.gemfile +0 -14
  62. data/spec/database.rb +0 -44
  63. data/spec/database.yml +0 -3
  64. data/spec/en.yml +0 -12
  65. data/spec/en_deprecated_style.yml +0 -10
  66. data/spec/generators/active_record_generator_spec.rb +0 -47
  67. data/spec/generators/mongoid_generator_spec.rb +0 -31
  68. data/spec/models/active_record/basic_active_record_two_state_machines_example.rb +0 -25
  69. data/spec/models/active_record/complex_active_record_example.rb +0 -37
  70. data/spec/models/active_record/derivate_new_dsl.rb +0 -7
  71. data/spec/models/active_record/false_state.rb +0 -35
  72. data/spec/models/active_record/gate.rb +0 -39
  73. data/spec/models/active_record/invalid_persistor.rb +0 -29
  74. data/spec/models/active_record/localizer_test_model.rb +0 -34
  75. data/spec/models/active_record/no_direct_assignment.rb +0 -21
  76. data/spec/models/active_record/no_scope.rb +0 -21
  77. data/spec/models/active_record/persisted_state.rb +0 -12
  78. data/spec/models/active_record/provided_and_persisted_state.rb +0 -24
  79. data/spec/models/active_record/reader.rb +0 -7
  80. data/spec/models/active_record/readme_job.rb +0 -21
  81. data/spec/models/active_record/silent_persistor.rb +0 -29
  82. data/spec/models/active_record/simple_new_dsl.rb +0 -17
  83. data/spec/models/active_record/thief.rb +0 -29
  84. data/spec/models/active_record/transactor.rb +0 -124
  85. data/spec/models/active_record/transient.rb +0 -6
  86. data/spec/models/active_record/validator.rb +0 -118
  87. data/spec/models/active_record/with_enum.rb +0 -39
  88. data/spec/models/active_record/with_enum_without_column.rb +0 -38
  89. data/spec/models/active_record/with_false_enum.rb +0 -31
  90. data/spec/models/active_record/with_true_enum.rb +0 -39
  91. data/spec/models/active_record/worker.rb +0 -2
  92. data/spec/models/active_record/writer.rb +0 -6
  93. data/spec/models/basic_two_state_machines_example.rb +0 -25
  94. data/spec/models/callbacks/basic.rb +0 -98
  95. data/spec/models/callbacks/basic_multiple.rb +0 -75
  96. data/spec/models/callbacks/guard_within_block.rb +0 -67
  97. data/spec/models/callbacks/guard_within_block_multiple.rb +0 -66
  98. data/spec/models/callbacks/multiple_transitions_transition_guard.rb +0 -66
  99. data/spec/models/callbacks/multiple_transitions_transition_guard_multiple.rb +0 -65
  100. data/spec/models/callbacks/private_method.rb +0 -44
  101. data/spec/models/callbacks/private_method_multiple.rb +0 -44
  102. data/spec/models/callbacks/with_args.rb +0 -62
  103. data/spec/models/callbacks/with_args_multiple.rb +0 -61
  104. data/spec/models/callbacks/with_state_arg.rb +0 -30
  105. data/spec/models/callbacks/with_state_arg_multiple.rb +0 -26
  106. data/spec/models/complex_example.rb +0 -222
  107. data/spec/models/conversation.rb +0 -93
  108. data/spec/models/default_state.rb +0 -12
  109. data/spec/models/double_definer.rb +0 -21
  110. data/spec/models/dynamoid/complex_dynamoid_example.rb +0 -37
  111. data/spec/models/dynamoid/dynamoid_multiple.rb +0 -18
  112. data/spec/models/dynamoid/dynamoid_simple.rb +0 -18
  113. data/spec/models/foo.rb +0 -106
  114. data/spec/models/foo_callback_multiple.rb +0 -45
  115. data/spec/models/guard_arguments_check.rb +0 -17
  116. data/spec/models/guard_with_params.rb +0 -24
  117. data/spec/models/guard_with_params_multiple.rb +0 -18
  118. data/spec/models/guardian.rb +0 -58
  119. data/spec/models/guardian_multiple.rb +0 -48
  120. data/spec/models/guardian_without_from_specified.rb +0 -18
  121. data/spec/models/initial_state_proc.rb +0 -31
  122. data/spec/models/mongoid/complex_mongoid_example.rb +0 -37
  123. data/spec/models/mongoid/invalid_persistor_mongoid.rb +0 -39
  124. data/spec/models/mongoid/mongoid_relationships.rb +0 -26
  125. data/spec/models/mongoid/no_scope_mongoid.rb +0 -21
  126. data/spec/models/mongoid/silent_persistor_mongoid.rb +0 -39
  127. data/spec/models/mongoid/simple_mongoid.rb +0 -23
  128. data/spec/models/mongoid/simple_new_dsl_mongoid.rb +0 -25
  129. data/spec/models/mongoid/validator_mongoid.rb +0 -100
  130. data/spec/models/multi_transitioner.rb +0 -34
  131. data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +0 -31
  132. data/spec/models/namespaced_multiple_example.rb +0 -42
  133. data/spec/models/no_initial_state.rb +0 -25
  134. data/spec/models/not_auto_loaded/process.rb +0 -21
  135. data/spec/models/parametrised_event.rb +0 -42
  136. data/spec/models/parametrised_event_multiple.rb +0 -29
  137. data/spec/models/process_with_new_dsl.rb +0 -31
  138. data/spec/models/provided_state.rb +0 -24
  139. data/spec/models/redis/complex_redis_example.rb +0 -40
  140. data/spec/models/redis/redis_multiple.rb +0 -20
  141. data/spec/models/redis/redis_simple.rb +0 -20
  142. data/spec/models/sequel/complex_sequel_example.rb +0 -46
  143. data/spec/models/sequel/invalid_persistor.rb +0 -52
  144. data/spec/models/sequel/sequel_multiple.rb +0 -25
  145. data/spec/models/sequel/sequel_simple.rb +0 -26
  146. data/spec/models/sequel/silent_persistor.rb +0 -50
  147. data/spec/models/sequel/transactor.rb +0 -112
  148. data/spec/models/sequel/validator.rb +0 -93
  149. data/spec/models/sequel/worker.rb +0 -12
  150. data/spec/models/silencer.rb +0 -27
  151. data/spec/models/simple_custom_example.rb +0 -53
  152. data/spec/models/simple_example.rb +0 -15
  153. data/spec/models/simple_multiple_example.rb +0 -42
  154. data/spec/models/state_machine_with_failed_event.rb +0 -20
  155. data/spec/models/states_on_one_line_example.rb +0 -8
  156. data/spec/models/sub_class.rb +0 -41
  157. data/spec/models/sub_class_with_more_states.rb +0 -18
  158. data/spec/models/sub_classing.rb +0 -3
  159. data/spec/models/super_class.rb +0 -46
  160. data/spec/models/this_name_better_not_be_in_use.rb +0 -11
  161. data/spec/models/valid_state_name.rb +0 -23
  162. data/spec/spec_helper.rb +0 -26
  163. data/spec/spec_helpers/active_record.rb +0 -7
  164. data/spec/spec_helpers/dynamoid.rb +0 -33
  165. data/spec/spec_helpers/mongoid.rb +0 -7
  166. data/spec/spec_helpers/redis.rb +0 -15
  167. data/spec/spec_helpers/remove_warnings.rb +0 -1
  168. data/spec/spec_helpers/sequel.rb +0 -7
  169. data/spec/unit/api_spec.rb +0 -100
  170. data/spec/unit/basic_two_state_machines_example_spec.rb +0 -10
  171. data/spec/unit/callback_multiple_spec.rb +0 -300
  172. data/spec/unit/callbacks_spec.rb +0 -491
  173. data/spec/unit/complex_example_spec.rb +0 -94
  174. data/spec/unit/complex_multiple_example_spec.rb +0 -115
  175. data/spec/unit/edge_cases_spec.rb +0 -16
  176. data/spec/unit/event_multiple_spec.rb +0 -73
  177. data/spec/unit/event_naming_spec.rb +0 -16
  178. data/spec/unit/event_spec.rb +0 -381
  179. data/spec/unit/exception_spec.rb +0 -11
  180. data/spec/unit/guard_arguments_check_spec.rb +0 -9
  181. data/spec/unit/guard_multiple_spec.rb +0 -60
  182. data/spec/unit/guard_spec.rb +0 -89
  183. data/spec/unit/guard_with_params_multiple_spec.rb +0 -10
  184. data/spec/unit/guard_with_params_spec.rb +0 -14
  185. data/spec/unit/guard_without_from_specified_spec.rb +0 -10
  186. data/spec/unit/initial_state_multiple_spec.rb +0 -15
  187. data/spec/unit/initial_state_spec.rb +0 -12
  188. data/spec/unit/inspection_multiple_spec.rb +0 -201
  189. data/spec/unit/inspection_spec.rb +0 -149
  190. data/spec/unit/localizer_spec.rb +0 -78
  191. data/spec/unit/memory_leak_spec.rb +0 -38
  192. data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +0 -14
  193. data/spec/unit/namespaced_multiple_example_spec.rb +0 -75
  194. data/spec/unit/new_dsl_spec.rb +0 -12
  195. data/spec/unit/override_warning_spec.rb +0 -94
  196. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +0 -618
  197. data/spec/unit/persistence/active_record_persistence_spec.rb +0 -735
  198. data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +0 -135
  199. data/spec/unit/persistence/dynamoid_persistence_spec.rb +0 -84
  200. data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +0 -204
  201. data/spec/unit/persistence/mongoid_persistence_spec.rb +0 -169
  202. data/spec/unit/persistence/redis_persistence_multiple_spec.rb +0 -88
  203. data/spec/unit/persistence/redis_persistence_spec.rb +0 -53
  204. data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +0 -148
  205. data/spec/unit/persistence/sequel_persistence_spec.rb +0 -368
  206. data/spec/unit/readme_spec.rb +0 -41
  207. data/spec/unit/reloading_spec.rb +0 -15
  208. data/spec/unit/rspec_matcher_spec.rb +0 -79
  209. data/spec/unit/simple_custom_example_spec.rb +0 -39
  210. data/spec/unit/simple_example_spec.rb +0 -42
  211. data/spec/unit/simple_multiple_example_spec.rb +0 -91
  212. data/spec/unit/state_spec.rb +0 -89
  213. data/spec/unit/states_on_one_line_example_spec.rb +0 -16
  214. data/spec/unit/subclassing_multiple_spec.rb +0 -74
  215. data/spec/unit/subclassing_spec.rb +0 -46
  216. data/spec/unit/transition_spec.rb +0 -436
  217. data/test/minitest_helper.rb +0 -57
  218. data/test/unit/minitest_matcher_test.rb +0 -80
@@ -1,11 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AASM::Core
2
4
  class State
3
- attr_reader :name, :state_machine, :options
5
+ attr_reader :name, :state_machine, :options, :default_display_name
4
6
 
5
7
  def initialize(name, klass, state_machine, options={})
6
8
  @name = name
7
9
  @klass = klass
8
10
  @state_machine = state_machine
11
+ @default_display_name = name.to_s.gsub(/_/, ' ').capitalize
9
12
  update(options)
10
13
  end
11
14
 
@@ -13,7 +16,13 @@ module AASM::Core
13
16
  def initialize_copy(orig)
14
17
  super
15
18
  @options = {}
16
- orig.options.each_pair { |name, setting| @options[name] = setting.is_a?(Hash) || setting.is_a?(Array) ? setting.dup : setting }
19
+ orig.options.each_pair do |name, setting|
20
+ @options[name] = if setting.is_a?(Hash) || setting.is_a?(Array)
21
+ setting.dup
22
+ else
23
+ setting
24
+ end
25
+ end
17
26
  end
18
27
 
19
28
  def ==(state)
@@ -46,11 +55,11 @@ module AASM::Core
46
55
  end
47
56
 
48
57
  def display_name
49
- @display_name ||= begin
58
+ @display_name = begin
50
59
  if Module.const_defined?(:I18n)
51
60
  localized_name
52
61
  else
53
- name.to_s.gsub(/_/, ' ').capitalize
62
+ @default_display_name
54
63
  end
55
64
  end
56
65
  end
@@ -67,22 +76,15 @@ module AASM::Core
67
76
  private
68
77
 
69
78
  def update(options = {})
70
- if options.key?(:display) then
71
- @display_name = options.delete(:display)
79
+ if options.key?(:display)
80
+ @default_display_name = options.delete(:display)
72
81
  end
73
82
  @options = options
74
83
  self
75
84
  end
76
85
 
77
86
  def _fire_callbacks(action, record, args)
78
- case action
79
- when Symbol, String
80
- arity = record.__send__(:method, action.to_sym).arity
81
- record.__send__(action, *(arity < 0 ? args : args[0...arity]))
82
- when Proc
83
- arity = action.arity
84
- action.call(record, *(arity < 0 ? args : args[0...arity]))
85
- end
87
+ Invoker.new(action, record, args).invoke
86
88
  end
87
89
 
88
90
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AASM::Core
2
4
  class Transition
3
- include DslHelper
5
+ include AASM::DslHelper
4
6
 
5
7
  attr_reader :from, :to, :event, :opts, :failures
6
8
  alias_method :options, :opts
@@ -67,77 +69,14 @@ module AASM::Core
67
69
  record.aasm(event.state_machine.name).to_state = @to if record.aasm(event.state_machine.name).respond_to?(:to_state=)
68
70
  end
69
71
 
70
- case code
71
- when Symbol, String
72
- result = (record.__send__(:method, code.to_sym).arity == 0 ? record.__send__(code) : record.__send__(code, *args))
73
- failures << code unless result
74
- result
75
- when Proc
76
- if code.respond_to?(:parameters)
77
- # In Ruby's Proc, the 'arity' method is not a good condidate to know if
78
- # we should pass the arguments or not, since it does return 0 even in
79
- # presence of optional parameters.
80
- result = (code.parameters.size == 0 ? record.instance_exec(&code) : record.instance_exec(*args, &code))
81
-
82
- failures << code.source_location.join('#') unless result
83
- else
84
- # In RubyMotion's Proc, the 'parameter' method does not exists, however its
85
- # 'arity' method works just like the one from Method, only returning 0 when
86
- # there is no parameters whatsoever, optional or not.
87
- result = (code.arity == 0 ? record.instance_exec(&code) : record.instance_exec(*args, &code))
88
-
89
- # Sadly, RubyMotion's Proc does not define the method 'source_location' either.
90
- failures << code unless result
91
- end
92
-
93
- result
94
- when Class
95
- arity = code.instance_method(:initialize).arity
96
- if arity == 0
97
- instance = code.new
98
- elsif arity == 1
99
- instance = code.new(record)
100
- else
101
- instance = code.new(record, *args)
102
- end
103
- result = instance.call
104
-
105
- if Method.method_defined?(:source_location)
106
- failures << instance.method(:call).source_location.join('#') unless result
107
- else
108
- # RubyMotion support ('source_location' not defined for Method)
109
- failures << instance.method(:call) unless result
110
- end
111
-
112
- result
113
- when Array
114
- if options[:guard]
115
- # invoke guard callbacks
116
- code.all? {|a| invoke_callbacks_compatible_with_guard(a, record, args)}
117
- elsif options[:unless]
118
- # invoke unless callbacks
119
- code.all? {|a| !invoke_callbacks_compatible_with_guard(a, record, args)}
120
- else
121
- # invoke after callbacks
122
- code.map {|a| invoke_callbacks_compatible_with_guard(a, record, args)}
123
- end
124
- else
125
- true
126
- end
72
+ Invoker.new(code, record, args)
73
+ .with_options(options)
74
+ .with_failures(failures)
75
+ .invoke
127
76
  end
128
77
 
129
78
  def _fire_callbacks(code, record, args)
130
- case code
131
- when Symbol, String
132
- arity = record.send(:method, code.to_sym).arity
133
- record.send(code, *(arity < 0 ? args : args[0...arity]))
134
- when Proc
135
- code.arity == 0 ? record.instance_exec(&code) : record.instance_exec(*args, &code)
136
- when Array
137
- code.map {|a| _fire_callbacks(a, record, args)}
138
- else
139
- true
140
- end
79
+ Invoker.new(code, record, args).invoke
141
80
  end
142
81
 
143
82
  end
@@ -1,30 +1,32 @@
1
- module DslHelper
1
+ module AASM
2
+ module DslHelper
2
3
 
3
- class Proxy
4
- attr_accessor :options
4
+ class Proxy
5
+ attr_accessor :options
5
6
 
6
- def initialize(options, valid_keys, source)
7
- @valid_keys = valid_keys
8
- @source = source
7
+ def initialize(options, valid_keys, source)
8
+ @valid_keys = valid_keys
9
+ @source = source
9
10
 
10
- @options = options
11
- end
11
+ @options = options
12
+ end
12
13
 
13
- def method_missing(name, *args, &block)
14
- if @valid_keys.include?(name)
15
- options[name] = Array(options[name])
16
- options[name] << block if block
17
- options[name] += Array(args)
18
- else
19
- @source.send name, *args, &block
14
+ def method_missing(name, *args, &block)
15
+ if @valid_keys.include?(name)
16
+ options[name] = Array(options[name])
17
+ options[name] << block if block
18
+ options[name] += Array(args)
19
+ else
20
+ @source.send name, *args, &block
21
+ end
20
22
  end
21
23
  end
22
- end
23
24
 
24
- def add_options_from_dsl(options, valid_keys, &block)
25
- proxy = Proxy.new(options, valid_keys, self)
26
- proxy.instance_eval(&block)
27
- proxy.options
28
- end
25
+ def add_options_from_dsl(options, valid_keys, &block)
26
+ proxy = Proxy.new(options, valid_keys, self)
27
+ proxy.instance_eval(&block)
28
+ proxy.options
29
+ end
29
30
 
30
- end
31
+ end
32
+ end
data/lib/aasm/errors.rb CHANGED
@@ -3,18 +3,20 @@ module AASM
3
3
  class UnknownStateMachineError < RuntimeError; end
4
4
 
5
5
  class InvalidTransition < RuntimeError
6
- attr_reader :object, :event_name, :originating_state, :failures
6
+ attr_reader :object, :event_name, :originating_state, :failures, :state_machine_name
7
7
 
8
8
  def initialize(object, event_name, state_machine_name, failures = [])
9
9
  @object, @event_name, @originating_state, @failures = object, event_name, object.aasm(state_machine_name).current_state, failures
10
- super("Event '#{event_name}' cannot transition from '#{originating_state}'. #{reasoning}")
10
+ @state_machine_name = state_machine_name
11
+ super("Event '#{event_name}' cannot transition from '#{originating_state}'.#{reasoning}")
11
12
  end
12
13
 
13
14
  def reasoning
14
- "Failed callback(s): #{failures}." unless failures.empty?
15
+ " Failed callback(s): #{failures}." unless failures.empty?
15
16
  end
16
17
  end
17
18
 
18
19
  class UndefinedState < RuntimeError; end
20
+ class UndefinedEvent < UndefinedState; end
19
21
  class NoDirectAssignmentError < RuntimeError; end
20
22
  end
@@ -1,6 +1,5 @@
1
1
  module AASM
2
2
  class InstanceBase
3
-
4
3
  attr_accessor :from_state, :to_state, :current_event
5
4
 
6
5
  def initialize(instance, name=:default) # instance of the class including AASM, name of the state machine
@@ -28,7 +27,7 @@ module AASM
28
27
  end
29
28
 
30
29
  def human_state
31
- AASM::Localizer.new.human_state_name(@instance.class, state_object_for_name(current_state))
30
+ state_object_for_name(current_state).display_name
32
31
  end
33
32
 
34
33
  def states(options={}, *args)
@@ -78,6 +77,17 @@ module AASM
78
77
  events
79
78
  end
80
79
 
80
+ def permitted_transitions
81
+ events(permitted: true).flat_map do |event|
82
+ available_transitions = event.transitions_from_state(current_state)
83
+ allowed_transitions = available_transitions.select { |t| t.allowed?(@instance) }
84
+
85
+ allowed_transitions.map do |transition|
86
+ { event: event.name, state: transition.to }
87
+ end
88
+ end
89
+ end
90
+
81
91
  def state_object_for_name(name)
82
92
  obj = @instance.class.aasm(@name).states.find {|s| s.name == name}
83
93
  raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
@@ -91,7 +101,7 @@ module AASM
91
101
  when Proc
92
102
  state.call(@instance)
93
103
  else
94
- raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
104
+ raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
95
105
  end
96
106
  end
97
107
 
@@ -104,11 +114,15 @@ module AASM
104
114
  end
105
115
 
106
116
  def fire(event_name, *args, &block)
107
- @instance.send(:aasm_fire_event, @name, event_name, {persist: false}, *args, &block)
117
+ event_exists?(event_name)
118
+
119
+ @instance.send(event_name, *args, &block)
108
120
  end
109
121
 
110
122
  def fire!(event_name, *args, &block)
111
- @instance.send(:aasm_fire_event, @name, event_name, {persist: true}, *args, &block)
123
+ event_exists?(event_name, true)
124
+ bang_event_name = "#{event_name}!".to_sym
125
+ @instance.send(bang_event_name, *args, &block)
112
126
  end
113
127
 
114
128
  def set_current_state_with_persistence(state)
@@ -117,5 +131,14 @@ module AASM
117
131
  save_success
118
132
  end
119
133
 
134
+ private
135
+
136
+ def event_exists?(event_name, bang = false)
137
+ event = @instance.class.aasm(@name).state_machine.events[event_name.to_sym]
138
+ return true if event
139
+
140
+ event_error = bang ? "#{event_name}!" : event_name
141
+ raise AASM::UndefinedEvent, "Event :#{event_error} doesn't exist" if event.nil?
142
+ end
120
143
  end
121
144
  end
@@ -5,7 +5,7 @@ module AASM
5
5
  list << :"#{i18n_scope(klass)}.events.#{i18n_klass(ancestor)}.#{event}"
6
6
  list
7
7
  end
8
- translate_queue(checklist) || I18n.translate(checklist.shift, :default => event.to_s.humanize)
8
+ translate_queue(checklist) || I18n.translate(checklist.shift, :default => default_display_name(event))
9
9
  end
10
10
 
11
11
  def human_state_name(klass, state)
@@ -14,7 +14,7 @@ module AASM
14
14
  list << item_for(klass, state, ancestor, :old_style => true)
15
15
  list
16
16
  end
17
- translate_queue(checklist) || I18n.translate(checklist.shift, :default => state.to_s.humanize)
17
+ translate_queue(checklist) || I18n.translate(checklist.shift, :default => default_display_name(state))
18
18
  end
19
19
 
20
20
  private
@@ -46,8 +46,18 @@ module AASM
46
46
  end
47
47
 
48
48
  def ancestors_list(klass)
49
+ has_active_record_base = defined?(::ActiveRecord::Base)
49
50
  klass.ancestors.select do |ancestor|
50
- ancestor.respond_to?(:model_name) unless ancestor.name == 'ActiveRecord::Base'
51
+ not_active_record_base = has_active_record_base ? (ancestor != ::ActiveRecord::Base) : true
52
+ ancestor.respond_to?(:model_name) && not_active_record_base
53
+ end
54
+ end
55
+
56
+ def default_display_name(object) # Can use better arguement name
57
+ if object.respond_to?(:default_display_name)
58
+ object.default_display_name
59
+ else
60
+ object.to_s.gsub(/_/, ' ').capitalize
51
61
  end
52
62
  end
53
63
  end
@@ -41,14 +41,15 @@ module AASM
41
41
 
42
42
  module ClassMethods
43
43
  def aasm_create_scope(state_machine_name, scope_name)
44
- conditions = {
45
- table_name => { aasm(state_machine_name).attribute_name => scope_name.to_s }
46
- }
47
44
  if ActiveRecord::VERSION::MAJOR >= 3
45
+ conditions = { aasm(state_machine_name).attribute_name => scope_name.to_s }
48
46
  class_eval do
49
- scope scope_name, lambda { where(conditions) }
47
+ scope scope_name, lambda { where(table_name => conditions) }
50
48
  end
51
49
  else
50
+ conditions = {
51
+ table_name => { aasm(state_machine_name).attribute_name => scope_name.to_s }
52
+ }
52
53
  class_eval do
53
54
  named_scope scope_name, :conditions => conditions
54
55
  end
@@ -60,6 +61,24 @@ module AASM
60
61
 
61
62
  private
62
63
 
64
+ def aasm_execute_after_commit
65
+ begin
66
+ require 'after_commit_everywhere'
67
+ raise LoadError unless Gem::Version.new(::AfterCommitEverywhere::VERSION) >= Gem::Version.new('0.1.5')
68
+
69
+ self.extend ::AfterCommitEverywhere
70
+ after_commit do
71
+ yield
72
+ end
73
+ rescue LoadError
74
+ warn <<-MSG
75
+ [DEPRECATION] :after_commit AASM callback is not safe in terms of race conditions and redundant calls.
76
+ Please add `gem 'after_commit_everywhere', '~> 1.0'` to your Gemfile in order to fix that.
77
+ MSG
78
+ yield
79
+ end
80
+ end
81
+
63
82
  def aasm_raise_invalid_record
64
83
  raise ActiveRecord::RecordInvalid.new(self)
65
84
  end
@@ -140,7 +159,8 @@ module AASM
140
159
 
141
160
  def aasm_column_is_blank?(state_machine_name)
142
161
  attribute_name = self.class.aasm(state_machine_name).attribute_name
143
- attribute_names.include?(attribute_name.to_s) && send(attribute_name).blank?
162
+ attribute_names.include?(attribute_name.to_s) &&
163
+ (send(attribute_name).respond_to?(:empty?) ? !!send(attribute_name).empty? : !send(attribute_name))
144
164
  end
145
165
 
146
166
  def aasm_validate_states
@@ -34,7 +34,7 @@ module AASM
34
34
  # This allows for nil aasm states - be sure to add validation to your model
35
35
  def aasm_read_state(name=:default)
36
36
  state = send(self.class.aasm(name).attribute_name)
37
- if state.blank?
37
+ if !state || state.empty?
38
38
  aasm_new_record? ? aasm(name).determine_state_name(self.class.aasm(name).initial_state) : nil
39
39
  else
40
40
  state.to_sym
@@ -59,7 +59,9 @@ module AASM
59
59
  # make sure to create a (named) scope for each state
60
60
  def state_with_scope(*args)
61
61
  names = state_without_scope(*args)
62
- names.each { |name| create_scope(name) if create_scope?(name) }
62
+ names.each do |name|
63
+ create_scopes(name)
64
+ end
63
65
  end
64
66
  alias_method :state_without_scope, :state
65
67
  alias_method :state, :state_with_scope
@@ -71,7 +73,16 @@ module AASM
71
73
  end
72
74
 
73
75
  def create_scope(name)
74
- @klass.aasm_create_scope(@name, name)
76
+ @klass.aasm_create_scope(@name, name) if create_scope?(name)
77
+ end
78
+
79
+ def create_scopes(name)
80
+ if namespace?
81
+ # Create default scopes even when namespace? for backward compatibility
82
+ namepaced_name = "#{namespace}_#{name}"
83
+ create_scope(namepaced_name)
84
+ end
85
+ create_scope(name)
75
86
  end
76
87
  end # Base
77
88
 
@@ -77,7 +77,8 @@ module AASM
77
77
  #
78
78
  def aasm_ensure_initial_state
79
79
  AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
80
- send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s) if send(self.class.aasm(state_machine_name).attribute_name).blank?
80
+ next if !send(self.class.aasm(state_machine_name).attribute_name) || send(self.class.aasm(state_machine_name).attribute_name).empty?
81
+ send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s)
81
82
  end
82
83
  end
83
84
  end # InstanceMethods
@@ -83,7 +83,7 @@ module AASM
83
83
  #
84
84
  def aasm_ensure_initial_state
85
85
  AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
86
- aasm(state_machine_name).enter_initial_state if send(self.class.aasm(state_machine_name).attribute_name).blank?
86
+ aasm(state_machine_name).enter_initial_state if !send(self.class.aasm(state_machine_name).attribute_name) || send(self.class.aasm(state_machine_name).attribute_name).empty?
87
87
  end
88
88
  end
89
89
  end # InstanceMethods
@@ -106,7 +106,7 @@ module AASM
106
106
  # mongoid has_many relationship does not load child object attributes when
107
107
  # only ids are loaded, for example parent.child_ids will not load child object attributes.
108
108
  # This feature is introduced in mongoid > 4.
109
- if attribute_names.include?(attribute_name) && attributes[attribute_name].blank?
109
+ if attribute_names.include?(attribute_name) && !attributes[attribute_name] || attributes[attribute_name].empty?
110
110
  # attribute_missing? is defined in mongoid > 4
111
111
  return if Mongoid::VERSION.to_f >= 4 && attribute_missing?(attribute_name)
112
112
  send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s)
@@ -0,0 +1,105 @@
1
+ require 'aasm/persistence/orm'
2
+ module AASM
3
+ module Persistence
4
+ module NoBrainerPersistence
5
+ # This method:
6
+ #
7
+ # * extends the model with ClassMethods
8
+ # * includes InstanceMethods
9
+ #
10
+ # Adds
11
+ #
12
+ # before_validation :aasm_ensure_initial_state
13
+ #
14
+ # As a result, it doesn't matter when you define your methods - the following 2 are equivalent
15
+ #
16
+ # class Foo
17
+ # include NoBrainer::Document
18
+ # def aasm_write_state(state)
19
+ # "bar"
20
+ # end
21
+ # include AASM
22
+ # end
23
+ #
24
+ # class Foo
25
+ # include NoBrainer::Document
26
+ # include AASM
27
+ # def aasm_write_state(state)
28
+ # "bar"
29
+ # end
30
+ # end
31
+ #
32
+ def self.included(base)
33
+ base.send(:include, AASM::Persistence::Base)
34
+ base.send(:include, AASM::Persistence::ORM)
35
+ base.send(:include, AASM::Persistence::NoBrainerPersistence::InstanceMethods)
36
+ base.extend AASM::Persistence::NoBrainerPersistence::ClassMethods
37
+
38
+ base.after_initialize :aasm_ensure_initial_state
39
+ end
40
+
41
+ module ClassMethods
42
+ def aasm_create_scope(state_machine_name, scope_name)
43
+ scope_options = lambda {
44
+ where(aasm(state_machine_name).attribute_name.to_sym => scope_name.to_s)
45
+ }
46
+ send(:scope, scope_name, scope_options)
47
+ end
48
+ end
49
+
50
+ module InstanceMethods
51
+
52
+ private
53
+
54
+ def aasm_save
55
+ self.save
56
+ end
57
+
58
+ def aasm_raise_invalid_record
59
+ raise NoBrainer::Error::DocumentInvalid.new(self)
60
+ end
61
+
62
+ def aasm_supports_transactions?
63
+ false
64
+ end
65
+
66
+ def aasm_update_column(attribute_name, value)
67
+ write_attribute(attribute_name, value)
68
+ save(validate: false)
69
+
70
+ true
71
+ end
72
+
73
+ def aasm_read_attribute(name)
74
+ read_attribute(name)
75
+ end
76
+
77
+ def aasm_write_attribute(name, value)
78
+ write_attribute(name, value)
79
+ end
80
+
81
+ # Ensures that if the aasm_state column is nil and the record is new
82
+ # that the initial state gets populated before validation on create
83
+ #
84
+ # foo = Foo.new
85
+ # foo.aasm_state # => nil
86
+ # foo.valid?
87
+ # foo.aasm_state # => "open" (where :open is the initial state)
88
+ #
89
+ #
90
+ # foo = Foo.find(:first)
91
+ # foo.aasm_state # => 1
92
+ # foo.aasm_state = nil
93
+ # foo.valid?
94
+ # foo.aasm_state # => nil
95
+ #
96
+ def aasm_ensure_initial_state
97
+ AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |name|
98
+ aasm_column = self.class.aasm(name).attribute_name
99
+ aasm(name).enter_initial_state if !read_attribute(aasm_column) || read_attribute(aasm_column).empty?
100
+ end
101
+ end
102
+ end # InstanceMethods
103
+ end
104
+ end # Persistence
105
+ end # AASM
@@ -81,6 +81,10 @@ module AASM
81
81
  true
82
82
  end
83
83
 
84
+ def aasm_execute_after_commit
85
+ yield
86
+ end
87
+
84
88
  def aasm_write_state_attribute(state, name=:default)
85
89
  aasm_write_attribute(self.class.aasm(name).attribute_name, aasm_raw_attribute_value(state, name))
86
90
  end
@@ -116,32 +120,32 @@ module AASM
116
120
 
117
121
  # Returns true if event was fired successfully and transaction completed.
118
122
  def aasm_fire_event(state_machine_name, name, options, *args, &block)
119
- if aasm_supports_transactions? && options[:persist]
120
- event = self.class.aasm(state_machine_name).state_machine.events[name]
121
- event.fire_callbacks(:before_transaction, self, *args)
122
- event.fire_global_callbacks(:before_all_transactions, self, *args)
123
-
124
- begin
125
- success = if options[:persist] && use_transactions?(state_machine_name)
126
- aasm_transaction(requires_new?(state_machine_name), requires_lock?(state_machine_name)) do
127
- super
128
- end
129
- else
123
+ return super unless aasm_supports_transactions? && options[:persist]
124
+
125
+ event = self.class.aasm(state_machine_name).state_machine.events[name]
126
+ event.fire_callbacks(:before_transaction, self, *args)
127
+ event.fire_global_callbacks(:before_all_transactions, self, *args)
128
+
129
+ begin
130
+ success = if options[:persist] && use_transactions?(state_machine_name)
131
+ aasm_transaction(requires_new?(state_machine_name), requires_lock?(state_machine_name)) do
130
132
  super
131
133
  end
134
+ else
135
+ super
136
+ end
132
137
 
133
- if success
138
+ if success && !(event.options.keys & [:after_commit, :after_all_commits]).empty?
139
+ aasm_execute_after_commit do
134
140
  event.fire_callbacks(:after_commit, self, *args)
135
141
  event.fire_global_callbacks(:after_all_commits, self, *args)
136
142
  end
137
-
138
- success
139
- ensure
140
- event.fire_callbacks(:after_transaction, self, *args)
141
- event.fire_global_callbacks(:after_all_transactions, self, *args)
142
143
  end
143
- else
144
- super
144
+
145
+ success
146
+ ensure
147
+ event.fire_callbacks(:after_transaction, self, *args)
148
+ event.fire_global_callbacks(:after_all_transactions, self, *args)
145
149
  end
146
150
  end
147
151
 
@@ -5,7 +5,8 @@ module AASM
5
5
  # may be overwritten by persistence mixins
6
6
  def aasm_read_state(name=:default)
7
7
  # all the following lines behave like @current_state ||= aasm(name).enter_initial_state
8
- current = aasm(name).instance_variable_get("@current_state_#{name}")
8
+ current = aasm(name).instance_variable_defined?("@current_state_#{name}") &&
9
+ aasm(name).instance_variable_get("@current_state_#{name}")
9
10
  return current if current
10
11
  aasm(name).instance_variable_set("@current_state_#{name}", aasm(name).enter_initial_state)
11
12
  end
@@ -69,7 +69,7 @@ module AASM
69
69
  def aasm_ensure_initial_state
70
70
  AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |name|
71
71
  aasm_column = self.class.aasm(name).attribute_name
72
- aasm(name).enter_initial_state if send(aasm_column).value.blank?
72
+ aasm(name).enter_initial_state if !send(aasm_column).value || send(aasm_column).value.empty?
73
73
  end
74
74
  end
75
75
 
@@ -15,7 +15,6 @@ module AASM
15
15
  end
16
16
 
17
17
  def before_create
18
- aasm_ensure_initial_state
19
18
  super
20
19
  end
21
20