aasm 4.11.1 → 4.12.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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +15 -16
  3. data/Appraisals +44 -0
  4. data/CHANGELOG.md +14 -0
  5. data/CONTRIBUTING.md +24 -0
  6. data/Gemfile +4 -21
  7. data/README.md +53 -32
  8. data/Rakefile +6 -1
  9. data/TESTING.md +25 -0
  10. data/aasm.gemspec +3 -0
  11. data/gemfiles/rails_3.2.gemfile +13 -0
  12. data/gemfiles/rails_4.0.gemfile +8 -9
  13. data/gemfiles/rails_4.2.gemfile +9 -9
  14. data/gemfiles/rails_4.2_mongoid_5.gemfile +5 -9
  15. data/gemfiles/rails_5.0.gemfile +7 -16
  16. data/lib/aasm/aasm.rb +9 -3
  17. data/lib/aasm/base.rb +3 -1
  18. data/lib/aasm/configuration.rb +4 -0
  19. data/lib/aasm/core/event.rb +17 -3
  20. data/lib/aasm/core/state.rb +7 -0
  21. data/lib/aasm/core/transition.rb +9 -0
  22. data/lib/aasm/persistence.rb +0 -3
  23. data/lib/aasm/persistence/active_record_persistence.rb +1 -1
  24. data/lib/aasm/persistence/mongoid_persistence.rb +48 -9
  25. data/lib/aasm/state_machine.rb +4 -2
  26. data/lib/aasm/state_machine_store.rb +5 -2
  27. data/lib/aasm/version.rb +1 -1
  28. data/lib/motion-aasm.rb +0 -1
  29. data/spec/generators/active_record_generator_spec.rb +42 -39
  30. data/spec/generators/mongoid_generator_spec.rb +4 -6
  31. data/spec/models/{invalid_persistor.rb → active_record/invalid_persistor.rb} +0 -2
  32. data/spec/models/{silent_persistor.rb → active_record/silent_persistor.rb} +0 -2
  33. data/spec/models/{transactor.rb → active_record/transactor.rb} +0 -2
  34. data/spec/models/{validator.rb → active_record/validator.rb} +0 -2
  35. data/spec/models/{worker.rb → active_record/worker.rb} +0 -0
  36. data/spec/models/callbacks/basic.rb +5 -2
  37. data/spec/models/guard_with_params.rb +1 -1
  38. data/spec/models/mongoid/invalid_persistor_mongoid.rb +39 -0
  39. data/spec/models/mongoid/silent_persistor_mongoid.rb +39 -0
  40. data/spec/models/mongoid/validator_mongoid.rb +100 -0
  41. data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +31 -0
  42. data/spec/models/parametrised_event.rb +7 -0
  43. data/spec/models/simple_multiple_example.rb +12 -0
  44. data/spec/models/sub_class.rb +34 -0
  45. data/spec/spec_helper.rb +0 -33
  46. data/spec/spec_helpers/active_record.rb +7 -0
  47. data/spec/spec_helpers/dynamoid.rb +33 -0
  48. data/spec/spec_helpers/mongoid.rb +7 -0
  49. data/spec/spec_helpers/redis.rb +7 -0
  50. data/spec/spec_helpers/remove_warnings.rb +1 -0
  51. data/spec/spec_helpers/sequel.rb +7 -0
  52. data/spec/unit/api_spec.rb +76 -73
  53. data/spec/unit/callbacks_spec.rb +5 -0
  54. data/spec/unit/event_spec.rb +12 -0
  55. data/spec/unit/guard_with_params_spec.rb +4 -0
  56. data/spec/unit/localizer_spec.rb +55 -53
  57. data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +14 -0
  58. data/spec/unit/override_warning_spec.rb +8 -0
  59. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +452 -448
  60. data/spec/unit/persistence/active_record_persistence_spec.rb +523 -501
  61. data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +4 -9
  62. data/spec/unit/persistence/dynamoid_persistence_spec.rb +4 -9
  63. data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +83 -9
  64. data/spec/unit/persistence/mongoid_persistence_spec.rb +85 -9
  65. data/spec/unit/persistence/redis_persistence_spec.rb +3 -7
  66. data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +4 -9
  67. data/spec/unit/persistence/sequel_persistence_spec.rb +4 -9
  68. data/spec/unit/simple_multiple_example_spec.rb +28 -0
  69. data/spec/unit/subclassing_multiple_spec.rb +37 -2
  70. data/spec/unit/subclassing_spec.rb +17 -2
  71. metadata +66 -28
  72. data/gemfiles/rails_3.2_stable.gemfile +0 -15
  73. data/gemfiles/rails_4.0_mongo_mapper.gemfile +0 -16
  74. data/gemfiles/rails_4.2_mongo_mapper.gemfile +0 -17
  75. data/lib/aasm/persistence/mongo_mapper_persistence.rb +0 -163
  76. data/spec/models/mongo_mapper/complex_mongo_mapper_example.rb +0 -37
  77. data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +0 -21
  78. data/spec/models/mongo_mapper/simple_mongo_mapper.rb +0 -23
  79. data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +0 -25
  80. data/spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb +0 -149
  81. data/spec/unit/persistence/mongo_mapper_persistence_spec.rb +0 -96
