state_machine-mongoid 0.1.4 → 0.1.5

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.
@@ -1,22 +1,22 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- activemodel (3.0.1)
5
- activesupport (= 3.0.1)
4
+ activemodel (3.0.3)
5
+ activesupport (= 3.0.3)
6
6
  builder (~> 2.1.2)
7
- i18n (~> 0.4.1)
8
- activesupport (3.0.1)
9
- bson (1.1.1)
10
- bson_ext (1.1.1)
7
+ i18n (~> 0.4)
8
+ activesupport (3.0.3)
9
+ bson (1.1.4)
10
+ bson_ext (1.1.4)
11
11
  builder (2.1.2)
12
12
  diff-lcs (1.1.2)
13
13
  git (1.2.5)
14
- i18n (0.4.2)
15
- jeweler (1.5.0.pre5)
14
+ i18n (0.5.0)
15
+ jeweler (1.5.1)
16
16
  bundler (~> 1.0.0)
17
17
  git (>= 1.2.5)
18
18
  rake
19
- mongo (1.1.1)
19
+ mongo (1.1.4)
20
20
  bson (>= 1.1.1)
21
21
  mongoid (2.0.0.beta.20)
22
22
  activemodel (~> 3.0)
@@ -25,16 +25,14 @@ GEM
25
25
  will_paginate (~> 3.0.pre)
26
26
  rake (0.8.7)
27
27
  rcov (0.9.9)
28
- rspec (2.0.1)
29
- rspec-core (~> 2.0.1)
30
- rspec-expectations (~> 2.0.1)
31
- rspec-mocks (~> 2.0.1)
32
- rspec-core (2.0.1)
33
- rspec-expectations (2.0.1)
34
- diff-lcs (>= 1.1.2)
35
- rspec-mocks (2.0.1)
36
- rspec-core (~> 2.0.1)
37
- rspec-expectations (~> 2.0.1)
28
+ rspec (2.2.0)
29
+ rspec-core (~> 2.2)
30
+ rspec-expectations (~> 2.2)
31
+ rspec-mocks (~> 2.2)
32
+ rspec-core (2.2.1)
33
+ rspec-expectations (2.2.0)
34
+ diff-lcs (~> 1.1.2)
35
+ rspec-mocks (2.2.0)
38
36
  state_machine (0.9.4)
39
37
  tzinfo (0.3.23)
40
38
  will_paginate (3.0.pre2)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.4
1
+ 0.1.5
@@ -1,319 +1,104 @@
1
1
  module StateMachine
2
- module Integrations
2
+ module Integrations #:nodoc:
3
3
  module Mongoid
4
+ include ActiveModel
5
+
4
6
  # The default options to use for state machines using this integration
5
- class << self; attr_reader :defaults; end
6
- @defaults = {:action => :save, :use_transactions => false}
7
-
7
+ @defaults = {:action => :save, :use_transactions => false}
8
+
8
9
  # Should this integration be used for state machines in the given class?
9
- # Classes that include Mongoid::Resource will automatically use the
10
- # Mongoid integration.
10
+ # Classes that include Mongoid::Document will automatically use
11
+ # the Mongoid integration.
11
12
  def self.matches?(klass)
12
13
  defined?(::Mongoid::Document) && klass <= ::Mongoid::Document
13
14
  end
14
-
15
+
15
16
  protected
16
-
17
+ # Never add observer support
18
+ def supports_observers?
19
+ false
20
+ end
21
+
22
+ # Always adds validation support
23
+ def supports_validations?
24
+ true
25
+ end
26
+
27
+ # Only runs validations on the action if using <tt>:save</tt>
28
+ def runs_validations_on_action?
29
+ action == :save
30
+ end
31
+
32
+ # Only adds dirty tracking support if ActiveRecord supports it
33
+ def supports_dirty_tracking?(object)
34
+ defined?(::Mongoid::Dirty) && object.respond_to?("#{attribute}_changed?") || super
35
+ end
36
+
37
+ # Always uses the <tt>:mongoid</tt> translation scope
38
+ def i18n_scope
39
+ :mongoid
40
+ end
41
+
42
+ # Only allows translation of I18n is available
43
+ def translate(klass, key, value)
44
+ if defined?(I18n)
45
+ super
46
+ else
47
+ value ? value.to_s.humanize.downcase : 'nil'
48
+ end
49
+ end
50
+
17
51
  # Defines an initialization hook into the owner class for setting the
