aasm 4.11.1 → 4.12.0

Sign up to get free protection for your applications and to get access to all the features.
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