state_machines-mongoid 0.0.0 → 0.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.travis.yml +20 -0
  4. data/Appraisals +8 -0
  5. data/Gemfile +0 -2
  6. data/LICENSE.txt +1 -0
  7. data/README.md +43 -4
  8. data/Rakefile +8 -1
  9. data/gemfiles/active_model_4.1.gemfile +7 -0
  10. data/gemfiles/active_model_4.2.gemfile +7 -0
  11. data/lib/state_machines-mongoid.rb +1 -0
  12. data/lib/state_machines/integrations/mongoid.rb +401 -0
  13. data/lib/state_machines/integrations/mongoid/locale.rb +11 -0
  14. data/lib/state_machines/integrations/mongoid/version.rb +7 -0
  15. data/lib/state_machines/state_machines-mongoid.rb +1 -0
  16. data/state_machines-mongoid.gemspec +18 -12
  17. data/test/files/en.yml +5 -0
  18. data/test/integration_test.rb +23 -0
  19. data/test/machine_by_default_test.rb +16 -0
  20. data/test/machine_errors_test.rb +20 -0
  21. data/test/machine_multiple_test.rb +17 -0
  22. data/test/machine_nested_action_test.rb +39 -0
  23. data/test/machine_with_aliased_attribute_test.rb +22 -0
  24. data/test/machine_with_aliased_field_test.rb +22 -0
  25. data/test/machine_with_callbacks_test.rb +167 -0
  26. data/test/machine_with_column_state_attribute_test.rb +44 -0
  27. data/test/machine_with_complex_pluralization_scopes_test.rb +16 -0
  28. data/test/machine_with_conflicting_predicate_test.rb +27 -0
  29. data/test/machine_with_conflicting_state_name_test.rb +29 -0
  30. data/test/machine_with_custom_attribute_test.rb +21 -0
  31. data/test/machine_with_default_scope_test.rb +14 -0
  32. data/test/machine_with_different_column_default_test.rb +26 -0
  33. data/test/machine_with_different_integer_column_default_test.rb +27 -0
  34. data/test/machine_with_different_same_default_test.rb +26 -0
  35. data/test/machine_with_dirty_attribute_and_custom_attributes_during_loopback_test.rb +24 -0
  36. data/test/machine_with_dirty_attribute_and_state_events_test.rb +20 -0
  37. data/test/machine_with_dirty_attributes_and_custom_attribute_test.rb +32 -0
  38. data/test/machine_with_dirty_attributes_during_loopback_test.rb +22 -0
  39. data/test/machine_with_dirty_attributes_test.rb +35 -0
  40. data/test/machine_with_dynamic_initial_state_test.rb +99 -0
  41. data/test/machine_with_event_attributes_on_autosave_test.rb +50 -0
  42. data/test/machine_with_event_attributes_on_custom_action_test.rb +40 -0
  43. data/test/machine_with_event_attributes_on_save_bang_test.rb +82 -0
  44. data/test/machine_with_event_attributes_on_save_test.rb +146 -0
  45. data/test/machine_with_event_attributes_on_validation_test.rb +115 -0
  46. data/test/machine_with_events_test.rb +13 -0
  47. data/test/machine_with_failed_action_test.rb +39 -0
  48. data/test/machine_with_failed_after_callbacks_test.rb +35 -0
  49. data/test/machine_with_failed_before_callbacks_test.rb +36 -0
  50. data/test/machine_with_field_test.rb +16 -0
  51. data/test/machine_with_initialized_state_test.rb +35 -0
  52. data/test/machine_with_internationalization_test.rb +175 -0
  53. data/test/machine_with_loopback_test.rb +26 -0
  54. data/test/machine_with_non_column_state_attribute_defined_test.rb +35 -0
  55. data/test/machine_with_non_column_state_attribute_undefined_test.rb +33 -0
  56. data/test/machine_with_scopes_and_owner_subclass_test.rb +42 -0
  57. data/test/machine_with_scopes_test.rb +69 -0
  58. data/test/machine_with_state_driven_validations_test.rb +33 -0
  59. data/test/machine_with_state_test.rb +13 -0
  60. data/test/machine_with_static_initial_state_test.rb +162 -0
  61. data/test/machine_with_validations_and_custom_attribute_test.rb +21 -0
  62. data/test/machine_with_validations_test.rb +46 -0
  63. data/test/machine_without_field_test.rb +14 -0
  64. data/test/test_helper.rb +90 -0
  65. metadata +170 -9
  66. data/lib/state_machines/mongoid.rb +0 -7
  67. data/lib/state_machines/mongoid/version.rb +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b01953c4a574ca151ab27f9a4735b2a4f18a2c3a
