transitions 0.1.11 → 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b2c18ccf9710c9acf987c3bab92b3f496c0109ef
4
- data.tar.gz: 050d68e232efbe9d830eabb7163c54536e451fee
3
+ metadata.gz: c5fdc12a9620c4afb752d88f581df0416e1bfb2c
4
+ data.tar.gz: 2e513473a1ab671758977874cc4670c43a38d76b
5
5
  SHA512:
6
- metadata.gz: 4c467765c0917f255aacf6cc082a33e5e21afe38f37d408376bc6055408317b05d4f3f3862db2ffbb2d9a5bbbe1c6124ffed10e3beb356ef6dee593308cc0ba4
7
- data.tar.gz: 6891fc7543e6d755c284bd4724e8fcead9405c822f2491b420616d0557866fafea947fd1c4fdc8be7a7837418ac4fdbfe807f6dc014f83a8c7c083c37563f452
6
+ metadata.gz: 8843c2845f29a81cc1a4fe170796976850515a3d5b513996e50e4904ae8a418d47fa38be5e72f1a325623e0295910df7022c4f061c395bc9aeb8120a6d84e2c8
7
+ data.tar.gz: a00d3f4ed41f725e47e4afc81aa030958e86d966f1192008365c988e9a167d3243dd2d79126fd7365a2965cd58071779294b59d8caaefef2849e3f41d43c56f5
data/.gitignore CHANGED
@@ -3,4 +3,5 @@ pkg
3
3
  .bundle
4
4
  .idea/
5
5
  Gemfile.lock