@@ -1,15 +1,11 @@
1
+ # This file was generated by Appraisal
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
- gem "sqlite3", :platforms => :ruby
4
- gem 'rubysl', :platforms => :rbx
5
- gem 'rubinius-developer_tools', :platforms => :rbx
6
- gem "jruby-openssl", :platforms => :jruby
5
+ gem "sqlite3", :platforms => :ruby
7
6
  gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
8
- gem "mime-types", "~> 2" if Gem::Version.create(RUBY_VERSION.dup) <= Gem::Version.create('1.9.3')
9
7
  gem "rails", "4.2.5"
10
- gem 'mongoid', '~>5.0' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
11
- gem 'sequel'
12
- gem 'dynamoid', '~> 1', :platforms => :ruby
13
- gem 'aws-sdk', '~>2', :platforms => :ruby
8
+ gem "mime-types", "~> 2", :platforms => [:ruby_19, :jruby]
9
+ gem "mongoid", "~>5.0"
14
10
 
15
11
  gemspec :path => "../"
@@ -1,21 +1,12 @@
1
+ # This file was generated by Appraisal
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
- gem "sqlite3", :platforms => :ruby
4
- gem 'rubysl', :platforms => :rbx
5
- gem 'rubinius-developer_tools', :platforms => :rbx
6
- gem "jruby-openssl", :platforms => :jruby
5
+ gem "sqlite3", :platforms => :ruby
7
6
  gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
8
-
9
- gem "rails", "5.0.0.beta4"
10
-
11
- # mongoid is not yet Rails 5 compatible
12
- # gem 'mongoid', '~>4.0' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
13
-
14
- gem 'sequel'
15
-
16
- # dynamoid is not yet Rails 5 compatible
17
- # gem 'dynamoid', '~> 1', :platforms => :ruby
18
-
19
- gem 'aws-sdk', '~>2', :platforms => :ruby
7
+ gem "rails", "5.0.0"
8
+ gem "mongoid", "~>6.0"
9
+ gem "sequel"
10
+ gem "aws-sdk", "~>2", :platforms => :ruby
20
11
 
21
12
  gemspec :path => "../"
@@ -42,7 +42,7 @@ module AASM
42
42
 
43
43
  raise ArgumentError, "The class #{aasm_klass} must inherit from AASM::Base!" unless aasm_klass.ancestors.include?(AASM::Base)
44
44
 
45
- @aasm ||= {}
45
+ @aasm ||= Concurrent::Map.new
46
46
  if @aasm[state_machine_name]
47
47
  # make sure to use provided options
48
48
  options.each do |key, value|
@@ -67,12 +67,12 @@ module AASM
67
67
  unless AASM::StateMachineStore.fetch(self.class, true).machine(name)