18
52
  # initial state of the machine *before* any attributes are set on the
19
53
  # object
20
54
  def define_state_initializer
55
+ @instance_helper_module.class_eval <<-end_eval, __FILE__, __LINE__
56
+ # Ensure that the attributes setter gets used to force initialization
57
+ # of the state machines
58
+ def initialize(attributes = nil, *args)
59
+ attributes ||= {}
60
+ super
61
+ end
62
+
63
+ # Hooks in to attribute initialization to set the states *prior*
64
+ # to the attributes being set
65
+ def attributes=(new_attributes, *args)
66
+ if new_record? && !@initialized_state_machines
67
+ @initialized_state_machines = true
68
+
69
+ ignore = if new_attributes
70
+ attributes = new_attributes.dup
71
+ attributes.stringify_keys!
72
+ sanitize_for_mass_assignment(attributes).keys
73
+ else
74
+ []
75
+ end
76
+
77
+ initialize_state_machines(:dynamic => false, :ignore => ignore)
78
+ super
79
+ initialize_state_machines(:dynamic => true, :ignore => ignore)
80
+ else
81
+ super
82
+ end
83
+ end
84
+ end_eval
85
+ end
86
+
87
+ # Adds support for defining the attribute predicate, while providing
88
+ # compatibility with the default predicate which determines whether
89
+ # *anything* is set for the attribute's value
90
+ def define_state_predicate
91
+ name = self.name
92
+
93
+ # Still use class_eval here instance of define_instance_method since
94
+ # we need to be able to call +super+
95
+ @instance_helper_module.class_eval do
96
+ define_method("#{name}?") do |*args|
97
+ args.empty? ? super(*args) : self.class.state_machine(name).states.matches?(self, *args)
98
+ end
99
+ end
21
100
  end
101
+
22
102
  end
23
103
  end
24
104
  end
