state_machine-mongoid 0.1.4 → 0.1.5

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