6
+ gemfiles/*.lock
6
7
  *.swp
data/.travis.yml CHANGED
@@ -1,3 +1,9 @@
1
1
  rvm:
2
2
  - 2.0.0
3
3
  - 1.9.3
4
+ - jruby-19mode
5
+ gemfile:
6
+ - gemfiles/rails_3_0.gemfile
7
+ - gemfiles/rails_3_1.gemfile
8
+ - gemfiles/rails_3_2.gemfile
9
+ - gemfiles/rails_4_0.gemfile
data/Appraisals ADDED
@@ -0,0 +1,23 @@
1
+ appraise "rails_3_0" do
2
+ gem "activerecord", "~> 3.0.10"
3
+ gem "activerecord-jdbcsqlite3-adapter", :platforms=>:jruby
4
+ gem 'sqlite3', :platforms => :ruby
5
+ end
6
+
7
+ appraise "rails_3_1" do
8
+ gem "activerecord", "~>3.1.12"
9
+ gem "activerecord-jdbcsqlite3-adapter", :platforms=>:jruby
10
+ gem 'sqlite3', :platforms => :ruby
11
+ end
12
+
13
+ appraise "rails_3_2" do
14
+ gem "activerecord", "~>3.2.14"
15
+ gem "activerecord-jdbcsqlite3-adapter", :platforms=>:jruby
16
+ gem 'sqlite3', :platforms => :ruby
17
+ end
18
+
19
+ appraise "rails_4_0" do
20
+ gem "activerecord", "~>4.0.0"
21
+ gem "activerecord-jdbcsqlite3-adapter", '1.3.0.rc1', :platforms=>:jruby
22
+ gem 'sqlite3', :platforms => :ruby
23
+ end
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 0.1.12
2
+
3
+ * (troessner) Fix issue 107: timestamps are updated even if guard fails.
4
+
1
5
  # 0.1.10
2
6
 
3
7
  * (troessner) Fix: Using a custom ActiveRecord select query without the name of the state attribute would trigger an exception.
data/Gemfile CHANGED
@@ -1,3 +1,7 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
+
5
+ gem 'activerecord', '~> 3.2.14'
6
+ gem 'activerecord-jdbcsqlite3-adapter', :platforms => :jruby
7
+ gem 'sqlite3', :platforms => :ruby
data/README.md CHANGED
@@ -22,44 +22,48 @@ issue](https://github.com/troessner/transitions/issues/86) for example).
22
22
  #### Rails
23
23
 
24
24
  This goes into your Gemfile:
25
-
26
- gem "transitions", :require => ["transitions", "active_model/transitions"]
25
+ ```ruby
26
+ gem "transitions", :require => ["transitions", "active_model/transitions"]
27
+ ```
27
28
 
28
29
  … and this into your ORM model:
29
-
30
- include ActiveModel::Transitions
30
+ ```ruby
31
+ include ActiveModel::Transitions
32
+ ```
31
33
 
32
34
  #### Standalone
33
-
34
- gem install transitions
35
+ ```shell
36
+ gem install transitions
37
+ ```
35
38
 
36
39
  ### Using transitions
40
+ ```ruby
41
+ class Product
42
+ include ActiveModel::Transitions
37
43
 
38
- class Product
39
- include ActiveModel::Transitions
40
-
41
- state_machine do
42
- state :available # first one is initial state
43
- state :out_of_stock, :exit => :exit_out_of_stock
44
- state :discontinued, :enter => lambda { |product| product.cancel_orders }
45
-
46
- event :discontinued do
47
- transitions :to => :discontinued, :from => [:available, :out_of_stock], :on_transition => :do_discontinue
48
- end
49
- event :out_of_stock, :success => :reorder do
50
- transitions :to => :out_of_stock, :from => [:available, :discontinued]
51
- end
52
- event :available do
53
- transitions :to => :available, :from => [:out_of_stock], :guard => lambda { |product| product.in_stock > 0 }
54
- end
55
- end
56
- end
44
+ state_machine do
45
+ state :available # first one is initial state
46
+ state :out_of_stock, :exit => :exit_out_of_stock
47
+ state :discontinued, :enter => lambda { |product| product.cancel_orders }
57
48
 
49
+ event :discontinued do
50
+ transitions :to => :discontinued, :from => [:available, :out_of_stock], :on_transition => :do_discontinue
51
+ end
52
+ event :out_of_stock, :success => :reorder do
53
+ transitions :to => :out_of_stock, :from => [:available, :discontinued]
54
+ end
55
+ event :available do
56
+ transitions :to => :available, :from => [:out_of_stock], :guard => lambda { |product| product.in_stock > 0 }
57
+ end
58
+ end
59
+ end
60
+ ```
58
61
  In this example we assume that you are in a rails project using Bundler, which
59
62
  would automatically require `transitions`. If this is not the case for you you
60
63
  have to add
61
-
62
- require 'transitions'
64
+ ```ruby
65
+ require 'transitions'
66
+ ```
63
67
 
64
68
  wherever you load your dependencies in your application.
65
69
 
@@ -80,18 +84,20 @@ wherever you load your dependencies in your application.
80
84
 
81
85
  Use the (surprise ahead) `current_state` method - in case you didn't set a
82
86
  state explicitly you'll get back the state that you defined as initial state.
83
-
84
- >> Product.new.current_state
85
- => :available
87
+ ```ruby
88
+ >> Product.new.current_state
89
+ => :available
90
+ ```
86
91
 
87
92
  You can also set a new state explicitly via `update_current_state(new_state,
88
93
  persist = true / false)` but you should never do this unless you really know
89
94
  what you're doing and why - rather use events / state transitions (see below).
90
95
 
91
96
  Predicate methods are also available using the name of the state.
92
-
93
- >> Product.new.available?
94
- => true
97
+ ```ruby
98
+ >> Product.new.available?
99
+ => true
100
+ ```
95
101
 
96
102
  #### Events
97
103
 
@@ -103,14 +109,16 @@ modify state but instead returns a boolean letting you know if a given
103
109
  transition is possible.
104
110
 
105
111
  In addition, a `can_transition?` method is added to the object that expects one or more event names as arguments. This semi-verbose method name is used to avoid collission with [https://github.com/ryanb/cancan](the authorization gem CanCan).
106
-
107
- >> Product.new.can_transition? :out_of_stock
108
- => true
112
+ ```ruby
113
+ >> Product.new.can_transition? :out_of_stock
114
+ => true
115
+ ```
109
116
 
110
117
  If you need to get all available transitions for current state you can simply call:
111
-
112
- >> Product.new.available_transitions
113
- => [:discontinued, :out_of_stock]
118
+ ```ruby
119
+ >> Product.new.available_transitions
120
+ => [:discontinued, :out_of_stock]
121
+ ```
114
122
 
115
123
  #### Automatic scope generation
116
124
 
@@ -118,26 +126,28 @@ If you need to get all available transitions for current state you can simply ca
118
126
  ActiveRecord and tell it to do so via the `auto_scopes` option:
119
127
 
120
128
  Given a model like this:
121
-
122
- class Order < ActiveRecord::Base
123
- include ActiveModel::Transitions
124
- state_machine :auto_scopes => true do
125
- state :pick_line_items
126
- state :picking_line_items
127
- event :move_cart do
128
- transitions to: :pick_line_items, from: :picking_line_items
129
- end
130
- end
129
+ ```ruby
130
+ class Order < ActiveRecord::Base
131
+ include ActiveModel::Transitions
132
+ state_machine :auto_scopes => true do
133
+ state :pick_line_items
134
+ state :picking_line_items
135
+ event :move_cart do
136
+ transitions to: :pick_line_items, from: :picking_line_items
131
137
  end
138
+ end
139
+ end
140
+ ```
132
141
 
133
142
  you can use this feature a la:
134
-
135
- >> Order.pick_line_items
136
- => []
137
- >> Order.create!
138
- => #<Order id: 3, state: "pick_line_items", description: nil, created_at: "2011-08-23 15:48:46", updated_at: "2011-08-23 15:48:46">
139
- >> Order.pick_line_items
140
- => [#<Order id: 3, state: "pick_line_items", description: nil, created_at: "2011-08-23 15:48:46", updated_at: "2011-08-23 15:48:46">]
143
+ ```ruby
144
+ >> Order.pick_line_items
145
+ => []
146
+ >> Order.create!
147
+ => #<Order id: 3, state: "pick_line_items", description: nil, created_at: "2011-08-23 15:48:46", updated_at: "2011-08-23 15:48:46">
148
+ >> Order.pick_line_items
149
+ => [#<Order id: 3, state: "pick_line_items", description: nil, created_at: "2011-08-23 15:48:46", updated_at: "2011-08-23 15:48:46">]
150
+ ```
141
151
 
142
152
  #### Using `guard`
143
153
 
@@ -145,16 +155,18 @@ Each event definition takes an optional `guard` argument, which acts as a
145
155
  predicate for the transition.
146
156
 
147
157
  You can pass in Symbols, Strings, or Procs like this:
148
-
149
- event :discontinue do
150
- transitions :to => :discontinued, :from => [:available, :out_of_stock], :guard => :can_discontinue
151
- end
158
+ ```ruby
159
+ event :discontinue do
160
+ transitions :to => :discontinued, :from => [:available, :out_of_stock], :guard => :can_discontinue
161
+ end
162
+ ```
152
163
 
153
164
  or
154
-
155
- event :discontinue do
156
- transitions :to => :discontinued, :from => [:available, :out_of_stock], :guard => [:can_discontinue, :super_sure?]
157
- end
165
+ ```ruby
166
+ event :discontinue do
167
+ transitions :to => :discontinued, :from => [:available, :out_of_stock], :guard => [:can_discontinue, :super_sure?]
168
+ end
169
+ ```
158
170
 
159
171
  Any arguments passed to the event method will be passed on to the `guard`
160
172
  predicate.
@@ -166,10 +178,11 @@ you to execute methods on transition.
166
178
 
167
179
  You can pass in a Symbol, a String, a Proc or an Array containing method names
168
180
  as Symbol or String like this:
169
-
170
- event :discontinue do
171
- transitions :to => :discontinued, :from => [:available, :out_of_stock], :on_transition => [:do_discontinue, :notify_clerk]
172
- end
181
+ ```ruby
182
+ event :discontinue do
183
+ transitions :to => :discontinued, :from => [:available, :out_of_stock], :on_transition => [:do_discontinue, :notify_clerk]
184
+ end
185
+ ```
173
186
 
174
187
  Any arguments passed to the event method will be passed on to the `on_transition` callback.
175
188
 
@@ -193,24 +206,27 @@ In case you need to trigger a method call after a successful transition you
193
206
  can use `success`. This will be called after the `save!` is complete (if you
194
207
  use the `state_name!` method) and should be used for any methods that require
195
208
  that the object be persisted.
196
-
197
- event :discontinue, :success => :notfiy_admin do
198
- transitions :to => :discontinued, :from => [:available, :out_of_stock]
199
- end
209
+ ```ruby
210
+ event :discontinue, :success => :notfiy_admin do
211
+ transitions :to => :discontinued, :from => [:available, :out_of_stock]
212
+ end
213
+ ```
200
214
 
201
215
  In addition to just specify the method name on the record as a symbol you can
202
216
  pass a lambda to perfom some more complex success callbacks:
203
-
204
- event :discontinue, :success => lambda { |order| AdminNotifier.notify_about_discontinued_order(order) } do
205
- transitions :to => :discontinued, :from => [:available, :out_of_stock]
206
- end
217
+ ```ruby
218
+ event :discontinue, :success => lambda { |order| AdminNotifier.notify_about_discontinued_order(order) } do
219
+ transitions :to => :discontinued, :from => [:available, :out_of_stock]
220
+ end
221
+ ```
207
222
 
208
223
  If you need it, you can even call multiple methods or lambdas just passing an
209
224
  array:
210
-
211
- event :discontinue, :success => [:notify_admin, lambda { |order| AdminNotifier.notify_about_discontinued_order(order) }] do
212
- transitions :to => :discontinued, :from => [:available, :out_of_stock]
213
- end
225
+ ```ruby
226
+ event :discontinue, :success => [:notify_admin, lambda { |order| AdminNotifier.notify_about_discontinued_order(order) }] do
227
+ transitions :to => :discontinued, :from => [:available, :out_of_stock]
228
+ end
229
+ ```
214
230
 
215
231
  #### Timestamps
216
232
 
@@ -218,17 +234,18 @@ If you'd like to note the time of a state change, Transitions comes with
218
234
  timestamps free! To activate them, simply pass the `timestamp` option to the
219
235
  event definition with a value of either true or the name of the timestamp
220
236
  column. *NOTE - This should be either true, a String or a Symbol*
221
-
222
- # This will look for an attribute called exploded_at or exploded_on (in that order)
223
- # If present, it will be updated
224
- event :explode, :timestamp => true do
225
- transitions :from => :complete, :to => :exploded
226
- end
227
-
228
- # This will look for an attribute named repaired_on to update upon save
229
- event :rebuild, :timestamp => :repaired_on do
230
- transitions :from => :exploded, :to => :rebuilt
231
- end
237
+ ```ruby
238
+ # This will look for an attribute called exploded_at or exploded_on (in that order)
239
+ # If present, it will be updated
240
+ event :explode, :timestamp => true do
241
+ transitions :from => :complete, :to => :exploded
242
+ end
243
+
244
+ # This will look for an attribute named repaired_on to update upon save
245
+ event :rebuild, :timestamp => :repaired_on do
246
+ transitions :from => :exploded, :to => :rebuilt
247
+ end
248
+ ```
232
249
 
233
250
  #### Using `event_fired` and `event_failed`
234
251
 
@@ -236,47 +253,52 @@ In case you define `event_fired` and / or `event_failed`, `transitions` will
236
253
  use those callbacks correspondingly.
237
254
 
238
255
  You can use those callbacks like this:
256
+ ```ruby
257
+ def event_fired(current_state, new_state, event)
258
+ MyLogger.info "Event fired #{event.inspect}"
259
+ end
239
260
 
240
- def event_fired(current_state, new_state, event)
241
- MyLogger.info "Event fired #{event.inspect}"
242
- end
243
-
244
- def event_failed(event)
245
- MyLogger.warn "Event failed #{event.inspect}"
246
- end
261
+ def event_failed(event)
262
+ MyLogger.warn "Event failed #{event.inspect}"
263
+ end
264
+ ```
247
265
 
248
266
  #### Listing all the available states and events
249
267
 
250
268
  You can easily get a listing of all available states:
251
-
252
- Order.available_states # Uses the <tt>default</tt> state machine
253
- # => [:pick_line_items, :picking_line_items]
269
+ ```ruby
270
+ Order.available_states # Uses the <tt>default</tt> state machine
271
+ # => [:pick_line_items, :picking_line_items]
272
+ ```
254
273
 
255
274
  Same goes for the available events:
256
-
257
- Order.available_events
258
- # => [:move_cart]
275
+ ```ruby
276
+ Order.available_events
277
+ # => [:move_cart]
278
+ ```
259
279
 
260
280
  #### Explicitly setting the initial state with the `initial` option
261
-
262
- state_machine :initial => :closed do
263
- state :open
264
- state :closed
265
- end
281
+ ```ruby
282
+ state_machine :initial => :closed do
283
+ state :open
284
+ state :closed
285
+ end
286
+ ```
266
287
 
267
288
  ### Configuring a different column name with ActiveRecord
268
289
 
269
290
  To use a different column than `state` to track it's value simply do this:
291
+ ```ruby
292
+ class Product < ActiveRecord::Base
293
+ include Transitions
270
294
 
271
- class Product < ActiveRecord::Base
272
- include Transitions
295
+ state_machine :attribute_name => :different_column do
273
296
 
274
- state_machine :attribute_name => :different_column do
297
+ ...
275
298
 
276
- ...
277
-
278
- end
279
- end
299
+ end
300
+ end
301
+ ```
280
302
 
281
303
  ### Known bugs / limitations
282
304
 
data/Rakefile CHANGED
@@ -2,6 +2,8 @@ require "bundler"
2
2
  Bundler::GemHelper.install_tasks
3
3
  Bundler.setup
4
4
 
5
+ require 'appraisal'
6
+
5
7
  require "rake/testtask"
6
8
  Rake::TestTask.new(:test) do |test|
7
9
  test.libs = %w(lib test)
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 3.0.10"
6
+ gem "activerecord-jdbcsqlite3-adapter", :platforms=>:jruby
7
+ gem "sqlite3", :platforms=>:ruby
8
+
9
+ gemspec :path=>"../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~>3.1.12"
6
+ gem "activerecord-jdbcsqlite3-adapter", :platforms=>:jruby
7
+ gem "sqlite3", :platforms=>:ruby
8
+
9
+ gemspec :path=>"../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~>3.2.14"
6
+ gem "activerecord-jdbcsqlite3-adapter", :platforms=>:jruby
7
+ gem "sqlite3", :platforms=>:ruby
8
+
9
+ gemspec :path=>"../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~>4.0.0"
6
+ gem "activerecord-jdbcsqlite3-adapter", "1.3.0.rc1", :platforms=>:jruby
7
+ gem "sqlite3", :platforms=>:ruby
8
+
9
+ gemspec :path=>"../"
@@ -50,10 +50,10 @@ module ActiveModel
50
50
  validate :state_presence
51
51
  validate :state_inclusion
52
52
  end
53
-
53
+
54
54
  # The optional options argument is passed to find when reloading so you may
55
55
  # do e.g. record.reload(:lock => true) to reload the same record with an
56
- # exclusive row lock.
56
+ # exclusive row lock.
57
57
  def reload(options = nil)
58
58
  super.tap do
59
59
  sm = self.class.get_state_machine
@@ -75,7 +75,7 @@ module ActiveModel
75
75
  write_state_without_persistence(prev_state)
76
76
  raise
77
77
  end
78
-
78
+
79
79
  def write_state_without_persistence(state)
80
80
  ivar = self.class.get_state_machine.current_state_variable
81
81
  instance_variable_set(ivar, state)
@@ -88,8 +88,8 @@ module ActiveModel
88
88
 
89
89
  def set_initial_state
90
90
  # In case we use a query with a custom select that excludes our state attribute name we need to skip the initialization below.
91
- if self.has_attribute?(transitions_state_column_name)
92
- self[transitions_state_column_name] ||= self.class.get_state_machine.initial_state.to_s
91
+ if self.has_attribute?(transitions_state_column_name) && state_not_set?
92
+ self[transitions_state_column_name] = self.class.get_state_machine.initial_state.to_s
93
93
  self.class.get_state_machine.state_index[self[transitions_state_column_name].to_sym].call_action(:enter, self)
94
94
  end
95
95
  end
@@ -105,5 +105,9 @@ module ActiveModel
105
105
  self.errors.add(transitions_state_column_name, :inclusion, :value => self[transitions_state_column_name])
106
106
  end
107
107
  end
108
+
109
+ def state_not_set?
110
+ self[transitions_state_column_name].nil?
111
+ end
108
112
  end
109
113
  end
@@ -49,14 +49,14 @@ module Transitions
49
49
  next_state = nil
50
50
  transitions.each do |transition|
51
51
  next if to_state && !Array(transition.to).include?(to_state)
52
- if transition.perform(obj, *args)
52
+ if transition.executable?(obj, *args)
53
53
  next_state = to_state || Array(transition.to).first
54
54
  transition.execute(obj, *args)
55
+ update_event_timestamp(obj, next_state) if timestamp_defined?
55
56
  break
56
57
  end
57
58
  end
58
59
  # Update timestamps on obj if a timestamp has been defined
59
- update_event_timestamp(obj, next_state) if timestamp_defined?
60
60
  next_state
61
61
  end
62
62
 
@@ -29,7 +29,7 @@ module Transitions
29
29
  @options = opts
30
30
  end
31
31
 
32
- def perform(obj, *args)
32
+ def executable?(obj, *args)
33
33
  [@guard].flatten.all? { |g| perform_guard(obj, g, *args) }
34
34
  end
35
35
 
@@ -1,3 +1,3 @@
1
1
  module Transitions
2
- VERSION = '0.1.11'
2
+ VERSION = '0.1.12'
3
3
  end
@@ -5,12 +5,11 @@ class CreateTrafficLights < ActiveRecord::Migration
5
5
  create_table(:traffic_lights, :force => true) do |t|
6
6
  t.string :state
7
7
  t.string :name
8
+ t.string :power
8
9
  end
9
10
  end
10
11
  end
11
12
 
12
- set_up_db CreateTrafficLights
13
-
14
13
  class CreateDifferentTrafficLights < ActiveRecord::Migration
15
14
  def self.up
16
15
  create_table(:different_traffic_lights) do |t|
@@ -20,11 +19,8 @@ class CreateDifferentTrafficLights < ActiveRecord::Migration
20
19
  end
21
20
  end
22
21
 
23
- set_up_db CreateDifferentTrafficLights
24
-
25
22
  class TrafficLight < ActiveRecord::Base
26
23
  include ActiveModel::Transitions
27
- attr_reader :power
28
24
 
29
25
  state_machine :auto_scopes => true do
30
26
  state :off, enter: :turn_power_on
@@ -51,15 +47,11 @@ class TrafficLight < ActiveRecord::Base
51
47
  end
52
48
 
53
49
  def turn_power_on
54
- raise "the power should not have been on already" if @power == :on
55
- @power = :on
50
+ raise "the power should not have been on already" if power == "on"
51
+ self.power = "on"
56
52
  end
57
53
  end
58
54
 
59
- class ProtectedTrafficLight < TrafficLight
60
- attr_protected :state
61
- end
62
-
63
55
  class ValidatingTrafficLight < TrafficLight
64
56
  validate {|t| errors.add(:base, 'This TrafficLight will never validate after creation') unless t.new_record? }
65
57
  end
@@ -90,7 +82,12 @@ class TestActiveRecord < Test::Unit::TestCase
90
82
 
91
83
  test "calls enter when setting the initial state" do
92
84
  @new_light = TrafficLight.new
93
- assert_equal :on, @new_light.power
85
+ assert_equal "on", @new_light.power
86
+ end
87
+
88
+ test "does not call enter when loading a persisted record" do
89
+ assert_equal "on", @light.power
90
+ assert_nothing_raised { TrafficLight.find(@light.id) }
94
91
  end
95
92
 
96
93
  test "transition to a valid state" do
@@ -122,14 +119,6 @@ class TestActiveRecord < Test::Unit::TestCase
122
119
  assert_equal :off, @light.current_state
123
120
  end
124
121
 
125
- test "transition does persists state when state is protected" do
126
- protected_light = ProtectedTrafficLight.create!
127
- protected_light.reset!
128
- assert_equal :red, protected_light.current_state
129
- protected_light.reload
130
- assert_equal "red", protected_light.state
131
- end
132
-
133
122
  test "transition with wrong state will not validate" do
134
123
  for s in @light.class.get_state_machine.states
135
124
  @light.state = s.name
@@ -160,7 +149,7 @@ class TestActiveRecord < Test::Unit::TestCase
160
149
  @light.update_attribute(:state, 'green')
161
150
  assert @light.reload.green?, "reloaded state should come from database, not instance variable"
162
151
  end
163
-
152
+
164
153
  test "calling non-bang event updates state attribute" do
165
154
  @light.reset!
166
155
  assert @light.red?
@@ -170,6 +159,29 @@ class TestActiveRecord < Test::Unit::TestCase
170
159
  end
171
160
  end
172
161
 
162
+ if ActiveRecord::VERSION::MAJOR == 3
163
+
164
+ class TestMassAssignmentActiveRecord < Test::Unit::TestCase
165
+ # attr_protected unfortunately invokes a db call, so this test requires that
166
+ # we define the class after the table already exists.
167
+ def setup
168
+ set_up_db CreateTrafficLights
169
+
170
+ @light_with_protected_state = Class.new(TrafficLight) do
171
+ attr_protected :state
172
+ end
173
+ end
174
+
175
+ test "transition does persists state when state is protected" do
176
+ protected_light = @light_with_protected_state.create!
177
+ protected_light.reset!
178
+ assert_equal :red, protected_light.current_state
179
+ protected_light.reload
180
+ assert_equal "red", protected_light.state
181
+ end
182
+ end
183
+ end
184
+
173
185
  class TestNewActiveRecord < TestActiveRecord
174
186
 
175
187
  def setup
@@ -305,7 +317,7 @@ class TestActiveRecordWithDifferentColumnName < Test::Unit::TestCase
305
317
  @light.update_attribute(:different_state, 'green')
306
318
  assert @light.reload.green?, "reloaded state should come from database, not instance variable"
307
319
  end
308
-
320
+
309
321
  test "calling non-bang event updates state attribute" do
310
322
  @light.reset!
311
323
  assert @light.red?
@@ -1,9 +1,9 @@
1
1
  require "helper"
2
2
 
3
- class CreateBears < ActiveRecord::Migration
3
+ class CreateBunnies < ActiveRecord::Migration
4
4
  def self.up
5
- create_table(:bears, :force => true) do |t|
6
- t.string :state
5
+ create_table(:bunnies, :force => true) do |t|
6
+ t.string :status # Explicitly use another state column to ensure that this whole enchilada is working with other state column names than the default ones.
7
7
  end
8
8
  end
9
9
  end
@@ -16,16 +16,6 @@ class CreatePuppies < ActiveRecord::Migration
16
16
  end
17
17
  end
18
18
 
19
- class CreateBunnies < ActiveRecord::Migration
20
- def self.up
21
- create_table(:bunnies, :force => true) do |t|
22
- t.string :status # Explicitly use another state column to ensure that this whole enchilada is working with other state column names than the default ones.
23
- end
24
- end
25
- end
26
-
27
- set_up_db CreateBunnies, CreatePuppies
28
-
29
19
  class Bunny < ActiveRecord::Base
30
20
  include ActiveModel::Transitions
31
21
 
@@ -44,7 +34,7 @@ end
44
34
 
45
35
  class TestScopes < Test::Unit::TestCase
46
36
  def setup
47
- set_up_db CreateBears, CreateBunnies, CreatePuppies
37
+ set_up_db CreateBunnies, CreatePuppies
48
38
  @bunny = Bunny.create!
49
39
  end
50
40
 
@@ -9,12 +9,11 @@ class CreateOrders < ActiveRecord::Migration
9
9
  t.datetime :prepared_on
10
10
  t.datetime :dispatched_at
11
11
  t.date :cancellation_date
12
+ t.boolean :allow_transition, :default => true
12
13
  end
13
14
  end
14
15
  end
15
16
 
16
- set_up_db CreateOrders
17
-
18
17
  class Order < ActiveRecord::Base
19
18
  include ActiveModel::Transitions
20
19
 
@@ -25,7 +24,7 @@ class Order < ActiveRecord::Base
25
24
  state :prepared
26
25
  state :delivered
27
26
  state :cancelled
28
-
27
+
29
28
  # no timestamp col is being specified here - should be ignored
30
29
  event :place do
31
30
  transitions :from => :opened, :to => :placed
@@ -33,7 +32,7 @@ class Order < ActiveRecord::Base
33
32
 
34
33
  # should set paid_at timestamp
35
34
  event :pay, :timestamp => true do
36
- transitions :from => :placed, :to => :paid
35
+ transitions :from => :placed, :to => :paid, :guard => lambda { |obj| obj.allow_transition }
37
36
  end
38
37
 
39
38
  # should set prepared_on
@@ -45,12 +44,12 @@ class Order < ActiveRecord::Base
45
44
  event :deliver, :timestamp => "dispatched_at" do
46
45
  transitions :from => :prepared, :to => :delivered
47
46
  end
48
-
47
+
49
48
  # should set cancellation_date
50
49
  event :cancel, :timestamp => :cancellation_date do
51
50
  transitions :from => [:placed, :paid, :prepared], :to => :cancelled
52
51
  end
53
-
52
+
54
53
  # should raise an exception as there is no timestamp col
55
54
  event :reopen, :timestamp => true do
56
55
  transitions :from => :cancelled, :to => :opened
@@ -75,41 +74,49 @@ class TestActiveRecordTimestamps < Test::Unit::TestCase
75
74
  assert_nothing_raised { @order.place! }
76
75
  assert_equal @order.state, "placed"
77
76
  end
78
-
77
+
79
78
  test "moving to paid should set paid_at" do
80
- @order = create_order(:placed)
79
+ @order = create_order(:placed)
81
80
  @order.pay!
82
81
  @order.reload
83
82
  assert_not_nil @order.paid_at
84
83
  end
85
-
84
+
85
+ test "moving to paid should not set paid_at if our guard evaluates to false" do
86
+ @order = create_order(:placed)
87
+ @order.update_attribute :allow_transition, false
88
+ @order.pay!
89
+ @order.reload
90
+ assert_nil @order.paid_at
91
+ end
92
+
86
93
  test "moving to prepared should set prepared_on" do
87
- @order = create_order(:paid)
94
+ @order = create_order(:paid)
88
95
  @order.prepare!
89
96
  @order.reload
90
97
  assert_not_nil @order.prepared_on
91
98
  end
92
-
99
+
93
100
  test "moving to delivered should set dispatched_at" do
94
- @order = create_order(:prepared)
101
+ @order = create_order(:prepared)
95
102
  @order.deliver!
96
103
  @order.reload
97
104
  assert_not_nil @order.dispatched_at
98
105
  end
99
-
106
+
100
107
  test "moving to cancelled should set cancellation_date" do
101
- @order = create_order(:placed)
108
+ @order = create_order(:placed)
102
109
  @order.cancel!
103
110
  @order.reload
104
111
  assert_not_nil @order.cancellation_date
105
112
  end
106
-
113
+
107
114
  test "moving to reopened should raise an exception as there is no attribute" do
108
- @order = create_order(:cancelled)
109
- assert_raise(NoMethodError) { @order.re_open! }
115
+ @order = create_order(:cancelled)
116
+ assert_raise(NoMethodError) { @order.re_open! }
110
117
  @order.reload
111
118
  end
112
-
119
+
113
120
  test "passing an invalid value to timestamp options should raise an exception" do
114
121
  assert_raise(ArgumentError) do
115
122
  class Order < ActiveRecord::Base
@@ -119,8 +126,8 @@ class TestActiveRecordTimestamps < Test::Unit::TestCase
119
126
  transitions :from => :prepared, :to => :placed
120
127
  end
121
128
  end
122
-
123
- end
129
+
130
+ end
124
131
  end
125
132
  end
126
133
  end
@@ -1,18 +1,15 @@
1
1
  require "helper"
2
2
 
3
- # Regressiontest for https://github.com/troessner/transitions/issues/95
4
- # TODO We use this Trafficlight class quite a lot in our specs including a ton of duplication.
5
- # Unify class and migration definition in one place and then clean up all related specs, including this one.
6
- class CreateTrafficLights < ActiveRecord::Migration
3
+ # Regression test for https://github.com/troessner/transitions/issues/95
4
+ class CreateSwitches < ActiveRecord::Migration
7
5
  def self.up
8
- create_table(:traffic_lights, :force => true) do |t|
6
+ create_table(:switches, :force => true) do |t|
9
7
  t.string :state
10
- t.string :name
11
8
  end
12
9
  end
13
10
  end
14
11
 
15
- class TrafficLight < ActiveRecord::Base
12
+ class Switch < ActiveRecord::Base
16
13
  include ActiveModel::Transitions
17
14
 
18
15
  state_machine do
@@ -23,12 +20,12 @@ end
23
20
 
24
21
  class TestCustomSelect < Test::Unit::TestCase
25
22
  def setup
26
- set_up_db CreateTrafficLights
27
- @light = TrafficLight.create!
23
+ set_up_db CreateSwitches
24
+ Switch.create!
28
25
  end
29
26
 
30
27
  test "should not trigger an exception when we use a custom select query which excludes the name of our state attribute" do
31
- result = TrafficLight.select("id, name")
28
+ result = Switch.select(:id)
32
29
  assert_nothing_raised NoMethodError do
33
30
  result.inspect
34
31
  end
@@ -7,7 +7,7 @@ class TestStateTransitionGuardCheck < Test::Unit::TestCase
7
7
  opts = {:from => "foo", :to => "bar"}
8
8
  st = Transitions::StateTransition.new(opts)
9
9
 
10
- assert st.perform(nil, *args)
10
+ assert st.executable?(nil, *args)
11
11
  end
12
12
 
13
13
  test "should call the method on the object if guard is a symbol" do
@@ -17,7 +17,7 @@ class TestStateTransitionGuardCheck < Test::Unit::TestCase
17
17
  obj = stub
18
18
  obj.expects(:test_guard).with(*args)
19
19
 
20
- st.perform(obj, *args)
20
+ st.executable?(obj, *args)
21
21
  end
22
22
 
23
23
  test "should call the method on the object if guard is a string" do
@@ -27,7 +27,7 @@ class TestStateTransitionGuardCheck < Test::Unit::TestCase
27
27
  obj = stub
28
28
  obj.expects(:test_guard).with(*args)
29
29
 
30
- st.perform(obj, *args)
30
+ st.executable?(obj, *args)
31
31
  end
32
32
 
33
33
  test "should call the proc passing the object if the guard is a proc" do
@@ -37,7 +37,7 @@ class TestStateTransitionGuardCheck < Test::Unit::TestCase
37
37
  obj = stub
38
38
  obj.expects(:test_guard).with(*args)
39
39
 
40
- st.perform(obj, *args)
40
+ st.executable?(obj, *args)
41
41
  end
42
42
 
43
43
  test "should call the method on the object if guard is a symbol" do
@@ -48,7 +48,7 @@ class TestStateTransitionGuardCheck < Test::Unit::TestCase
48
48
  obj.expects(:test_guard).with(*args).returns(true)
49
49
  obj.expects(:test_another_guard).with(*args).returns(true)
50
50
 
51
- assert st.perform(obj, *args)
51
+ assert st.executable?(obj, *args)
52
52
  end
53
53
 
54
54
  end
data/transitions.gemspec CHANGED
@@ -19,8 +19,8 @@ Gem::Specification.new do |s|
19
19
  s.add_development_dependency "mocha", '~> 0.11.0' # With mocha 0.12 we get: undefined method `run' for #<StateMachineMachineTest:0x94918b8> (NoMethodError)
20
20
  s.add_development_dependency "rake"
21
21
  s.add_development_dependency "random_data"
22
- s.add_development_dependency "sqlite3"
23
- s.add_development_dependency "activerecord", "~> 3"
22
+ s.add_development_dependency 'appraisal'
23
+ s.add_development_dependency "activerecord", [">= 3.0", "<= 4.0"]
24
24
 
25
25
  s.files = `git ls-files`.split("\n")
26
26
  s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: transitions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Kuzma
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-09 00:00:00.000000000 Z
12
+ date: 2013-11-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -82,7 +82,7 @@ dependencies:
82
82
  - !ruby/object:Gem::Version
83
83
  version: '0'
84
84
  - !ruby/object:Gem::Dependency
85
- name: sqlite3
85
+ name: appraisal
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
88
  - - '>='
@@ -99,16 +99,22 @@ dependencies:
99
99
  name: activerecord
100
100
  requirement: !ruby/object:Gem::Requirement
101
101
  requirements:
102
- - - ~>
102
+ - - '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '3.0'
105
+ - - <=
103
106
  - !ruby/object:Gem::Version
104
- version: '3'
107
+ version: '4.0'
105
108
  type: :development
106
109
  prerelease: false
107
110
  version_requirements: !ruby/object:Gem::Requirement
108
111
  requirements:
109
- - - ~>
112
+ - - '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '3.0'
115
+ - - <=
110
116
  - !ruby/object:Gem::Version
111
- version: '3'
117
+ version: '4.0'
112
118
  description: Lightweight state machine extracted from ActiveModel
113
119
  email: timo.roessner@googlemail.com
114
120
  executables: []
@@ -119,11 +125,16 @@ files:
119
125
  - .ruby-gemset
120
126
  - .ruby-version
121
127
  - .travis.yml
128
+ - Appraisals
122
129
  - CHANGELOG.md
123
130
  - Gemfile
124
131
  - MIT-LICENSE.txt
125
132
  - README.md
126
133
  - Rakefile
134
+ - gemfiles/rails_3_0.gemfile
135
+ - gemfiles/rails_3_1.gemfile
136
+ - gemfiles/rails_3_2.gemfile
137
+ - gemfiles/rails_4_0.gemfile
127
138
  - lib/active_model/transitions.rb
128
139
  - lib/active_record/transitions.rb
129
140
  - lib/transitions.rb
@@ -172,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
183
  version: 1.3.6
173
184
  requirements: []
174
185
  rubyforge_project: transitions
175
- rubygems_version: 2.0.6
186
+ rubygems_version: 2.1.11
176
187
  signing_key:
177
188
  specification_version: 4
178
189
  summary: State machine extracted from ActiveModel