aasm 4.0.5 → 4.1.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +10 -11
  3. data/CHANGELOG.md +18 -1
  4. data/Gemfile +6 -2
  5. data/README.md +21 -4
  6. data/README_FROM_VERSION_3_TO_4.md +1 -1
  7. data/aasm.gemspec +5 -2
  8. data/gemfiles/rails_3.2.gemfile +3 -1
  9. data/gemfiles/rails_4.0.gemfile +2 -5
  10. data/gemfiles/rails_4.0_bson1.gemfile +14 -0
  11. data/gemfiles/rails_4.1.gemfile +2 -5
  12. data/gemfiles/rails_4.1_bson1.gemfile +14 -0
  13. data/gemfiles/rails_4.2.gemfile +13 -0
  14. data/gemfiles/rails_4.2_bson1.gemfile +14 -0
  15. data/lib/aasm/aasm.rb +1 -1
  16. data/lib/aasm/base.rb +7 -7
  17. data/lib/aasm/core/event.rb +10 -2
  18. data/lib/aasm/core/transition.rb +0 -2
  19. data/lib/aasm/instance_base.rb +1 -1
  20. data/lib/aasm/persistence/active_record_persistence.rb +4 -4
  21. data/lib/aasm/persistence/base.rb +4 -1
  22. data/lib/aasm/persistence/mongo_mapper_persistence.rb +174 -0
  23. data/lib/aasm/persistence/mongoid_persistence.rb +1 -5
  24. data/lib/aasm/persistence.rb +2 -0
  25. data/lib/aasm/version.rb +1 -1
  26. data/spec/database.rb +1 -1
  27. data/spec/models/callbacks/basic.rb +33 -20
  28. data/spec/models/callbacks/private_method.rb +44 -0
  29. data/spec/models/callbacks/with_args.rb +20 -4
  30. data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +10 -0
  31. data/spec/models/mongo_mapper/simple_mongo_mapper.rb +11 -0
  32. data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +12 -0
  33. data/spec/models/mongoid/simple_new_dsl_mongoid.rb +1 -1
  34. data/spec/models/persistence.rb +18 -0
  35. data/spec/spec_helper.rb +2 -1
  36. data/spec/unit/callbacks_spec.rb +68 -32
  37. data/spec/unit/complex_example_spec.rb +9 -9
  38. data/spec/unit/event_spec.rb +6 -6
  39. data/spec/unit/inspection_spec.rb +2 -2
  40. data/spec/unit/persistence/active_record_persistence_spec.rb +38 -39
  41. data/spec/unit/persistence/mongo_mapper_persistance_spec.rb +135 -0
  42. data/spec/unit/persistence/mongoid_persistance_spec.rb +4 -10
  43. data/spec/unit/persistence/sequel_persistence_spec.rb +2 -1
  44. data/spec/unit/subclassing_spec.rb +1 -1
  45. data/spec/unit/transition_spec.rb +10 -10
  46. metadata +21 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0fd3025d299701a391be90b67871d0110925522e
4
- data.tar.gz: d56adcb31986d0bc257e4c4a32e79462a6715709
3
+ metadata.gz: a07996d6cd3f0270a6ec9d010a904cebd462b6a7
4
+ data.tar.gz: 57e8efc5d284371e23c50d41771beb3172787bdd
5
5
  SHA512:
6
- metadata.gz: f2e8c7bd2e7ce8f614b8b72a48ffbf396c9194c5369a128bbe6d201443e067a48caea69b9f3f9ec4c6af30956b358deb4d8544172599fe7120bd4edbc4c9717d
7
- data.tar.gz: b6d9db8e0f1cd99e263d3cb9e16ff837adab1ce57faf918ca222f48a9bb674ebf29bbdae640d4987bd6f676d35896dec9d3060347e709229ad831160702b11a4
6
+ metadata.gz: 0e1f53507bcbcf9deecce7193a5fed9f37ae5fbc91b6325f127820ead35113e19bea7dba6f87af9482affac31462b05e1b6c89a87ef61a47ad93d95c07f19179
7
+ data.tar.gz: 9da9eea4b58047190c441dc42c120fed864c318828340cc61505601f70179736d9cc70a0f60166bae13c6fc6aa3f3c8c4569047915a0af72ab152f30ce5422fd
data/.travis.yml CHANGED
@@ -1,13 +1,16 @@
1
+ sudo: false
1
2
  language: ruby