25
- # module StateMachine
26
- # module Integrations #:nodoc:
27
- # # Adds support for integrating state machines with Mongoid models.
28
- # #
29
- # # == Examples
30
- # #
31
- # # Below is an example of a simple state machine defined within a
32
- # # Mongoid model:
33
- # #
34
- # # class Vehicle
35
- # # include Mongoid::Document
36
- # #
37
- # # state_machine :initial => :parked do
38
- # # event :ignite do
39
- # # transition :parked => :idling
40
- # # end
41
- # # end
42
- # # end
43
- # #
44
- # # The examples in the sections below will use the above class as a
45
- # # reference.
46
- # #
47
- # # == Actions
48
- # #
49
- # # By default, the action that will be invoked when a state is transitioned
50
- # # is the +save+ action. This will cause the record to save the changes
51
- # # made to the state machine's attribute. *Note* that if any other changes
52
- # # were made to the record prior to transition, then those changes will
53
- # # be saved as well.
54
- # #
55
- # # For example,
56
- # #
57
- # # vehicle = Vehicle.create # => #<Vehicle id: 1, name: nil, state: "parked">
58
- # # vehicle.name = 'Ford Explorer'
59
- # # vehicle.ignite # => true
60
- # # vehicle.reload # => #<Vehicle id: 1, name: "Ford Explorer", state: "idling">
61
- # #
62
- # # == Events
63
- # #
64
- # # As described in StateMachine::InstanceMethods#state_machine, event
65
- # # attributes are created for every machine that allow transitions to be
66
- # # performed automatically when the object's action (in this case, :save)
67
- # # is called.
68
- # #
69
- # # In Mongoid, these automated events are run in the following order:
70
- # # * before validation - Run before callbacks and persist new states, then validate
71
- # # * before save - If validation was skipped, run before callbacks and persist new states, then save
72
- # # * after save - Run after callbacks
73
- # #
74
- # # For example,
75
- # #
76
- # # vehicle = Vehicle.create # => #<Vehicle id: 1, name: nil, state: "parked">
77
- # # vehicle.state_event # => nil
78
- # # vehicle.state_event = 'invalid'
79
- # # vehicle.valid? # => false
80
- # # vehicle.errors.full_messages # => ["State event is invalid"]
81
- # #
82
- # # vehicle.state_event = 'ignite'
83
- # # vehicle.valid? # => true
84
- # # vehicle.save # => true
85
- # # vehicle.state # => "idling"
86
- # # vehicle.state_event # => nil
87
- # #
88
- # # Note that this can also be done on a mass-assignment basis:
89
- # #
90
- # # vehicle = Vehicle.create(:state_event => 'ignite') # => #<Vehicle id: 1, name: nil, state: "idling">
91
- # # vehicle.state # => "idling"
92
- # #
93
- # # This technique is always used for transitioning states when the +save+
94
- # # action (which is the default) is configured for the machine.
95
- # #
96
- # # === Security implications
97
- # #
98
- # # Beware that public event attributes mean that events can be fired
99
- # # whenever mass-assignment is being used. If you want to prevent malicious
100
- # # users from tampering with events through URLs / forms, the attribute
101
- # # should be protected like so:
102
- # #
103
- # # class Vehicle
104
- # # include Mongoid::Document
105
- # #
106
- # # attr_protected :state_event
107
- # # # attr_accessible ... # Alternative technique
108
- # #
109
- # # state_machine do
110
- # # ...
111
- # # end
112
- # # end
113
- # #
114
- # # If you want to only have *some* events be able to fire via mass-assignment,
115
- # # you can build two state machines (one public and one protected) like so:
116
- # #
117
- # # class Vehicle
118
- # # include Mongoid::Document
119
- # #
120
- # # attr_protected :state_event # Prevent access to events in the first machine
121
- # #
122
- # # state_machine do
123
- # # # Define private events here
124
- # # end
125
- # #
126
- # # # Public machine targets the same state as the private machine
127
- # # state_machine :public_state, :attribute => :state do
128
- # # # Define public events here
129
- # # end
130
- # # end
131
- # #
132
- # # == Validation errors
133
- # #
134
- # # If an event fails to successfully fire because there are no matching
135
- # # transitions for the current record, a validation error is added to the
136
- # # record's state attribute to help in determining why it failed and for
137
- # # reporting via the UI.
138
- # #
139
- # # For example,
140
- # #
141
- # # vehicle = Vehicle.create(:state => 'idling') # => #<Vehicle id: 1, name: nil, state: "idling">
142
- # # vehicle.ignite # => false
143
- # # vehicle.errors.full_messages # => ["State cannot transition via \"ignite\""]
144
- # #
145
- # # If an event fails to fire because of a validation error on the record and
146
- # # *not* because a matching transition was not available, no error messages
147
- # # will be added to the state attribute.
148
- # #
149
- # # == Scopes
150
- # #
151
- # # To assist in filtering models with specific states, a series of basic
152
- # # scopes are defined on the model for finding records with or without a
153
- # # particular set of states.
154
- # #
155
- # # These scopes are essentially the functional equivalent of the following
156
- # # definitions:
157
- # #
158
- # # class Vehicle
159
- # # include Mongoid::Document
160
- # #
161
- # # def self.with_states(*states)
162
- # # all(:conditions => {:state => {'$in' => states}})
163
- # # end
164
- # # # with_states also aliased to with_state
165
- # #
166
- # # def self.without_states(*states)
167
- # # all(:conditions => {:state => {'$nin' => states}})
168
- # # end
169
- # # # without_states also aliased to without_state
170
- # # end
171
- # #
172
- # # *Note*, however, that the states are converted to their stored values
173
- # # before being passed into the query.
174
- # #
175
- # # Because of the way named scopes work in Mongoid, they *cannot* be
176
- # # chained.
177
- # #
178
- # # == Callbacks
179
- # #
180
- # # All before/after transition callbacks defined for Mongoid models
181
- # # behave in the same way that other Mongoid callbacks behave. The
182
- # # object involved in the transition is passed in as an argument.
183
- # #
184
- # # For example,
185
- # #
186
- # # class Vehicle
187
- # # include Mongoid::Document
188
- # #
189
- # # state_machine :initial => :parked do
190
- # # before_transition any => :idling do |vehicle|
191
- # # vehicle.put_on_seatbelt
192
- # # end
193
- # #
194
- # # before_transition do |vehicle, transition|
195
- # # # log message
196
- # # end
197
- # #
198
- # # event :ignite do
199
- # # transition :parked => :idling
200
- # # end
201
- # # end
202
- # #
203
- # # def put_on_seatbelt
204
- # # ...
205
- # # end
206
- # # end
207
- # #
208
- # # Note, also, that the transition can be accessed by simply defining
209
- # # additional arguments in the callback block.
210
- # module Mongoid
211
- # include ActiveModel
212
- #
213
- # # The default options to use for state machines using this integration
214
- # @defaults = {:action => :save}
215
- #
216
- # # Should this integration be used for state machines in the given class?
217
- # # Classes that include Mongoid::Document will automatically use the
218
- # # Mongoid integration.
219
- # def self.matches?(klass)
220
- # defined?(::Mongoid::Document) && klass <= ::Mongoid::Document
221
- # end
222
- #
223
- # # Adds a validation error to the given object (no i18n support)
224
- # def invalidate(object, attribute, message, values = [])
225
- # object.errors.add(self.attribute(attribute), generate_message(message, values))
226
- # end
227
- #
228
- # protected
229
- # # Does not support observers
230
- # def supports_observers?
231
- # false
232
- # end
233
- #
234
- # # Always adds validation support
235
- # def supports_validations?
236
- # true
237
- # end
238
- #
239
- # # Only runs validations on the action if using <tt>:save</tt>
240
- # def runs_validations_on_action?
241
- # action == :save
242
- # end
243
- #
244
- # # Always adds dirty tracking support
245
- # def supports_dirty_tracking?(object)
246
- # true
247
- # end
248
- #
249
- # # Don't allow callback terminators
250
- # def callback_terminator
251
- # end
252
- #
253
- # # Defines an initialization hook into the owner class for setting the
254
- # # initial state of the machine *before* any attributes are set on the
255
- # # object
256
- # def define_state_initializer
257
- # @instance_helper_module.class_eval <<-end_eval, __FILE__, __LINE__
258
- # def initialize(attrs = {}, *args)
259
- # from_database = args.first
260
- #
261
- # if !from_database && (!attrs || !attrs.stringify_keys.key?('_id'))
262
- # filtered = respond_to?(:filter_protected_attrs) ? filter_protected_attrs(attrs) : attrs
263
- # ignore = filtered ? filtered.keys : []
264
- #
265
- # initialize_state_machines(:dynamic => false, :ignore => ignore)
266
- # super
267
- # initialize_state_machines(:dynamic => true, :ignore => ignore)
268
- # else
269
- # super
270
- # end
271
- # end
272
- # end_eval
273
- # end
274
- #
275
- # # Skips defining reader/writer methods since this is done automatically
276
- # def define_state_accessor
277
- # owner_class.field(attribute, :type => String) unless owner_class.fields.nil? #.include?(attribute)
278
- #
279
- # name = self.name
280
- # owner_class.validates_each(attribute, :logic => lambda {|*|
281
- # machine = self.class.state_machine(name)
282
- # machine.invalidate(self, :state, :invalid) unless machine.states.match(self)
283
- # })
284
- # end
285
- #
286
- # # Adds support for defining the attribute predicate, while providing
287
- # # compatibility with the default predicate which determines whether
288
- # # *anything* is set for the attribute's value
289
- # def define_state_predicate
290
- # name = self.name
291
- #
292
- # # Still use class_eval here instance of define_instance_method since
293
- # # we need to be able to call +super+
294
- # @instance_helper_module.class_eval do
295
- # define_method("#{name}?") do |*args|
296
- # args.empty? ? super(*args) : self.class.state_machine(name).states.matches?(self, *args)
297
- # end
298
- # end
299
- # end
300
- #
301
- # # Adds hooks into validation for automatically firing events
302
- # def define_action_helpers
303
- # super(action == :save ? :create_or_update : action)
304
- # end
305
- #
306
- # # Creates a scope for finding records *with* a particular state or
307
- # # states for the attribute
308
- # def create_with_scope(name)
309
- # lambda {|model, values| model.all(:conditions => {attribute => {'$in' => values}})}
310
- # end
311
- #
312
- # # Creates a scope for finding records *without* a particular state or
313
- # # states for the attribute
314
- # def create_without_scope(name)
315
- # lambda {|model, values| model.all(:conditions => {attribute => {'$nin' => values}})}
316
- # end
317
- # end
318
- # end
319
- # end
@@ -27,8 +27,20 @@ describe "StateMachineMongoid integration" do
27
27
  end