4
- data.tar.gz: b3ab34e806e69375051f8029b092240167bec4f9
3
+ metadata.gz: fedf8d86a63fa90c27d8b53ec06b1931a546eef6
4
+ data.tar.gz: ef44e18220ddea7b4ce97150c9c6a4bce212a07d
5
5
  SHA512:
6
- metadata.gz: c1ce30e55acd3d37f9143b71e1f768867e057836e767ad0b56fd26e6972a20811104322e76328125edb8e046fb7e671a25f8c68d15eb845ee4d176514c2ee63f
7
- data.tar.gz: f720b7bfa5b6360ad8e042e52a147761ff55e331d4f45e0b7db7e7cec836b1f0bd78ecd13bf1873060817b6fc699959bb16a24a5c26031ad267b4f05712ca2fc
6
+ metadata.gz: d032c0f6a64c07d57890d1a41aa3b050d4321805b859cec4f04d0b57014b4e1fef1765f7559519ed8c7b1eabb825f5f823aa4907f01b2a7e5b5d4a414121e224
7
+ data.tar.gz: 809fb648a49257d2340498ac8dd7eb43ba292ba1075450326c6365ea33a71cc4cf8cf06fdd93d82e1006284e41f040c046ed2682e15f133301496e830085f6f7
data/.gitignore CHANGED
@@ -3,7 +3,7 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
- Gemfile.lock
6
+ *.lock
7
7
  InstalledFiles
8
8
  _yardoc
9
9
  coverage
@@ -20,3 +20,4 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
+ .idea
data/.travis.yml ADDED
@@ -0,0 +1,20 @@
1
+ language: ruby
2
+ sudo: false
3
+ cache: bundler
4
+
5
+ services:
6
+ - mongodb
7
+ rvm:
8
+ - 2.2
9
+ - 2.1
10
+ - 1.9.3
11
+ - jruby-19mode
12
+ - rbx-2
13
+
14
+ gemfile:
15
+ - gemfiles/active_model_4.1.gemfile
16
+ - gemfiles/active_model_4.2.gemfile
17
+
18
+ matrix:
19
+ allow_failures:
20
+ - rvm: ruby-head
data/Appraisals ADDED
@@ -0,0 +1,8 @@
1
+ # ActiveModel integrations
2
+ appraise 'active_model_4.1' do
3
+ gem 'activemodel', github: 'rails/rails', branch: '4-1-stable'
4
+ end
5
+
6
+ appraise 'active_model_4.2' do
7
+ gem 'activemodel', github: 'rails/rails', branch: '4-2-stable'
8
+ end
data/Gemfile CHANGED
@@ -1,4 +1,2 @@
1
1
  source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in state_machines-mongoid.gemspec
4
2
  gemspec
data/LICENSE.txt CHANGED
@@ -1,3 +1,4 @@
1
+ Copyright (c) 2006-2012 Aaron Pfeifer
1
2
  Copyright (c) 2014 Abdelkader Boudih
2
3
 