3
+ cache: bundler
2
4
 
3
5
  rvm:
4
6
  # - 1.8.7
5
- - 1.9.2
7
+ # - 1.9.2
6
8
  - 1.9.3
7
9
  - 2.0.0
8
10
  - 2.1
11
+ - 2.2
9
12
  # - jruby-18mode # JRuby in 1.8 mode
10
- - jruby-19mode # JRuby in 1.9 mode
13
+ - jruby-1.7 # JRuby in 1.9 mode
11
14
  - rbx-2.2.1
12
15
 
13
16
  services: mongodb
@@ -15,21 +18,17 @@ services: mongodb
15
18
  gemfile:
16
19
  - gemfiles/rails_3.2.gemfile
17
20
  - gemfiles/rails_4.0.gemfile
21
+ - gemfiles/rails_4.0_bson1.gemfile
18
22
  - gemfiles/rails_4.1.gemfile
23
+ - gemfiles/rails_4.1_bson1.gemfile
24
+ - gemfiles/rails_4.2.gemfile
25
+ - gemfiles/rails_4.2_bson1.gemfile
19
26
 
20
27
  matrix:
21
28
  allow_failures:
22
29
  - rvm: rbx-2.2.1
23
- - rvm: jruby-19mode
30
+ - rvm: jruby-1.7
24
31
  exclude:
25
- - rvm: 1.8.7
26
- gemfile: gemfiles/rails_4.0.gemfile
27
- - rvm: 1.8.7
28
- gemfile: gemfiles/rails_4.1.gemfile
29
- - rvm: 1.9.2
30
- gemfile: gemfiles/rails_4.0.gemfile
31
- - rvm: 1.9.2
32
- gemfile: gemfiles/rails_4.1.gemfile
33
32
  - rvm: 1.9.3
34
33
  gemfile: gemfiles/rails_4.1.gemfile
35
34
  - rvm: jruby-19mode
data/CHANGELOG.md CHANGED
@@ -1,10 +1,27 @@
1
1
  # CHANGELOG
2
2
 
3
- ## 4.1.0 (not yet released)
3
+ ## 4.2.0 (not yet released)
4
4
 
5
+ ## 4.1.0
6
+
7
+ * bugfix: initialize the aasm state column after initialization of the _Mongoid_ instance (see [issue #206](https://github.com/aasm/aasm/issues/206), thanks to [@Shwetakale ](https://github.com/Shwetakale ))
8
+ * added support for mongomapper ORM (see [issue #203](https://github.com/aasm/aasm/issues/203), thanks to [@reidmorrison ](https://github.com/reidmorrison ))
5
9
  * `aasm_column` has been removed. Use `aasm.attribute_name` instead
6
10
  * `aasm_human_event_name` has been removed. Use `aasm.human_event_name` instead
7
11
 
12
+ ## 4.0.8
13
+
14
+ * bugfix: may_event_name? should return true or false only (see [issue #200](https://github.com/aasm/aasm/issues/200) for details)
15
+
16
+ ## 4.0.7
17
+
18
+ * bugfix: take private methods into account when checking for callbacks (see [issue #197](https://github.com/aasm/aasm/issues/197) for details)
19
+
20
+ ## 4.0.6
21
+
22
+ * bugfix: `false` is treated as uninitialised state (same as `nil`) (see [issue #195](https://github.com/aasm/aasm/issues/195) for details)
23
+ * bugfix: an event's `:error` callback now retrieves all arguments passed to the event (see [issue #196](https://github.com/aasm/aasm/issues/196) for details)
24
+
8
25
  ## 4.0.5
9
26
 
10
27
  * bugfix: initialize the aasm state column after initialization of the _ActiveRecord_ instance only if the attribute has been loaded (see [issue #193](https://github.com/aasm/aasm/issues/193) for details)
data/Gemfile CHANGED
@@ -1,12 +1,16 @@
1
1
  source "https://rubygems.org"
2
2
 
3
3
  gem "sqlite3", :platforms => :ruby
4
- gem "coveralls", :platforms => :ruby
4
+ gem "coveralls"
5
5
  gem 'rubysl', :platforms => :rbx
6
6
  gem "jruby-openssl", :platforms => :jruby
7
7
  gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
8
8
  gem "rails", "~>4.1"
9
- gem 'mongoid' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
9
+ gem 'mongoid', '~>4.0' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
10
10
  gem 'sequel'
11
+ # Since mongoid V4 requires incompatible bson V2, cannot have mongoid (V4 or greater)
12
+ # and mongo_mapper ( or mongo ) in the same application
13
+ #gem 'mongo_mapper', '~> 0.13'
14
+ #gem 'bson_ext', :platforms => :ruby
11
15
 
12
16
  gemspec
data/README.md CHANGED
@@ -9,8 +9,8 @@ This package contains AASM, a library for adding finite state machines to Ruby c
9
9
 
10
10
  AASM started as the *acts_as_state_machine* plugin but has evolved into a more generic library
11
11
  that no longer targets only ActiveRecord models. It currently provides adapters for
12
- [ActiveRecord](http://api.rubyonrails.org/classes/ActiveRecord/Base.html) and
13
- [Mongoid](http://mongoid.org/), but it can be used for any Ruby class, no matter what
12
+ [ActiveRecord](http://api.rubyonrails.org/classes/ActiveRecord/Base.html),
13
+ [Mongoid](http://mongoid.org/), and [Mongomapper](http://mongomapper.com/) but it can be used for any Ruby class, no matter what
14
14
  parent class it has (if any).
15
15
 
16
16
  ## Upgrade from version 3 to 4
@@ -435,7 +435,7 @@ to ```false```.
435
435
 
436
436
  ### Sequel
437
437
 
438
- AASM also supports [Sequel](http://sequel.jeremyevans.net/) besides _ActiveRecord_ and _Mongoid_.
438
+ AASM also supports [Sequel](http://sequel.jeremyevans.net/) besides _ActiveRecord_, _Mongoid_, and _MongoMapper_.
439
439
 
440
440
  ```ruby
441
441
  class Job < Sequel::Model
@@ -466,6 +466,23 @@ class Job
466
466
  end
467
467
  ```
468
468
 
469
+ ### MongoMapper
470
+
471
+ AASM also supports persistence to Mongodb if you're using MongoMapper. Make sure
472
+ to include MongoMapper::Document before you include AASM.
473
+
474
+ ```ruby
475
+ class Job
476
+ include MongoMapper::Document
477
+ include AASM
478
+
479
+ key :aasm_state, Symbol
480
+ aasm do
481
+ ...
482
+ end
483
+ end
484
+ ```
485
+
469
486
  ### Automatic Scopes
470
487
 
471
488
  AASM will automatically create scope methods for each state in the model.
@@ -671,7 +688,7 @@ purpose.
671
688
 
672
689
  ## License ##
673
690
 
674
- Copyright (c) 2006-2014 Scott Barron
691
+ Copyright (c) 2006-2015 Scott Barron
675
692
 
676
693
  Permission is hereby granted, free of charge, to any person obtaining
677
694
  a copy of this software and associated documentation files (the
@@ -139,7 +139,7 @@ to
139
139
  ```ruby
140
140
  job = Job.new
141
141
 
142
- job.aasm.events(:permitted => true)
142
+ job.aasm.events(:permitted => true).map(&:name)
143
143
  # => [:run]
144
144
  ```
145
145
 
data/aasm.gemspec CHANGED
@@ -9,13 +9,16 @@ Gem::Specification.new do |s|
9
9
  s.email = %q{scott@elitists.net, ttilley@gmail.com, aasm@mt7.de}
10
10
  s.homepage = %q{https://github.com/aasm/aasm}
11
11
  s.summary = %q{State machine mixin for Ruby objects}
12
- s.description = %q{AASM is a continuation of the acts as state machine rails plugin, built for plain Ruby objects.}
12
+ s.description = %q{AASM is a continuation of the acts-as-state-machine rails plugin, built for plain Ruby objects.}
13
13
  s.date = Time.now
14
14
  s.licenses = ["MIT"]
15
15
 
16
+ s.platform = Gem::Platform::RUBY
17
+ s.required_ruby_version = '>= 1.9.3'
18
+
16
19
  s.add_development_dependency 'rake'
17
20
  s.add_development_dependency 'sdoc'
18
- s.add_development_dependency 'rspec', '>= 2.14', '< 2.99'
21
+ s.add_development_dependency 'rspec'
19
22
 
20
23
  # debugging
21
24
  # s.add_development_dependency 'debugger'
@@ -7,7 +7,9 @@ gem 'rubinius-developer_tools', :platforms => :rbx
7
7
  gem "jruby-openssl", :platforms => :jruby
8
8
  gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
9
9
  gem "rails", "3.2.21"
10
- gem 'mongoid' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
10
+ gem 'mongoid', '~>3.1' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
11
11
  gem 'sequel'
12
+ gem 'mongo_mapper', '~>0.13'
13
+ gem 'bson_ext', :platforms => :ruby
12
14
 
13
15
  gemspec :path => "../"
@@ -6,11 +6,8 @@ gem 'rubysl', :platforms => :rbx
6
6
  gem 'rubinius-developer_tools', :platforms => :rbx
7
7
  gem "jruby-openssl", :platforms => :jruby
8
8
  gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
9
- gem "rails", "4.0.12"
10
-
11
- # mongoid is not yet compatible with Rails >= 4
12
- # gem 'mongoid' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
13
-
9
+ gem "rails", "4.0.13"
10
+ gem 'mongoid', '~>4.0' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
14
11
  gem 'sequel'
15
12
 
16
13
  gemspec :path => "../"
@@ -0,0 +1,14 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "sqlite3", :platforms => :ruby
4
+ gem "coveralls"
5
+ gem 'rubysl', :platforms => :rbx
6
+ gem 'rubinius-developer_tools', :platforms => :rbx
7
+ gem "jruby-openssl", :platforms => :jruby
8
+ gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
9
+ gem "rails", "4.0.13"
10
+ gem 'sequel'
11
+ gem 'mongo_mapper', '~>0.13'
12
+ gem 'bson_ext', :platforms => :ruby
13
+
14
+ gemspec :path => "../"
@@ -6,11 +6,8 @@ gem 'rubysl', :platforms => :rbx
6
6
  gem 'rubinius-developer_tools', :platforms => :rbx
7
7
  gem "jruby-openssl", :platforms => :jruby
8
8
  gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
9
- gem "rails", "4.1.8"
10
-
11
- # mongoid is not yet compatible with Rails >= 4
12
- # gem 'mongoid' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
13
-
9
+ gem "rails", "4.1.9"
10
+ gem 'mongoid', '~>4.0' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
14
11
  gem 'sequel'
15
12
 
16
13
  gemspec :path => "../"
@@ -0,0 +1,14 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "sqlite3", :platforms => :ruby
4
+ gem "coveralls"
5
+ gem 'rubysl', :platforms => :rbx
6
+ gem 'rubinius-developer_tools', :platforms => :rbx
7
+ gem "jruby-openssl", :platforms => :jruby
8
+ gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
9
+ gem "rails", "4.1.9"
10
+ gem 'sequel'
11
+ gem 'mongo_mapper', '~> 0.13'
12
+ gem 'bson_ext', :platforms => :ruby
13
+
14
+ gemspec :path => "../"
@@ -0,0 +1,13 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "sqlite3", :platforms => :ruby
4
+ gem "coveralls"
5
+ gem 'rubysl', :platforms => :rbx
6
+ gem 'rubinius-developer_tools', :platforms => :rbx
7
+ gem "jruby-openssl", :platforms => :jruby
8
+ gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
9
+ gem "rails", "4.2.0"
10
+ gem 'mongoid', '~>4.0' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
11
+ gem 'sequel'
12
+
13
+ gemspec :path => "../"
@@ -0,0 +1,14 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "sqlite3", :platforms => :ruby
4
+ gem "coveralls"
5
+ gem 'rubysl', :platforms => :rbx
6
+ gem 'rubinius-developer_tools', :platforms => :rbx
7
+ gem "jruby-openssl", :platforms => :jruby
8
+ gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
9
+ gem "rails", "4.2.0"
10
+ gem 'sequel'
11
+ gem 'mongo_mapper'
12
+ gem 'bson_ext', :platforms => :ruby
13
+
14
+ gemspec :path => "../"
data/lib/aasm/aasm.rb CHANGED
@@ -81,7 +81,7 @@ private
81
81
  aasm_failed(event_name, old_state)
82
82
  end
83
83
  rescue StandardError => e
84
- event.fire_callbacks(:error, self, e) || raise(e)
84
+ event.fire_callbacks(:error, self, e, *process_args(event, aasm.current_state, *args)) || raise(e)
85
85
  end
86
86
  end
87
87
 
data/lib/aasm/base.rb CHANGED
@@ -56,12 +56,12 @@ module AASM
56
56
  def state(name, options={})
57
57
  @state_machine.add_state(name, @klass, options)
58
58
 
59
- @klass.send(:define_method, "#{name.to_s}?") do
59
+ @klass.send(:define_method, "#{name}?") do
60
60
  aasm.current_state == name
61
61
  end
62
62
 
63
- unless @klass.const_defined?("STATE_#{name.to_s.upcase}")
64
- @klass.const_set("STATE_#{name.to_s.upcase}", name)
63
+ unless @klass.const_defined?("STATE_#{name.upcase}")
64
+ @klass.const_set("STATE_#{name.upcase}", name)
65
65
  end
66
66
  end
67
67
 
@@ -72,16 +72,16 @@ module AASM
72
72
  # an addition over standard aasm so that, before firing an event, you can ask
73
73
  # may_event? and get back a boolean that tells you whether the guard method
74
74
  # on the transition will let this happen.
75
- @klass.send(:define_method, "may_#{name.to_s}?") do |*args|
75
+ @klass.send(:define_method, "may_#{name}?") do |*args|
76
76
  aasm.may_fire_event?(name, *args)
77
77
  end
78
78
 
79
- @klass.send(:define_method, "#{name.to_s}!") do |*args, &block|
80
- aasm.current_event = "#{name.to_s}!".to_sym
79
+ @klass.send(:define_method, "#{name}!") do |*args, &block|
80
+ aasm.current_event = "#{name}!".to_sym
81
81
  aasm_fire_event(name, {:persist => true}, *args, &block)
82
82
  end
83
83
 
84
- @klass.send(:define_method, "#{name.to_s}") do |*args, &block|
84
+ @klass.send(:define_method, "#{name}") do |*args, &block|
85
85
  aasm.current_event = name.to_sym
86
86
  aasm_fire_event(name, {:persist => false}, *args, &block)
87
87
  end
@@ -124,14 +124,22 @@ module AASM::Core
124
124
  def invoke_callbacks(code, record, args)
125
125
  case code
126
126
  when Symbol, String
127
- record.send(code, *args)
127
+ unless record.respond_to?(code, true)
128
+ raise NoMethodError.new("NoMethodError: undefined method `#{code}' for #{record.inspect}:#{record.class}")
129
+ end
130
+ arity = record.send(:method, code.to_sym).arity
131
+ record.send(code, *(arity < 0 ? args : args[0...arity]))
128
132
  true
133
+
129
134
  when Proc
130
- record.instance_exec(*args, &code)
135
+ arity = code.arity
136
+ record.instance_exec(*(arity < 0 ? args : args[0...arity]), &code)
131
137
  true
138
+
132
139
  when Array
133
140
  code.each {|a| invoke_callbacks(a, record, args)}
134
141
  true
142
+
135
143
  else
136
144
  false
137
145
  end
@@ -50,11 +50,9 @@ module AASM::Core
50
50
 
51
51
  case code
52
52
  when Symbol, String
53
- # QUESTION : record.send(code, *args) ?
54
53
  arity = record.send(:method, code.to_sym).arity
55
54
  arity == 0 ? record.send(code) : record.send(code, *args)
56
55
  when Proc
57
- # QUESTION : record.instance_exec(*args, &code) ?
58
56
  code.arity == 0 ? record.instance_exec(&code) : record.instance_exec(*args, &code)
59
57
  when Array
60
58
  if options[:guard]
@@ -76,7 +76,7 @@ module AASM
76
76
 
77
77
  def may_fire_event?(name, *args)
78
78
  if event = @instance.class.aasm.state_machine.events[name]
79
- event.may_fire?(@instance, *args)
79
+ !!event.may_fire?(@instance, *args)
80
80
  else
81
81
  false # unknown event
82
82
  end
@@ -10,7 +10,7 @@ module AASM
10
10
  #
11
11
  # Adds
12
12
  #
13
- # before_validation :aasm_ensure_initial_state, :on => :create
13
+ # after_initialize :aasm_ensure_initial_state
14
14
  #
15
15
  # As a result, it doesn't matter when you define your methods - the following 2 are equivalent
16
16
  #
@@ -142,9 +142,9 @@ module AASM
142
142
 
143
143
  def aasm_raw_attribute_value(state)
144
144
  if aasm_enum
145
- value = self.class.send(aasm_enum)[state]
145
+ self.class.send(aasm_enum)[state]
146
146
  else
147
- value = state.to_s
147
+ state.to_s
148
148
  end
149
149
  end
150
150
 
@@ -166,7 +166,7 @@ module AASM
166
166
  def aasm_ensure_initial_state
167
167
  # checking via respond_to? does not work in Rails <= 3
168
168
  # if respond_to?(self.class.aasm.attribute_name) && send(self.class.aasm.attribute_name).blank? # Rails 4
169
- if attributes.key?(self.class.aasm.attribute_name.to_s) && send(self.class.aasm.attribute_name).blank?
169
+ if attribute_names.include?(self.class.aasm.attribute_name.to_s) && send(self.class.aasm.attribute_name).blank?
170
170
  aasm.enter_initial_state
171
171
  end
172
172
  end
@@ -37,7 +37,7 @@ module AASM
37
37
  if new_record?
38
38
  state.blank? ? aasm.determine_state_name(self.class.aasm.initial_state) : state.to_sym
39
39
  else
40
- state.nil? ? nil : state.to_sym
40
+ state.blank? ? nil : state.to_sym
41
41
  end
42
42
  end
43
43
 
@@ -71,6 +71,9 @@ module AASM
71
71
  elsif @klass.ancestors.map {|klass| klass.to_s}.include?("Mongoid::Document")
72
72
  scope_options = lambda { @klass.send(:where, {@klass.aasm.attribute_name.to_sym => name.to_s}) }
73
73
  @klass.send(:scope, name, scope_options)
74
+ elsif @klass.ancestors.map {|klass| klass.to_s}.include?("MongoMapper::Document")
75
+ conditions = { @klass.aasm.attribute_name.to_sym => name.to_s }
76
+ @klass.scope(name, lambda { @klass.where(conditions) })
74
77
  end
75
78
  end
76
79
  end
@@ -0,0 +1,174 @@
1
+ require_relative 'base'
2
+
3
+ module AASM
4
+ module Persistence
5
+ module MongoMapperPersistence
6
+ # This method:
7
+ #
8
+ # * extends the model with ClassMethods
9
+ # * includes InstanceMethods
10
+ #
11
+ # Adds
12
+ #
13
+ # before_validation :aasm_ensure_initial_state, :on => :create
14
+ #
15
+ # As a result, it doesn't matter when you define your methods - the following 2 are equivalent
16
+ #
17
+ # class Foo
18
+ # include MongoMapper::Document
19
+ # def aasm_write_state(state)
20
+ # "bar"
21
+ # end
22
+ # include AASM
23
+ # end
24
+ #
25
+ # class Foo < ActiveRecord::Base
26
+ # include MongoMapper::Document
27
+ # include AASM
28
+ # def aasm_write_state(state)
29
+ # "bar"
30
+ # end
31
+ # end
32
+ #
33
+ def self.included(base)
34
+ base.send(:include, AASM::Persistence::Base)
35
+ base.extend AASM::Persistence::MongoMapperPersistence::ClassMethods
36
+ base.send(:include, AASM::Persistence::MongoMapperPersistence::InstanceMethods)
37
+
38
+ base.before_create :aasm_ensure_initial_state
39
+
40
+ # ensure state is in the list of states
41
+ base.validate :aasm_validate_states
42
+ end
43
+
44
+ module ClassMethods
45
+
46
+ def find_in_state(number, state, *args)
47
+ with_state_scope(state).find!(number, *args)
48
+ end
49
+
50
+ def count_in_state(state, *args)
51
+ with_state_scope(state).count(*args)
52
+ end
53
+
54
+ def calculate_in_state(state, *args)
55
+ with_state_scope(state).calculate(*args)
56
+ end
57
+
58
+ protected
59
+ def with_state_scope(state)
60
+ where(aasm.attribute_name.to_sym => state.to_s)
61
+ end
62
+ end
63
+
64
+ module InstanceMethods
65
+
66
+ # Writes <tt>state</tt> to the state column and persists it to the database
67
+ #
68
+ # foo = Foo.find(1)
69
+ # foo.aasm.current_state # => :opened
70
+ # foo.close!
71
+ # foo.aasm.current_state # => :closed
72
+ # Foo.find(1).aasm.current_state # => :closed
73
+ #
74
+ # NOTE: intended to be called from an event
75
+ def aasm_write_state(state)
76
+ old_value = read_attribute(self.class.aasm.attribute_name)
77
+ aasm_write_attribute state
78
+
79
+ success = if aasm_skipping_validations
80
+ value = aasm_raw_attribute_value state
81
+ self.class.where(self.class.primary_key => self.id).update_all(self.class.aasm.attribute_name => value) == 1
82
+ else
83
+ self.save
84
+ end
85
+ unless success
86
+ write_attribute(self.class.aasm.attribute_name, old_value)
87
+ return false
88
+ end
89
+
90
+ true
91
+ end
92
+
93
+ # Writes <tt>state</tt> to the state column, but does not persist it to the database
94
+ #
95
+ # foo = Foo.find(1)
96
+ # foo.aasm.current_state # => :opened
97
+ # foo.close
98
+ # foo.aasm.current_state # => :closed
99
+ # Foo.find(1).aasm.current_state # => :opened
100
+ # foo.save
101
+ # foo.aasm.current_state # => :closed
102
+ # Foo.find(1).aasm.current_state # => :closed
103
+ #
104
+ # NOTE: intended to be called from an event
105
+ def aasm_write_state_without_persistence(state)
106
+ aasm_write_attribute state
107
+ end
108
+
109
+ private
110
+ def aasm_enum
111
+ case AASM::StateMachine[self.class].config.enum
112
+ when false then nil
113
+ when true then aasm_guess_enum_method
114
+ when nil then aasm_guess_enum_method if aasm_column_looks_like_enum
115
+ else AASM::StateMachine[self.class].config.enum
116
+ end
117
+ end
118
+
119
+ def aasm_column_looks_like_enum
120
+ self.class.keys[self.class.aasm.attribute_name.to_s].type == Integer
121
+ end
122
+
123
+ def aasm_guess_enum_method
124
+ self.class.aasm.attribute_name.to_s.pluralize.to_sym
125
+ end
126
+
127
+ def aasm_skipping_validations
128
+ AASM::StateMachine[self.class].config.skip_validation_on_save
129
+ end
130
+
131
+ def aasm_write_attribute(state)
132
+ write_attribute self.class.aasm.attribute_name, aasm_raw_attribute_value(state)
133
+ end
134
+
135
+ def aasm_raw_attribute_value(state)
136
+ if aasm_enum
137
+ self.class.send(aasm_enum)[state]
138
+ else
139
+ state.to_s
140
+ end
141
+ end
142
+
143
+ # Ensures that if the aasm_state column is nil and the record is new
144
+ # that the initial state gets populated before validation on create
145
+ #
146
+ # foo = Foo.new
147
+ # foo.aasm_state # => nil
148
+ # foo.valid?
149
+ # foo.aasm_state # => "open" (where :open is the initial state)
150
+ #
151
+ #
152
+ # foo = Foo.find(:first)
153
+ # foo.aasm_state # => 1
154
+ # foo.aasm_state = nil
155
+ # foo.valid?
156
+ # foo.aasm_state # => nil
157
+ #
158
+ def aasm_ensure_initial_state
159
+ return send("#{self.class.aasm.attribute_name}=", aasm.enter_initial_state.to_s) if send(self.class.aasm.attribute_name).blank?
160
+ end
161
+
162
+ def aasm_validate_states
163
+ send("#{self.class.aasm.attribute_name}=", aasm.enter_initial_state.to_s) if send(self.class.aasm.attribute_name).blank?
164
+ unless AASM::StateMachine[self.class].config.skip_validation_on_save
165
+ if aasm.current_state && !aasm.states.include?(aasm.current_state)
166
+ self.errors.add(AASM::StateMachine[self.class].config.column , "is invalid")
167
+ end
168
+ end
169
+ end
170
+ end # InstanceMethods
171
+
172
+ end
173
+ end # Persistence
174
+ end # AASM
@@ -35,11 +35,7 @@ module AASM
35
35
  base.extend AASM::Persistence::MongoidPersistence::ClassMethods
36
36
  base.send(:include, AASM::Persistence::MongoidPersistence::InstanceMethods)
37
37
 
38
- # Mongoid's Validatable gem dependency goes not have a before_validation_on_xxx hook yet.
39
- # base.before_validation_on_create :aasm_ensure_initial_state
40
- base.before_validation :aasm_ensure_initial_state
41
- # ensure initial aasm state even when validations are skipped
42
- base.before_create :aasm_ensure_initial_state
38
+ base.after_initialize :aasm_ensure_initial_state
43
39
  end
44
40
 
45
41
  module ClassMethods
@@ -10,6 +10,8 @@ module AASM
10
10
  include_persistence base, :active_record
11
11
  elsif hierarchy.include?("Mongoid::Document")
12
12
  include_persistence base, :mongoid
13
+ elsif hierarchy.include?("MongoMapper::Document")
14
+ include_persistence base, :mongo_mapper
13
15
  elsif hierarchy.include?("Sequel::Model")
14
16
  include_persistence base, :sequel
15
17
  else
data/lib/aasm/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module AASM
2
- VERSION = "4.0.5"
2
+ VERSION = "4.1.0"
3
3
  end
data/spec/database.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  ActiveRecord::Migration.suppress_messages do
2
- %w{gates readers writers transients simples no_scopes no_direct_assignments thieves localizer_test_models persisted_states provided_and_persisted_states with_enums with_true_enums with_false_enums}.each do |table_name|
2
+ %w{gates readers writers transients simples no_scopes no_direct_assignments thieves localizer_test_models persisted_states provided_and_persisted_states with_enums with_true_enums with_false_enums false_states}.each do |table_name|
3
3
  ActiveRecord::Migration.create_table table_name, :force => true do |t|
4
4
  t.string "aasm_state"
5
5
  end