28
28
 
29
29
  it "should be ignited" do
30
+ @vehicle.state.should == "idling"
30
31
  @vehicle.idling?.should be_true
31
32
  end
33
+
34
+ it "should add error messages when transition is invalid" do
35
+ @vehicle.ignite.should be_false
36
+ @vehicle.errors.should_not be_empty
37
+ end
38
+
39
+ it "should not allow to set incorrect state" do
40
+ @vehicle.state = "flying"
41
+ @vehicle.valid?.should be_false
42
+ end
43
+
32
44
  end
33
45
  end
34
46
 
@@ -43,5 +55,12 @@ describe "StateMachineMongoid integration" do
43
55
  @vehicle.ignite
44
56
  @vehicle.idling?.should be_true
45
57
  end
58
+
59
+ it "should add error messages when transition is invalid" do
60
+ @vehicle.ignite.should be_true
61
+ @vehicle.ignite.should be_false
62
+ @vehicle.errors.should_not be_empty
63
+ end
64
+
46
65
  end
47
66
  end
@@ -2,7 +2,7 @@ class Vehicle
2
2
  include Mongoid::Document
3
3
 
4
4
  field :state, :type => String, :default => "parked"
5
- field :alarm_state, :type => String, :default => "active"
5
+ field :alarm_state, :type => Integer, :default => 1
6
6
 
7
7
  state_machine :state, :initial => :parked do
8
8
  before_transition :parked => any - :parked, :do => :put_on_seatbelt
@@ -1,15 +1,15 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{state_machine-mongoid}
8
- s.version = "0.1.4"
8
+ s.version = "0.1.5"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Marcin Ciunelis"]
12
- s.date = %q{2010-11-03}
12
+ s.date = %q{2010-12-09}
13
13
  s.description = %q{a little lack of tests but it works!}
14
14
  s.email = %q{marcin.ciunelis@gmail.com}
15
15
  s.extra_rdoc_files = [
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: state_machine-mongoid
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 17
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 4
10
- version: 0.1.4
9
+ - 5
10
+ version: 0.1.5
11
11
  platform: ruby
12
12
  authors:
13
13
  - Marcin Ciunelis
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-03 00:00:00 +01:00
18
+ date: 2010-12-09 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency