aasm 4.12.3 → 5.1.1

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 (114) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. data/.travis.yml +48 -18
  5. data/Appraisals +50 -26
  6. data/CHANGELOG.md +75 -3
  7. data/Dockerfile +44 -0
  8. data/Gemfile +2 -3
  9. data/README.md +216 -110
  10. data/aasm.gemspec +2 -0
  11. data/docker-compose.yml +40 -0
  12. data/gemfiles/norails.gemfile +10 -0
  13. data/gemfiles/rails_4.2.gemfile +9 -8
  14. data/gemfiles/rails_4.2_mongoid_5.gemfile +6 -5
  15. data/gemfiles/rails_4.2_nobrainer.gemfile +9 -0
  16. data/gemfiles/rails_5.0.gemfile +6 -6
  17. data/gemfiles/rails_5.0_nobrainer.gemfile +9 -0
  18. data/gemfiles/rails_5.1.gemfile +14 -0
  19. data/gemfiles/rails_5.2.gemfile +14 -0
  20. data/lib/aasm.rb +5 -2
  21. data/lib/aasm/aasm.rb +30 -27
  22. data/lib/aasm/base.rb +25 -7
  23. data/lib/aasm/core/event.rb +14 -24
  24. data/lib/aasm/core/invoker.rb +129 -0
  25. data/lib/aasm/core/invokers/base_invoker.rb +75 -0
  26. data/lib/aasm/core/invokers/class_invoker.rb +52 -0
  27. data/lib/aasm/core/invokers/literal_invoker.rb +47 -0
  28. data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
  29. data/lib/aasm/core/state.rb +10 -9
  30. data/lib/aasm/core/transition.rb +7 -68
  31. data/lib/aasm/errors.rb +4 -3
  32. data/lib/aasm/instance_base.rb +16 -4
  33. data/lib/aasm/persistence.rb +3 -0
  34. data/lib/aasm/persistence/active_record_persistence.rb +25 -5
  35. data/lib/aasm/persistence/base.rb +1 -1
  36. data/lib/aasm/persistence/core_data_query_persistence.rb +2 -1
  37. data/lib/aasm/persistence/dynamoid_persistence.rb +1 -1
  38. data/lib/aasm/persistence/mongoid_persistence.rb +1 -1
  39. data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
  40. data/lib/aasm/persistence/orm.rb +23 -19
  41. data/lib/aasm/persistence/plain_persistence.rb +2 -1
  42. data/lib/aasm/persistence/redis_persistence.rb +1 -1
  43. data/lib/aasm/persistence/sequel_persistence.rb +0 -1
  44. data/lib/aasm/rspec/allow_event.rb +5 -1
  45. data/lib/aasm/rspec/allow_transition_to.rb +5 -1
  46. data/lib/aasm/rspec/transition_from.rb +5 -1
  47. data/lib/aasm/version.rb +1 -1
  48. data/lib/generators/aasm/orm_helpers.rb +6 -0
  49. data/lib/generators/active_record/aasm_generator.rb +3 -1
  50. data/lib/generators/nobrainer/aasm_generator.rb +28 -0
  51. data/lib/motion-aasm.rb +1 -0
  52. data/spec/database.rb +16 -1
  53. data/spec/en.yml +0 -3
  54. data/spec/generators/active_record_generator_spec.rb +6 -0
  55. data/spec/generators/no_brainer_generator_spec.rb +29 -0
  56. data/spec/{en_deprecated_style.yml → localizer_test_model_deprecated_style.yml} +0 -4
  57. data/spec/localizer_test_model_new_style.yml +5 -0
  58. data/spec/models/active_record/active_record_callback.rb +93 -0
  59. data/spec/models/active_record/instance_level_skip_validation_example.rb +19 -0
  60. data/spec/models/active_record/localizer_test_model.rb +3 -3
  61. data/spec/models/active_record/person.rb +23 -0
  62. data/spec/models/active_record/simple_new_dsl.rb +15 -0
  63. data/spec/models/active_record/work.rb +3 -0
  64. data/spec/models/callbacks/with_state_arg.rb +5 -1
  65. data/spec/models/callbacks/with_state_arg_multiple.rb +4 -1
  66. data/spec/models/default_state.rb +1 -1
  67. data/spec/models/nobrainer/complex_no_brainer_example.rb +36 -0
  68. data/spec/models/nobrainer/invalid_persistor_no_brainer.rb +39 -0
  69. data/spec/models/nobrainer/no_scope_no_brainer.rb +21 -0
  70. data/spec/models/nobrainer/nobrainer_relationships.rb +25 -0
  71. data/spec/models/nobrainer/silent_persistor_no_brainer.rb +39 -0
  72. data/spec/models/nobrainer/simple_new_dsl_nobrainer.rb +25 -0
  73. data/spec/models/nobrainer/simple_no_brainer.rb +23 -0
  74. data/spec/models/nobrainer/validator_no_brainer.rb +98 -0
  75. data/spec/models/simple_example.rb +8 -0
  76. data/spec/models/simple_example_with_guard_args.rb +17 -0
  77. data/spec/spec_helper.rb +15 -0
  78. data/spec/spec_helpers/active_record.rb +2 -1
  79. data/spec/spec_helpers/dynamoid.rb +7 -5
  80. data/spec/spec_helpers/mongoid.rb +20 -1
  81. data/spec/spec_helpers/nobrainer.rb +15 -0
  82. data/spec/spec_helpers/redis.rb +5 -2
  83. data/spec/spec_helpers/sequel.rb +1 -1
  84. data/spec/unit/abstract_class_spec.rb +27 -0
  85. data/spec/unit/api_spec.rb +4 -0
  86. data/spec/unit/callback_multiple_spec.rb +7 -3
  87. data/spec/unit/callbacks_spec.rb +32 -2
  88. data/spec/unit/complex_example_spec.rb +0 -1
  89. data/spec/unit/event_spec.rb +13 -0
  90. data/spec/unit/exception_spec.rb +1 -1
  91. data/spec/unit/inspection_multiple_spec.rb +9 -5
  92. data/spec/unit/inspection_spec.rb +7 -3
  93. data/spec/unit/invoker_spec.rb +189 -0
  94. data/spec/unit/invokers/base_invoker_spec.rb +72 -0
  95. data/spec/unit/invokers/class_invoker_spec.rb +95 -0
  96. data/spec/unit/invokers/literal_invoker_spec.rb +86 -0
  97. data/spec/unit/invokers/proc_invoker_spec.rb +86 -0
  98. data/spec/unit/localizer_spec.rb +9 -10
  99. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +4 -4
  100. data/spec/unit/persistence/active_record_persistence_spec.rb +109 -4
  101. data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +0 -4
  102. data/spec/unit/persistence/mongoid_persistence_spec.rb +0 -4
  103. data/spec/unit/persistence/no_brainer_persistence_multiple_spec.rb +198 -0
  104. data/spec/unit/persistence/no_brainer_persistence_spec.rb +158 -0
  105. data/spec/unit/rspec_matcher_spec.rb +9 -0
  106. data/spec/unit/simple_example_spec.rb +15 -0
  107. data/spec/unit/state_spec.rb +23 -7
  108. data/spec/unit/transition_spec.rb +1 -1
  109. data/test/minitest_helper.rb +2 -2
  110. data/test/unit/minitest_matcher_test.rb +1 -1
  111. metadata +106 -12
  112. data/callbacks.txt +0 -51
  113. data/gemfiles/rails_3.2.gemfile +0 -13
  114. data/gemfiles/rails_4.0.gemfile +0 -15
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AASM
4
+ module Core
5
+ module Invokers
6
+ ##
7
+ # Base concrete invoker class which contain basic
8
+ # invoking and logging definitions
9
+ class BaseInvoker
10
+ attr_reader :failures, :subject, :record, :args, :result
11
+
12
+ ##
13
+ # Initialize a new concrete invoker instance.
14
+ # NOTE that concrete invoker must be used per-subject/record
15
+ # (one instance per subject/record)
16
+ #
17
+ # ==Options:
18
+ #
19
+ # +subject+ - invoking subject comparable with this invoker
20
+ # +record+ - invoking record
21
+ # +args+ - arguments which will be passed to the callback
22
+
23
+ def initialize(subject, record, args)
24
+ @subject = subject
25
+ @record = record
26
+ @args = args
27
+ @result = false
28
+ @failures = []
29
+ end
30
+
31
+ ##
32
+ # Collect failures to a specified buffer
33
+ #
34
+ # ==Options:
35
+ #
36
+ # +failures+ - failures buffer to collect failures
37
+
38
+ def with_failures(failures_buffer)
39
+ @failures = failures_buffer
40
+ self
41
+ end
42
+
43
+ ##
44
+ # Execute concrete invoker, log the error and return result
45
+
46
+ def invoke
47
+ return unless may_invoke?
48
+ log_failure unless invoke_subject
49
+ result
50
+ end
51
+
52
+ ##
53
+ # Check if concrete invoker may be invoked for a specified subject
54
+
55
+ def may_invoke?
56
+ raise NoMethodError, '"#may_invoke?" is not implemented'
57
+ end
58
+
59
+ ##
60
+ # Log failed invoking
61
+
62
+ def log_failure
63
+ raise NoMethodError, '"#log_failure" is not implemented'
64
+ end
65
+
66
+ ##
67
+ # Execute concrete invoker
68
+
69
+ def invoke_subject
70
+ raise NoMethodError, '"#invoke_subject" is not implemented'
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AASM
4
+ module Core
5
+ module Invokers
6
+ ##
7
+ # Class invoker which allows to use classes which respond to #call
8
+ # to be used as state/event/transition callbacks.
9
+ class ClassInvoker < BaseInvoker
10
+ def may_invoke?
11
+ subject.is_a?(Class) && subject.instance_methods.include?(:call)
12
+ end
13
+
14
+ def log_failure
15
+ return log_source_location if Method.method_defined?(:source_location)
16
+ log_method_info
17
+ end
18
+
19
+ def invoke_subject
20
+ @result = retrieve_instance.call
21
+ end
22
+
23
+ private
24
+
25
+ def log_source_location
26
+ failures << instance.method(:call).source_location.join('#')
27
+ end
28
+
29
+ def log_method_info
30
+ failures << instance.method(:call)
31
+ end
32
+
33
+ def instance
34
+ @instance ||= retrieve_instance
35
+ end
36
+
37
+ # rubocop:disable Metrics/AbcSize
38
+ def retrieve_instance
39
+ return subject.new if subject_arity.zero?
40
+ return subject.new(record) if subject_arity == 1
41
+ return subject.new(record, *args) if subject_arity < 0
42
+ subject.new(record, *args[0..(subject_arity - 2)])
43
+ end
44
+ # rubocop:enable Metrics/AbcSize
45
+
46
+ def subject_arity
47
+ @arity ||= subject.instance_method(:initialize).arity
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AASM
4
+ module Core
5
+ module Invokers
6
+ ##
7
+ # Literal invoker which allows to use strings or symbols to call
8
+ # record methods as state/event/transition callbacks.
9
+ class LiteralInvoker < BaseInvoker
10
+ def may_invoke?
11
+ subject.is_a?(String) || subject.is_a?(Symbol)
12
+ end
13
+
14
+ def log_failure
15
+ failures << subject
16
+ end
17
+
18
+ def invoke_subject
19
+ @result = exec_subject
20
+ end
21
+
22
+ private
23
+
24
+ def subject_arity
25
+ @arity ||= record.__send__(:method, subject.to_sym).arity
26
+ end
27
+
28
+ # rubocop:disable Metrics/AbcSize
29
+ def exec_subject
30
+ raise(*record_error) unless record.respond_to?(subject, true)
31
+ return record.__send__(subject) if subject_arity.zero?
32
+ return record.__send__(subject, *args) if subject_arity < 0
33
+ record.__send__(subject, *args[0..(subject_arity - 1)])
34
+ end
35
+ # rubocop:enable Metrics/AbcSize
36
+
37
+ def record_error
38
+ [
39
+ NoMethodError,
40
+ 'NoMethodError: undefined method ' \
41
+ "`#{subject}' for #{record.inspect}:#{record.class}"
42
+ ]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AASM
4
+ module Core
5
+ module Invokers
6
+ ##
7
+ # Proc invoker which allows to use Procs as
8
+ # state/event/transition callbacks.
9
+ class ProcInvoker < BaseInvoker
10
+ def may_invoke?
11
+ subject.is_a?(Proc)
12
+ end
13
+
14
+ def log_failure
15
+ return log_source_location if Method.method_defined?(:source_location)
16
+ log_proc_info
17
+ end
18
+
19
+ def invoke_subject
20
+ @result = if support_parameters?
21
+ exec_proc(parameters_to_arity)
22
+ else
23
+ exec_proc(subject.arity)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def support_parameters?
30
+ subject.respond_to?(:parameters)
31
+ end
32
+
33
+ # rubocop:disable Metrics/AbcSize
34
+ def exec_proc(parameters_size)
35
+ return record.instance_exec(&subject) if parameters_size.zero?
36
+ return record.instance_exec(*args, &subject) if parameters_size < 0
37
+ record.instance_exec(*args[0..(parameters_size - 1)], &subject)
38
+ end
39
+ # rubocop:enable Metrics/AbcSize
40
+
41
+ def log_source_location
42
+ failures << subject.source_location.join('#')
43
+ end
44
+
45
+ def log_proc_info
46
+ failures << subject
47
+ end
48
+
49
+ def parameters_to_arity
50
+ subject.parameters.inject(0) do |memo, parameter|
51
+ memo += 1
52
+ memo *= -1 if parameter[0] == :rest && memo > 0
53
+ memo
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AASM::Core
2
4
  class State
3
5
  attr_reader :name, :state_machine, :options
@@ -13,7 +15,13 @@ module AASM::Core
13
15
  def initialize_copy(orig)
14
16
  super
15
17
  @options = {}
16
- orig.options.each_pair { |name, setting| @options[name] = setting.is_a?(Hash) || setting.is_a?(Array) ? setting.dup : setting }
18
+ orig.options.each_pair do |name, setting|
19
+ @options[name] = if setting.is_a?(Hash) || setting.is_a?(Array)
20
+ setting.dup
21
+ else
22
+ setting
23
+ end
24
+ end
17
25
  end
18
26
 
19
27
  def ==(state)
@@ -75,14 +83,7 @@ module AASM::Core
75
83
  end
76
84
 
77
85
  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
86
+ Invoker.new(action, record, args).invoke
86
87
  end
87
88
 
88
89
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AASM::Core
2
4
  class Transition
3
5
  include DslHelper
@@ -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
data/lib/aasm/errors.rb CHANGED
@@ -3,15 +3,16 @@ 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
 
@@ -28,7 +28,7 @@ module AASM
28
28
  end
29
29
 
30
30
  def human_state
31
- AASM::Localizer.new.human_state_name(@instance.class, state_object_for_name(current_state))
31
+ state_object_for_name(current_state).display_name
32
32
  end