68
68
  raise AASM::UnknownStateMachineError.new("There is no state machine with the name '#{name}' defined in #{self.class.name}!")
69
69
  end
70
- @aasm ||= {}
70
+ @aasm ||= Concurrent::Map.new
71
71
  @aasm[name.to_sym] ||= AASM::InstanceBase.new(self, name.to_sym)
72
72
  end
73
73
 
74
74
  def initialize_dup(other)
75
- @aasm = {}
75
+ @aasm = Concurrent::Map.new
76
76
  super
77
77
  end
78
78
 
@@ -150,6 +150,7 @@ private
150
150
  persist_successful = aasm(state_machine_name).set_current_state_with_persistence(new_state_name)
151
151
  if persist_successful
152
152
  yield if block_given?
153
+ event.fire_callbacks(:before_success, self)
153
154
  event.fire_transition_callbacks(self, *process_args(event, old_state.name, *args))
154
155
  event.fire_callbacks(:success, self)
155
156
  end
@@ -158,6 +159,11 @@ private
158
159
  yield if block_given?
159
160
  end
160
161
 
162
+ binding_event = event.options[:binding_event]
163
+ if binding_event
164
+ __send__("#{binding_event}#{'!' if persist}")
165
+ end
166
+
161
167
  if persist_successful
162
168
  old_state.fire_callbacks(:after_exit, self,
163
169
  *process_args(event, aasm(state_machine_name).current_state, *args))
@@ -208,7 +208,9 @@ module AASM
208
208
  klass.defined_enums.values.any?{ |methods|
209
209
  methods.keys{| enum | enum + '?' == method_name }
210
210
  })
211
- @state_machine.config.logger.warn "#{klass.name}: overriding method '#{method_name}'!"
211
+ unless AASM::Configuration.hide_warnings
212
+ @state_machine.config.logger.warn "#{klass.name}: overriding method '#{method_name}'!"
213
+ end
212
214
  end
213
215
 
214
216
  klass.send(:define_method, method_name, method_definition)
@@ -34,5 +34,9 @@ module AASM
34
34
 
35
35
  # Configure a logger, with default being a Logger to STDERR
36
36
  attr_accessor :logger
37
+
38
+ class << self
39
+ attr_accessor :hide_warnings
40
+ end
37
41
  end
38
42
  end
@@ -22,10 +22,21 @@ module AASM::Core
22
22
  :before_transaction,
23
23
  :ensure,
24
24
  :error,
25
+ :before_success,
25
26
  :success,
26
27
  ], &block) if block
27
28
  end
28
29
 
30
+ # called internally by Ruby 1.9 after clone()
31
+ def initialize_copy(orig)
32
+ super
33
+ @transitions = @transitions.collect { |transition| transition.clone }
34
+ @guards = @guards.dup
35
+ @unless = @unless.dup
36
+ @options = {}
37
+ orig.options.each_pair { |name, setting| @options[name] = setting.is_a?(Hash) || setting.is_a?(Array) ? setting.dup : setting }
38
+ end
39
+
29
40
  # a neutered version of fire - it doesn't actually fire the event, it just
30
41
  # executes the transition guards to determine if a transition is even
31
42
  # an option given current conditions.
@@ -122,16 +133,19 @@ module AASM::Core
122
133
  args.unshift(to_state)
123
134
  to_state = nil
124
135
  end
136
+ else
137
+ args.unshift(nil) if args.nil? || args.empty? # If single arg given which is nil, push it back to args
125
138
  end
126
139
 
127
140
  transitions.each do |transition|
128
141
  next if to_state and !Array(transition.to).include?(to_state)
129
- if (options.key?(:may_fire) && Array(transition.to).include?(options[:may_fire])) ||
142
+ if (options.key?(:may_fire) && transition.eql?(options[:may_fire])) ||
130
143
  (!options.key?(:may_fire) && transition.allowed?(obj, *args))
131
- result = to_state || Array(transition.to).first
144
+
132
145
  if options[:test_only]