3
4
  MIT License
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
- # StateMachines::Mongoid
1
+ [![Build Status](https://travis-ci.org/state-machines/state_machines-mongoid.svg?branch=master)](https://travis-ci.org/state-machines/state_machines-mongoid)
2
+ [![Code Climate](https://codeclimate.com/github/state-machines/state_machines-mongoid.png)](https://codeclimate.com/github/state-machines/state_machines-mongoid)
2
3
 
3
- TODO: Write a gem description
4
+ # StateMachines Mongoid Integration
5
+
6
+ The Mongoid integration adds support for automatically
7
+ saving the record, named scopes, validation errors.
4
8
 
5
9
  ## Installation
6
10
 
@@ -18,12 +22,47 @@ Or install it yourself as:
18
22
 
19
23
  ## Usage
20
24
 
21
- TODO: Write usage instructions here
25
+ ```ruby
26
+ class Vehicle
27
+ include Mongoid::Document
28
+
29
+ state_machine :initial => :parked do
30
+ before_transition :parked => any - :parked, :do => :put_on_seatbelt
31
+ after_transition any => :parked do |vehicle, transition|
32
+ vehicle.seatbelt_on = 'off'
33
+ end
34
+ around_transition :benchmark
35
+
36
+ event :ignite do
37
+ transition :parked => :idling
38
+ end
39
+
40
+ state :first_gear, :second_gear do
41
+ validates_presence_of :seatbelt_on
42
+ end
43
+ end
44
+
45
+ def put_on_seatbelt
46
+ self.seatbelt_on = 'on'
47
+ end
48
+
49
+ def benchmark
50
+ #...
51
+ yield
52
+ #...
53
+ end
54
+ end
55
+ ```
56
+
57
+ Dependencies
58
+
59
+ Mongoid 4.0+
22
60
 
23
61
  ## Contributing
24
62
 
25
- 1. Fork it ( https://github.com/[my-github-username]/state_machines-mongoid/fork )
63
+ 1. Fork it ( https://github.com/state-machines/state_machines-mongoid/fork )
26
64
  2. Create your feature branch (`git checkout -b my-new-feature`)
27
65
  3. Commit your changes (`git commit -am 'Add some feature'`)
28
66
  4. Push to the branch (`git push origin my-new-feature`)
29
67
  5. Create a new Pull Request
68
+
data/Rakefile CHANGED
@@ -1,2 +1,9 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
2
3
 
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = 'test/*_test.rb'
6
+ end
7
+
8
+ desc 'Default: run all tests.'
9
+ task :default => :test
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activemodel", :github => "rails/rails", :branch => "4-1-stable"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activemodel", :github => "rails/rails", :branch => "4-2-stable"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1 @@
1
+ require 'state_machines/integrations/mongoid'
@@ -0,0 +1,401 @@
1
+ require 'state_machines'
2
+ require 'state_machines-activemodel'
3
+ require 'mongoid'
4
+
5
+ module StateMachines
6
+ module Integrations #:nodoc:
7
+ # Adds support for integrating state machines with Mongoid models.
8
+ #
9
+ # == Examples
10
+ #
11
+ # Below is an example of a simple state machine defined within a
12
+ # Mongoid model:
13
+ #
14
+ # class Vehicle
15
+ # include Mongoid::Document
16
+ #
17
+ # state_machine :initial => :parked do
18
+ # event :ignite do
19
+ # transition :parked => :idling
20
+ # end
21
+ # end
22
+ # end
23
+ #
24
+ # The examples in the sections below will use the above class as a
25
+ # reference.
26
+ #
27
+ # == Actions
28
+ #
29
+ # By default, the action that will be invoked when a state is transitioned
30
+ # is the +save+ action. This will cause the record to save the changes
31
+ # made to the state machine's attribute. *Note* that if any other changes
32
+ # were made to the record prior to transition, then those changes will
33
+ # be saved as well.
34
+ #
35
+ # For example,
36
+ #
37
+ # vehicle = Vehicle.create # => #<Vehicle _id: 4d70e028b876bb54d9000003, name: nil, state: "parked">
38
+ # vehicle.name = 'Ford Explorer'
39
+ # vehicle.ignite # => true
40
+ # vehicle.reload # => #<Vehicle _id: 4d70e028b876bb54d9000003, name: "Ford Explorer", state: "idling">
41
+ #
42
+ # == Events
43
+ #
44
+ # As described in StateMachines::InstanceMethods#state_machine, event
45
+ # attributes are created for every machine that allow transitions to be
46
+ # performed automatically when the object's action (in this case, :save)
47
+ # is called.
48
+ #
49
+ # In Mongoid, these automated events are run in the following order:
50
+ # * before validation - Run before callbacks and persist new states, then validate
51
+ # * before save - If validation was skipped, run before callbacks and persist new states, then save
52
+ # * after save - Run after callbacks
53
+ #
54
+ # For example,
55
+ #
56
+ # vehicle = Vehicle.create # => #<Vehicle _id: 4d70e028b876bb54d9000003, name: nil, state: "parked">
57
+ # vehicle.state_event # => nil
58
+ # vehicle.state_event = 'invalid'
59
+ # vehicle.valid? # => false
60
+ # vehicle.errors.full_messages # => ["State event is invalid"]
61
+ #
62
+ # vehicle.state_event = 'ignite'
63
+ # vehicle.valid? # => true
64
+ # vehicle.save # => true
65
+ # vehicle.state # => "idling"
66
+ # vehicle.state_event # => nil
67
+ #
68
+ # Note that this can also be done on a mass-assignment basis:
69
+ #
70
+ # vehicle = Vehicle.create(:state_event => 'ignite') # => #<Vehicle _id: 4d70e028b876bb54d9000003, name: nil, state: "idling">
71
+ # vehicle.state # => "idling"
72
+ #
73
+ # This technique is always used for transitioning states when the +save+
74
+ # action (which is the default) is configured for the machine.
75
+ #
76
+ # === Security implications
77
+ #
78
+ # Beware that public event attributes mean that events can be fired
79
+ # whenever mass-assignment is being used. If you want to prevent malicious
80
+ # users from tampering with events through URLs / forms, the attribute
81
+ # should be protected like so:
82
+ #
83
+ # class Vehicle
84
+ # include Mongoid::Document
85
+ #
86
+ # attr_protected :state_event
87
+ # # attr_accessible ... # Alternative technique
88
+ #
89
+ # state_machine do
90
+ # ...
91
+ # end
92
+ # end
93
+ #
94
+ # If you want to only have *some* events be able to fire via mass-assignment,
95
+ # you can build two state machines (one public and one protected) like so:
96
+ #
97
+ # class Vehicle
98
+ # include Mongoid::Document
99
+ #
100
+ # attr_protected :state_event # Prevent access to events in the first machine
101
+ #
102
+ # state_machine do
103
+ # # Define private events here
104
+ # end
105
+ #
106
+ # # Public machine targets the same state as the private machine
107
+ # state_machine :public_state, :attribute => :state do
108
+ # # Define public events here
109
+ # end
110
+ # end
111
+ #
112
+ # == Validations
113
+ #
114
+ # As mentioned in StateMachines::Machine#state, you can define behaviors,
115
+ # like validations, that only execute for certain states. One *important*
116
+ # caveat here is that, due to a constraint in Mongoid's validation
117
+ # framework, custom validators will not work as expected when defined to run
118
+ # in multiple states. For example:
119
+ #
120
+ # class Vehicle
121
+ # include Mongoid::Document
122
+ #
123
+ # state_machine do
124
+ # ...
125
+ # state :first_gear, :second_gear do
126
+ # validate :speed_is_legal
127
+ # end
128
+ # end
129
+ # end
130
+ #
131
+ # In this case, the <tt>:speed_is_legal</tt> validation will only get run
132
+ # for the <tt>:second_gear</tt> state. To avoid this, you can define your
133
+ # custom validation like so:
134
+ #
135
+ # class Vehicle
136
+ # include Mongoid::Document
137
+ #
138
+ # state_machine do
139
+ # ...
140
+ # state :first_gear, :second_gear do
141
+ # validate {|vehicle| vehicle.speed_is_legal}
142
+ # end
143
+ # end
144
+ # end
145
+ #
146
+ # == Validation errors
147
+ #
148
+ # If an event fails to successfully fire because there are no matching
149
+ # transitions for the current record, a validation error is added to the
150
+ # record's state attribute to help in determining why it failed and for
151
+ # reporting via the UI.
152
+ #
153
+ # For example,
154
+ #
155
+ # vehicle = Vehicle.create(:state => 'idling') # => #<Vehicle _id: 4d70e028b876bb54d9000003, name: nil, state: "idling">
156
+ # vehicle.ignite # => false
157
+ # vehicle.errors.full_messages # => ["State cannot transition via \"ignite\""]
158
+ #
159
+ # If an event fails to fire because of a validation error on the record and
160
+ # *not* because a matching transition was not available, no error messages
161
+ # will be added to the state attribute.
162
+ #
163
+ # In addition, if you're using the <tt>ignite!</tt> version of the event,
164
+ # then the failure reason (such as the current validation errors) will be
165
+ # included in the exception that gets raised when the event fails. For
166
+ # example, assuming there's a validation on a field called +name+ on the class:
167
+ #
168
+ # vehicle = Vehicle.new
169
+ # vehicle.ignite! # => StateMachines::InvalidTransition: Cannot transition state via :ignite from :parked (Reason(s): Name cannot be blank)
170
+ #
171
+ # == Scopes
172
+ #
173
+ # To assist in filtering models with specific states, a series of basic
174
+ # scopes are defined on the model for finding records with or without a
175
+ # particular set of states.
176
+ #
177
+ # These scopes are essentially the functional equivalent of the following
178
+ # definitions:
179
+ #
180
+ # class Vehicle
181
+ # include Mongoid::Document
182
+ #
183
+ # scope :with_states, lambda {|*states| where(:state => {'$in' => states})}
184
+ # # with_states also aliased to with_state
185
+ #
186
+ # scope :without_states, lambda {|*states| where(:state => {'$nin' => states})}
187
+ # # without_states also aliased to without_state
188
+ # end
189
+ #
190
+ # *Note*, however, that the states are converted to their stored values
191
+ # before being passed into the query.
192
+ #
193
+ # Because of the way named scopes work in Mongoid, they *cannot* be
194
+ # chained.
195
+ #
196
+ # Note that states can also be referenced by the string version of their
197
+ # name:
198
+ #
199
+ # Vehicle.with_state('parked')
200
+ #
201
+ # == Callbacks
202
+ #
203
+ # All before/after transition callbacks defined for Mongoid models
204
+ # behave in the same way that other Mongoid callbacks behave. The
205
+ # object involved in the transition is passed in as an argument.
206
+ #
207
+ # For example,
208
+ #
209
+ # class Vehicle
210
+ # include Mongoid::Document
211
+ #
212
+ # state_machine :initial => :parked do
213
+ # before_transition any => :idling do |vehicle|
214
+ # vehicle.put_on_seatbelt
215
+ # end
216
+ #
217
+ # before_transition do |vehicle, transition|
218
+ # # log message
219
+ # end
220
+ #
221
+ # event :ignite do
222
+ # transition :parked => :idling
223
+ # end
224
+ # end
225
+ #
226
+ # def put_on_seatbelt
227
+ # ...
228
+ # end
229
+ # end
230
+ #
231
+ # Note, also, that the transition can be accessed by simply defining
232
+ # additional arguments in the callback block.
233
+ #
234
+ # == Observers
235
+ #
236
+ # Have been removed in Rails 4 and therefore are no longer supported.
237
+ #
238
+ # == Internationalization
239
+ #
240
+ # Any error message that is generated from performing invalid transitions
241
+ # can be localized. The following default translations are used:
242
+ #
243
+ # en:
244
+ # mongoid:
245
+ # errors:
246
+ # messages:
247
+ # invalid: "is invalid"
248
+ # # %{value} = attribute value, %{state} = Human state name
249
+ # invalid_event: "cannot transition when %{state}"
250
+ # # %{value} = attribute value, %{event} = Human event name, %{state} = Human current state name
251
+ # invalid_transition: "cannot transition via %{event}"
252
+ #
253
+ # You can override these for a specific model like so:
254
+ #
255
+ # en:
256
+ # mongoid:
257
+ # errors:
258
+ # models:
259
+ # user:
260
+ # invalid: "is not valid"
261
+ #
262
+ # In addition to the above, you can also provide translations for the
263
+ # various states / events in each state machine. Using the Vehicle example,
264
+ # state translations will be looked for using the following keys, where
265
+ # +model_name+ = "vehicle", +machine_name+ = "state" and +state_name+ = "parked":
266
+ # * <tt>mongoid.state_machines.#{model_name}.#{machine_name}.states.#{state_name}</tt>
267
+ # * <tt>mongoid.state_machines.#{model_name}.states.#{state_name}</tt>
268
+ # * <tt>mongoid.state_machines.#{machine_name}.states.#{state_name}</tt>
269
+ # * <tt>mongoid.state_machines.states.#{state_name}</tt>
270
+ #
271
+ # Event translations will be looked for using the following keys, where
272
+ # +model_name+ = "vehicle", +machine_name+ = "state" and +event_name+ = "ignite":
273
+ # * <tt>mongoid.state_machines.#{model_name}.#{machine_name}.events.#{event_name}</tt>
274
+ # * <tt>mongoid.state_machines.#{model_name}.events.#{event_name}</tt>
275
+ # * <tt>mongoid.state_machines.#{machine_name}.events.#{event_name}</tt>
276
+ # * <tt>mongoid.state_machines.events.#{event_name}</tt>
277
+ #
278
+ # An example translation configuration might look like so:
279
+ #
280
+ # es:
281
+ # mongoid:
282
+ # state_machines:
283
+ # states:
284
+ # parked: 'estacionado'
285
+ # events:
286
+ # park: 'estacionarse'
287
+ module Mongoid
288
+ include Base
289
+ include ActiveModel
290
+
291
+ # The default options to use for state machines using this integration
292
+ @defaults = { action: :save, use_transactions: false }
293
+
294
+ # Classes that include Mongoid::Document will automatically use the
295
+ # Mongoid integration.
296
+ def self.matching_ancestors
297
+ %w(Mongoid::Document)
298
+ end
299
+
300
+ def self.locale_path
301
+ "#{File.dirname(__FILE__)}/mongoid/locale.rb"
302
+ end
303
+
304
+ protected
305
+
306
+ # Only runs validations on the action if using <tt>:save</tt>
307
+ def runs_validations_on_action?
308
+ action == :save
309
+ end
310
+
311
+ # Gets the db default for the machine's attribute
312
+ def owner_class_attribute_default
313
+ attribute_field && attribute_field.default_val
314
+ end
315
+
316
+ # Gets the field for this machine's attribute (if it exists)
317
+ def attribute_field
318
+ owner_class.fields[attribute.to_s] || owner_class.fields[owner_class.aliased_fields[attribute.to_s]]
319
+ end
320
+
321
+ # Defines an initialization hook into the owner class for setting the
322
+ # initial state of the machine *before* any attributes are set on the
323
+ # object
324
+ def define_state_initializer
325
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
326
+ def initialize(*)
327
+ super do |*args|
328
+ self.class.state_machines.initialize_states(self, :static => false)
329
+ yield(*args) if block_given?
330
+ end
331
+ end
332
+
333
+ def apply_pre_processed_defaults
334
+ defaults = {}
335
+ self.class.state_machines.initialize_states(self, :static => :force, :dynamic => false, :to => defaults)
336
+ defaults.each do |attr, value|
337
+ send(:"\#{attr}=", value) unless attributes.include?(attr)
338
+ end
339
+ super
340
+ end
341
+ end_eval
342
+ end
343
+
344
+ # Skips defining reader/writer methods since this is done automatically
345
+ def define_state_accessor
346
+ owner_class.field(attribute, type: String) unless attribute_field
347
+ super
348
+ end
349
+
350
+ # Uses around callbacks to run state events if using the :save hook
351
+ def define_action_hook
352
+ if action_hook == :save
353
+ define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
354
+ def insert(*)
355
+ self.class.state_machine(#{name.inspect}).send(:around_save, self) { super.persisted? }
356
+ self
357
+ end
358
+
359
+ def update(*)
360
+ self.class.state_machine(#{name.inspect}).send(:around_save, self) { super }
361
+ end
362
+
363
+ def upsert(*)
364
+ self.class.state_machine(#{name.inspect}).send(:around_save, self) { super }
365
+ end
366
+ end_eval
367
+ else
368
+ super
369
+ end
370
+ end
371
+
372
+ # Runs state events around the machine's :save action
373
+ def around_save(object)
374
+ object.class.state_machines.transitions(object, action).perform { yield }
375
+ end
376
+
377
+ # Creates a scope for finding records *with* a particular state or
378
+ # states for the attribute
379
+ def create_with_scope(name)
380
+ define_scope(name, lambda { |values| { attribute => { '$in' => values } } })
381
+ end
382
+
383
+ # Creates a scope for finding records *without* a particular state or
384
+ # states for the attribute
385
+ def create_without_scope(name)
386
+ define_scope(name, lambda { |values| { attribute => { '$nin' => values } } })
387
+ end
388
+
389
+ # Defines a new scope with the given name
390
+ def define_scope(_name, scope)
391
+ lambda { |model, values| model.criteria.where(scope.call(values)) }
392
+ end
393
+
394
+ def locale_path
395
+ "#{File.dirname(__FILE__)}/mongoid/locale.rb"
396
+ end
397
+ end
398
+ register(Mongoid)
399
+
400
+ end
401
+ end