33
33
 
34
34
  def states(options={}, *args)
@@ -78,6 +78,17 @@ module AASM
78
78
  events
79
79
  end
80
80
 
81
+ def permitted_transitions
82
+ events(permitted: true).flat_map do |event|
83
+ available_transitions = event.transitions_from_state(current_state)
84
+ allowed_transitions = available_transitions.select { |t| t.allowed?(@instance) }
85
+
86
+ allowed_transitions.map do |transition|
87
+ { event: event.name, state: transition.to }
88
+ end
89
+ end
90
+ end
91
+
81
92
  def state_object_for_name(name)
82
93
  obj = @instance.class.aasm(@name).states.find {|s| s.name == name}
83
94
  raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
@@ -91,7 +102,7 @@ module AASM
91
102
  when Proc
92
103
  state.call(@instance)
93
104
  else
94
- raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
105
+ raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
95
106
  end
96
107
  end
97
108
 
@@ -104,11 +115,12 @@ module AASM
104
115
  end
105
116
 
106
117
  def fire(event_name, *args, &block)
107
- @instance.send(:aasm_fire_event, @name, event_name, {persist: false}, *args, &block)
118
+ @instance.send(event_name, *args, &block)
108
119
  end
109
120
 
110
121
  def fire!(event_name, *args, &block)
111
- @instance.send(:aasm_fire_event, @name, event_name, {persist: true}, *args, &block)
122
+ event_name = event_name.to_s.+("!").to_sym
123
+ @instance.send(event_name, *args, &block)
112
124
  end
113
125
 
114
126
  def set_current_state_with_persistence(state)
@@ -12,6 +12,9 @@ module AASM
12
12
  elsif hierarchy.include?("Mongoid::Document")
13
13
  require_persistence :mongoid
14
14
  include_persistence base, :mongoid
15
+ elsif hierarchy.include?("NoBrainer::Document")
16
+ require_persistence :no_brainer
17
+ include_persistence base, :no_brainer
15
18
  elsif hierarchy.include?("Sequel::Model")
16
19
  require_persistence :sequel
17
20
  include_persistence base, :sequel
@@ -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', '~> 0.1', '>= 0.1.5'` 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