133
- # result = true
146
+ result = transition
134
147
  else
148
+ result = to_state || Array(transition.to).first
135
149
  Array(transition.to).each {|to| @valid_transitions[to] = transition }
136
150
  transition.execute(obj, *args)
137
151
  end
@@ -9,6 +9,13 @@ module AASM::Core
9
9
  update(options)
10
10
  end
11
11
 
12
+ # called internally by Ruby 1.9 after clone()
13
+ def initialize_copy(orig)
14
+ super
15
+ @options = {}
16
+ orig.options.each_pair { |name, setting| @options[name] = setting.is_a?(Hash) || setting.is_a?(Array) ? setting.dup : setting }
17
+ end
18
+
12
19
  def ==(state)
13
20
  if state.is_a? Symbol
14
21
  name == state
@@ -28,6 +28,15 @@ module AASM::Core
28
28
  @opts = opts
29
29
  end
30
30
 
31
+ # called internally by Ruby 1.9 after clone()
32
+ def initialize_copy(orig)
33
+ super
34
+ @guards = @guards.dup
35
+ @unless = @unless.dup
36
+ @opts = {}
37
+ orig.opts.each_pair { |name, setting| @opts[name] = setting.is_a?(Hash) || setting.is_a?(Array) ? setting.dup : setting }
38
+ end
39
+
31
40
  def allowed?(obj, *args)
32
41
  invoke_callbacks_compatible_with_guard(@guards, obj, args, :guard => true) &&
33
42
  invoke_callbacks_compatible_with_guard(@unless, obj, args, :unless => true)
@@ -12,9 +12,6 @@ module AASM
12
12
  elsif hierarchy.include?("Mongoid::Document")
13
13
  require_persistence :mongoid
14
14
  include_persistence base, :mongoid
15
- elsif hierarchy.include?("MongoMapper::Document")
16
- require_persistence :mongo_mapper
17
- include_persistence base, :mongo_mapper
18
15
  elsif hierarchy.include?("Sequel::Model")
19
16
  require_persistence :sequel
20
17
  include_persistence base, :sequel
@@ -103,7 +103,7 @@ module AASM
103
103
  private
104
104
 
105
105
  def aasm_update_column(name, value)
106
- self.class.where(self.class.primary_key => self.id).update_all(self.class.aasm(name).attribute_name => value) == 1
106
+ self.class.unscoped.where(self.class.primary_key => self.id).update_all(self.class.aasm(name).attribute_name => value) == 1
107
107
  end
108
108
 
109
109
  def aasm_rollback(name, old_value)
@@ -50,8 +50,7 @@ module AASM
50
50
 
51
51
  module InstanceMethods
52
52
 
53
- # Writes <tt>state</tt> to the state column and persists it to the database
54
- # using update_attribute (which bypasses validation)
53
+ # Writes <tt>state</tt> to the state field and persists it to the database
55
54
  #
56
55
  # foo = Foo.find(1)
57
56
  # foo.aasm.current_state # => :opened
@@ -62,17 +61,24 @@ module AASM
62
61
  # NOTE: intended to be called from an event
63
62
  def aasm_write_state(state, name=:default)
64
63
  old_value = read_attribute(self.class.aasm(name).attribute_name)
65
- write_attribute(self.class.aasm(name).attribute_name, state.to_s)
64
+ aasm_write_attribute(state, name)
66
65
 
67
- unless self.save(:validate => false)
68
- write_attribute(self.class.aasm(name).attribute_name, old_value)
69
- return false
66
+ success = if aasm_skipping_validations(name)
67
+ value = aasm_raw_attribute_value(state, name)
68
+ aasm_update_column(name, value)
69
+ else
70
+ self.save
71
+ end
72
+
73
+ unless success
74
+ aasm_rollback(name, old_value)
75
+ raise Mongoid::Errors::Validations.new(self) if aasm_whiny_persistence(name)
70
76
  end
71
77
 
72
- true
78
+ success
73
79
  end
74
80
 
75
- # Writes <tt>state</tt> to the state column, but does not persist it to the database
81
+ # Writes <tt>state</tt> to the state field, but does not persist it to the database
76
82
  #
77
83
  # foo = Foo.find(1)
78
84
  # foo.aasm.current_state # => :opened
@@ -85,11 +91,44 @@ module AASM
85
91
  #
86
92
  # NOTE: intended to be called from an event
87
93
  def aasm_write_state_without_persistence(state, name=:default)
88
- write_attribute(self.class.aasm(name).attribute_name, state.to_s)
94
+ aasm_write_attribute(state, name)
89
95
  end
90
96
 
91
97
  private
92
98
 
99
+ def aasm_update_column(name, value)
100
+ attribute_name = self.class.aasm(name).attribute_name
101
+
102
+ if Mongoid::VERSION.to_f >= 4
103
+ set(Hash[attribute_name, value])
104
+ else
105
+ set(attribute_name, value)
106
+ end
107
+
108
+ true
109
+ end
110
+
111
+ def aasm_rollback(name, old_value)
112
+ write_attribute(self.class.aasm(name).attribute_name, old_value)
113
+ false
114
+ end
115
+
116
+ def aasm_skipping_validations(state_machine_name)
117
+ AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.skip_validation_on_save
118
+ end
119
+
120
+ def aasm_whiny_persistence(state_machine_name)
121
+ AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.whiny_persistence
122
+ end
123
+
124
+ def aasm_write_attribute(state, name=:default)
125
+ write_attribute(self.class.aasm(name).attribute_name, aasm_raw_attribute_value(state, name))
126
+ end
127
+
128
+ def aasm_raw_attribute_value(state, _name=:default)
129
+ state.to_s
130
+ end
131
+
93
132
  # Ensures that if the aasm_state column is nil and the record is new
94
133
  # that the initial state gets populated before validation on create
95
134
  #
@@ -16,8 +16,10 @@ module AASM
16
16
  # called internally by Ruby 1.9 after clone()
17
17
  def initialize_copy(orig)
18
18
  super
19
- @states = @states.dup
20
- @events = @events.dup
19
+ @states = orig.states.collect { |state| state.clone }
20
+ @events = {}
21
+ orig.events.each_pair { |name, event| @events[name] = event.clone }
22
+ @global_callbacks = @global_callbacks.dup
21
23
  end
22
24
 
23
25
  def add_state(state_name, klass, options)
@@ -1,8 +1,11 @@
1
+ require 'concurrent'
1
2
  module AASM
2
3
  class StateMachineStore
4
+ @stores = Concurrent::Map.new
5
+
3
6
  class << self
4
7
  def stores
5
- @stores ||= {}
8
+ @stores
6
9
  end
7
10
 
8
11
  # do not overwrite existing state machines, which could have been created by
@@ -38,7 +41,7 @@ module AASM
38
41
  end
39
42
 
40
43
  def initialize
41
- @machines = {}
44
+ @machines = Concurrent::Map.new
42
45
  end
43
46
 
44
47
  def clone
@@ -1,3 +1,3 @@
1
1
  module AASM
2
- VERSION = "4.11.1"
2
+ VERSION = "4.12.0"
3
3
  end
@@ -13,7 +13,6 @@ exclude_files = [
13
13
  'aasm/rspec.*',
14
14
  'aasm/persistence/active_record_persistence.rb',
15
15
  'aasm/persistence/dynamoid_persistence.rb',
16
- 'aasm/persistence/mongo_mapper_persistence.rb',
17
16
  'aasm/persistence/mongoid_persistence.rb',
18
17
  'aasm/persistence/sequel_persistence.rb',
19
18
  'aasm/persistence/redis_persistence.rb'
@@ -1,44 +1,47 @@
1
1
  require 'spec_helper'
2
- require 'generator_spec'
3
- require 'generators/active_record/aasm_generator'
4
2
 
5
- describe ActiveRecord::Generators::AASMGenerator, type: :generator do
6
- destination File.expand_path("../../../tmp", __FILE__)
3
+ if defined?(ActiveRecord)
4
+ require 'generator_spec'
5
+ require 'generators/active_record/aasm_generator'
6
+
7
+ describe ActiveRecord::Generators::AASMGenerator, type: :generator do
8
+ destination File.expand_path("../../../tmp", __FILE__)
9
+
10
+ before(:all) do
11
+ prepare_destination
12
+ end
13
+
14
+ it "creates model with aasm block for default column_name" do
15
+ run_generator %w(user)
16
+ assert_file "app/models/user.rb", /include AASM\n\n aasm do\n end\n/
17
+ end
18
+
19
+ it "creates model with aasm block for custom column_name" do
20
+ run_generator %w(user state)
21
+ assert_file "app/models/user.rb", /aasm :column => 'state' do\n end\n/
22
+ end
23
+
24
+ it "creates model with aasm block for namespaced model" do
25
+ run_generator %w(Admin::User state)
26
+ assert_file "app/models/admin/user.rb", /aasm :column => 'state' do\n end\n/
27
+ end
28
+
29
+ it "creates migration for model with aasm_column" do
30
+ run_generator %w(post)
31
+ assert_migration "db/migrate/aasm_create_posts.rb", /create_table(:posts) do |t|\n t.string :aasm_state\n/
32
+ end
33
+
34
+ it "add aasm_column in existing model" do
35
+ run_generator %w(job)
36
+ assert_file "app/models/job.rb"
37
+ run_generator %w(job)
38
+ assert_migration "db/migrate/add_aasm_state_to_jobs.rb"
39
+ end
40
+
41
+ it "add custom aasm_column in existing model" do
42
+ run_generator %w(job state)
43
+ assert_migration "db/migrate/add_state_to_jobs.rb"
44
+ end
7
45
 
8
- before(:all) do
9
- prepare_destination
10
46
  end
11
-
12
- it "creates model with aasm block for default column_name" do
13
- run_generator %w(user)
14
- assert_file "app/models/user.rb", /include AASM\n\n aasm do\n end\n/
15
- end
16
-
17
- it "creates model with aasm block for custom column_name" do
18
- run_generator %w(user state)
19
- assert_file "app/models/user.rb", /aasm :column => 'state' do\n end\n/
20
- end
21
-
22
- it "creates model with aasm block for namespaced model" do
23
- run_generator %w(Admin::User state)
24
- assert_file "app/models/admin/user.rb", /aasm :column => 'state' do\n end\n/
25
- end
26
-
27
- it "creates migration for model with aasm_column" do
28
- run_generator %w(post)
29
- assert_migration "db/migrate/aasm_create_posts.rb", /create_table(:posts) do |t|\n t.string :aasm_state\n/
30
- end
31
-
32
- it "add aasm_column in existing model" do
33
- run_generator %w(job)
34
- assert_file "app/models/job.rb"
35
- run_generator %w(job)
36
- assert_migration "db/migrate/add_aasm_state_to_jobs.rb"
37
- end
38
-
39
- it "add custom aasm_column in existing model" do
40
- run_generator %w(job state)
41
- assert_migration "db/migrate/add_state_to_jobs.rb"
42
- end
43
-
44
47
  end
@@ -1,9 +1,8 @@
1
1
  require 'spec_helper'
2
- require 'generator_spec'
3
- require 'generators/mongoid/aasm_generator'
4
2
 
5
- begin
6
- require "mongoid"
3
+ if defined?(Mongoid::Document)
4
+ require 'generator_spec'
5
+ require 'generators/mongoid/aasm_generator'
7
6
 
8
7
  describe Mongoid::Generators::AASMGenerator, type: :generator do
9
8
  destination File.expand_path("../../../tmp", __FILE__)
@@ -28,6 +27,5 @@ begin
28
27
  end
29
28
 
30
29
  end
31
- rescue LoadError
32
- puts "Not running Mongoid specs because mongoid gem is not installed!!!"
30
+
33
31
  end