state_machine 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,5 +1,13 @@
1
1
  == master
2
2
 
3
+ == 1.0.1 / 2011-05-30
4
+
5
+ * Add the ability to ignore method conflicts for helpers
6
+ * Generate warnings for any helper, not just state helpers, that has a conflicting method defined in the class
7
+ * Fix scopes in Sequel not working if the table name contains double underscores or is not a string/symbol
8
+ * Add full support for chaining state scopes within Sequel integrations
9
+ * Fix Rails 3.1 deprecation warnings for configuring engine locales [Stefan Penner]
10
+
3
11
  == 1.0.0 / 2011-05-12
4
12
 
5
13
  * Celebrate
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'rake/gempackagetask'
6
6
 
7
7
  spec = Gem::Specification.new do |s|
8
8
  s.name = 'state_machine'
9
- s.version = '1.0.0'
9
+ s.version = '1.0.1'
10
10
  s.platform = Gem::Platform::RUBY
11
11
  s.summary = 'Adds support for creating state machines for attributes on any Ruby class'
12
12
  s.description = s.summary
@@ -13,7 +13,11 @@ if defined?(Rails)
13
13
  end
14
14
  end
15
15
 
16
- StateMachine::RailsEngine.paths.config.locales = locale_paths
16
+ if Rails::VERSION::MAJOR == 3 && Rails::VERSION::MINOR == 0
17
+ StateMachine::RailsEngine.paths.config.locales = locale_paths
18
+ else
19
+ StateMachine::RailsEngine.paths['config/locales'] = locale_paths
20
+ end
17
21
  elsif defined?(I18n)
18
22
  # Rails 2.x
19
23
  I18n.load_path.unshift(*locale_paths)
@@ -416,8 +416,8 @@ module StateMachine
416
416
  # ActiveModel's use of method_missing / respond_to for attribute methods
417
417
  # breaks both ancestor lookups and defined?(super). Need to special-case
418
418
  # the existence of query attribute methods.
419
- def owner_class_ancestor_has_method?(method)
420
- method == "#{name}?" || super
419
+ def owner_class_ancestor_has_method?(scope, method)
420
+ scope == :instance && method == "#{name}?" || super
421
421
  end
422
422
  end
423
423
  end
@@ -264,8 +264,8 @@ module StateMachine
264
264
  # ActiveModel's use of method_missing / respond_to for attribute methods
265
265
  # breaks both ancestor lookups and defined?(super). Need to special-case
266
266
  # the existence of query attribute methods.
267
- def owner_class_ancestor_has_method?(method)
268
- method == "#{name}?" || super
267
+ def owner_class_ancestor_has_method?(scope, method)
268
+ scope == :instance && method == "#{name}?" || super
269
269
  end
270
270
  end
271
271
  end
@@ -349,8 +349,8 @@ module StateMachine
349
349
  # ActiveModel's use of method_missing / respond_to for attribute methods
350
350
  # breaks both ancestor lookups and defined?(super). Need to special-case
351
351
  # the existence of query attribute methods.
352
- def owner_class_ancestor_has_method?(method)
353
- method == "#{name}?" || super
352
+ def owner_class_ancestor_has_method?(scope, method)
353
+ scope == :instance && method == "#{name}?" || super
354
354
  end
355
355
  end
356
356
  end
@@ -26,6 +26,10 @@ module StateMachine
26
26
  def action_hook
27
27
  action == :save ? :save : super
28
28
  end
29
+
30
+ def model_from_dataset(dataset)
31
+ dataset.model_classes[nil]
32
+ end
29
33
  end
30
34
  end
31
35
  end
@@ -374,13 +374,39 @@ module StateMachine
374
374
  # Creates a scope for finding records *with* a particular state or
375
375
  # states for the attribute
376
376
  def create_with_scope(name)
377
- lambda {|model, values| model.filter(:"#{owner_class.table_name}__#{attribute}" => values)}
377
+ create_scope(name, lambda {|dataset, values| dataset.filter(attribute_column => values)})
378
378
  end
379
379
 
380
380
  # Creates a scope for finding records *without* a particular state or
381
381
  # states for the attribute
382
382
  def create_without_scope(name)
383
- lambda {|model, values| model.filter(~{:"#{owner_class.table_name}__#{attribute}" => values})}
383
+ create_scope(name, lambda {|dataset, values| dataset.exclude(attribute_column => values)})
384
+ end
385
+
386
+ # Creates a new named scope with the given name
387
+ def create_scope(name, scope)
388
+ machine = self
389
+ owner_class.def_dataset_method(name) do |*states|
390
+ machine.send(:run_scope, scope, self, states)
391
+ end
392
+
393
+ false
394
+ end
395
+
396
+ # Generates the results for the given scope based on one or more states to
397
+ # filter by
398
+ def run_scope(scope, dataset, states)
399
+ super(scope, model_from_dataset(dataset).state_machine(name), dataset, states)
400
+ end
401
+
402
+ # Determines the model associated with the given dataset
403
+ def model_from_dataset(dataset)
404
+ dataset.model
405
+ end
406
+
407
+ # Generates the fully-qualifed column name for this machine's attribute
408
+ def attribute_column
409
+ ::Sequel::SQL::QualifiedIdentifier.new(owner_class.table_name, attribute)
384
410
  end
385
411
 
386
412
  # Runs a new database transaction, rolling back any changes if the
@@ -376,6 +376,11 @@ module StateMachine
376
376
  :invalid_transition => 'cannot transition via "%s"'
377
377
  }
378
378
 
379
+ # Whether to ignore any conflicts that are detected for helper methods that
380
+ # get generated for a machine's owner class. Default is false.
381
+ class << self; attr_accessor :ignore_method_conflicts; end
382
+ @ignore_method_conflicts = false
383
+
379
384
  # The class that the machine is defined in
380
385
  attr_accessor :owner_class
381
386
 
@@ -583,6 +588,11 @@ module StateMachine
583
588
  # name. If the method is already defined in the scope, then this will not
584
589
  # override it.
585
590
  #
591
+ # If passing in a block, there are two side effects to be aware of
592
+ # 1. The method cannot be chained, meaning that the block cannot call +super+
593
+ # 2. If the method is already defined in an ancestor, then it will not get
594
+ # overridden and a warning will be output.
595
+ #
586
596
  # Example:
587
597
  #
588
598
  # # Instance helper
@@ -611,15 +621,22 @@ module StateMachine
611
621
  # end
612
622
  # end_eval
613
623
  def define_helper(scope, method, *args, &block)
624
+ helper_module = @helper_modules.fetch(scope)
625
+
614
626
  if block_given?
615
- name = self.name
616
- @helper_modules.fetch(scope).class_eval do
617
- define_method(method) do |*args|
618
- block.call((scope == :instance ? self.class : self).state_machine(name), self, *args)
627
+ if !self.class.ignore_method_conflicts && conflicting_ancestor = owner_class_ancestor_has_method?(scope, method)
628
+ ancestor_name = conflicting_ancestor.name && !conflicting_ancestor.name.empty? ? conflicting_ancestor.name : conflicting_ancestor.to_s
629
+ warn "#{scope == :class ? 'Class' : 'Instance'} method \"#{method}\" is already defined in #{ancestor_name}, use generic helper instead."
630
+ else
631
+ name = self.name
632
+ helper_module.class_eval do
633
+ define_method(method) do |*args|
634
+ block.call((scope == :instance ? self.class : self).state_machine(name), self, *args)
635
+ end
619
636
  end
620
637
  end
621
638
  else
622
- @helper_modules.fetch(scope).class_eval(method, *args)
639
+ helper_module.class_eval(method, *args)
623
640
  end
624
641
  end
625
642
 
@@ -1585,13 +1602,14 @@ module StateMachine
1585
1602
  def define_state_accessor
1586
1603
  attribute = self.attribute
1587
1604
 
1588
- @helper_modules[:instance].class_eval { attr_accessor attribute }
1605
+ @helper_modules[:instance].class_eval { attr_reader attribute } unless owner_class_ancestor_has_method?(:instance, attribute)
1606
+ @helper_modules[:instance].class_eval { attr_writer attribute } unless owner_class_ancestor_has_method?(:instance, "#{attribute}=")
1589
1607
  end
1590
1608
 
1591
1609
  # Adds predicate method to the owner class for determining the name of the
1592
1610
  # current state
1593
1611
  def define_state_predicate
1594
- call_super = owner_class_ancestor_has_method?("#{name}?")
1612
+ call_super = !!owner_class_ancestor_has_method?(:instance, "#{name}?")
1595
1613
  define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
1596
1614
  def #{name}?(*args)
1597
1615
  args.empty? && #{call_super} ? super : self.class.state_machine(#{name.inspect}).states.matches?(self, *args)
@@ -1686,14 +1704,35 @@ module StateMachine
1686
1704
  # action must be defined in an ancestor of the owner classs in order for
1687
1705
  # it to be the action hook.
1688
1706
  def action_hook
1689
- action && owner_class_ancestor_has_method?(action) ? action : nil
1707
+ action && owner_class_ancestor_has_method?(:instance, action) ? action : nil
1690
1708
  end
1691
1709
 
1692
- # Determines whether any of the ancestors for this machine's owner class
1693
- # has the given method defined, even if it's private.
1694
- def owner_class_ancestor_has_method?(method)
1695
- owner_class.ancestors.any? do |ancestor|
1696
- ancestor != owner_class && (ancestor.method_defined?(method) || ancestor.private_method_defined?(method))
1710
+ # Determines whether there's already a helper method defined within the
1711
+ # given scope. This is true only if one of the owner's ancestors defines
1712
+ # the method and is further along in the ancestor chain than this
1713
+ # machine's helper module.
1714
+ def owner_class_ancestor_has_method?(scope, method)
1715
+ superclasses = owner_class.ancestors[1..-1].select {|ancestor| ancestor.is_a?(Class)}
1716
+
1717
+ if scope == :class
1718
+ # Use singleton classes
1719
+ current = (class << owner_class; self; end)
1720
+ superclass = superclasses.first
1721
+ else
1722
+ current = owner_class
1723
+ superclass = owner_class.superclass
1724
+ end
1725
+
1726
+ # Generate the list of modules that *only* occur in the owner class, but
1727
+ # were included *prior* to the helper modules, in addition to the
1728
+ # superclasses
1729
+ ancestors = current.ancestors - superclass.ancestors + superclasses
1730
+ ancestors = ancestors[ancestors.index(@helper_modules[scope]) + 1..-1].reverse
1731
+
1732
+ # Search for for the first ancestor that defined this method
1733
+ ancestors.detect do |ancestor|
1734
+ ancestor = (class << ancestor; self; end) if scope == :class && ancestor.is_a?(Class)
1735
+ ancestor.method_defined?(method) || ancestor.private_method_defined?(method)
1697
1736
  end
1698
1737
  end
1699
1738
 
@@ -1737,14 +1776,20 @@ module StateMachine
1737
1776
  # Converts state names to their corresponding values so that they
1738
1777
  # can be looked up properly
1739
1778
  define_helper(:class, method) do |machine, klass, *states|
1740
- values = states.flatten.map {|state| machine.states.fetch(state).value}
1741
- scope.call(klass, values)
1779
+ run_scope(scope, machine, klass, states)
1742
1780
  end
1743
1781
  end
1744
1782
  end
1745
1783
  end
1746
1784
  end
1747
1785
 
1786
+ # Generates the results for the given scope based on one or more states to
1787
+ # filter by
1788
+ def run_scope(scope, machine, klass, states)
1789
+ values = states.flatten.map {|state| machine.states.fetch(state).value}
1790
+ scope.call(klass, values)
1791
+ end
1792
+
1748
1793
  # Pluralizes the given word using #pluralize (if available) or simply
1749
1794
  # adding an "s" to the end of the word
1750
1795
  def pluralize(word)
@@ -260,19 +260,11 @@ module StateMachine
260
260
  end
261
261
 
262
262
  # Adds a predicate method to the owner class so long as a name has
263
- # actually been configured for the state and the method isn't already
264
- # defined in the owner class.
263
+ # actually been configured for the state
265
264
  def add_predicate
266
- owner_class = machine.owner_class
267
- predicate = "#{qualified_name}?"
268
- if !owner_class.method_defined?(predicate) && !owner_class.private_method_defined?(predicate)
269
- # Checks whether the current value matches this state
270
- machine.define_helper(:instance, predicate) do |machine, object|
271
- machine.states.matches?(object, name)
272
- end
273
- else
274
- # Only output a warning since we can't defined the predicate
275
- warn "#{owner_class.name}##{predicate} is already defined, use #{owner_class.name}##{machine.name}?(:#{name}) instead."
265
+ # Checks whether the current value matches this state
266
+ machine.define_helper(:instance, "#{qualified_name}?") do |machine, object|
267
+ machine.states.matches?(object, name)
276
268
  end
277
269
  end
278
270
  end
@@ -127,8 +127,68 @@ class EventWithDynamicHumanNameTest < Test::Unit::TestCase
127
127
  end
128
128
  end
129
129
 
130
- class EventWithConflictingHelpersTest < Test::Unit::TestCase
130
+ class EventWithConflictingHelpersBeforeDefinitionTest < Test::Unit::TestCase
131
131
  def setup
132
+ require 'stringio'
133
+ @original_stderr, $stderr = $stderr, StringIO.new
134
+
135
+ @superclass = Class.new do
136
+ def can_ignite?
137
+ 0
138
+ end
139
+
140
+ def ignite_transition
141
+ 0
142
+ end
143
+
144
+ def ignite
145
+ 0
146
+ end
147
+
148
+ def ignite!
149
+ 0
150
+ end
151
+ end
152
+ @klass = Class.new(@superclass)
153
+ @machine = StateMachine::Machine.new(@klass)
154
+ @machine.events << @event = StateMachine::Event.new(@machine, :ignite)
155
+ @object = @klass.new
156
+ end
157
+
158
+ def test_should_not_redefine_predicate
159
+ assert_equal 0, @object.can_ignite?
160
+ end
161
+
162
+ def test_should_not_redefine_transition_accessor
163
+ assert_equal 0, @object.ignite_transition
164
+ end
165
+
166
+ def test_should_not_redefine_action
167
+ assert_equal 0, @object.ignite
168
+ end
169
+
170
+ def test_should_not_redefine_bang_action
171
+ assert_equal 0, @object.ignite!
172
+ end
173
+
174
+ def test_should_output_warning
175
+ expected = %w(can_ignite? ignite_transition ignite ignite!).map do |method|
176
+ "Instance method \"#{method}\" is already defined in #{@superclass.to_s}, use generic helper instead.\n"
177
+ end.join
178
+
179
+ assert_equal expected, $stderr.string
180
+ end
181
+
182
+ def teardown
183
+ $stderr = @original_stderr
184
+ end
185
+ end
186
+
187
+ class EventWithConflictingHelpersAfterDefinitionTest < Test::Unit::TestCase
188
+ def setup
189
+ require 'stringio'
190
+ @original_stderr, $stderr = $stderr, StringIO.new
191
+
132
192
  @klass = Class.new do
133
193
  def can_ignite?
134
194
  0
@@ -170,31 +230,34 @@ class EventWithConflictingHelpersTest < Test::Unit::TestCase
170
230
  def test_should_allow_super_chaining
171
231
  @klass.class_eval do
172
232
  def can_ignite?
173
- super ? 1 : 0
233
+ super
174
234
  end
175
235
 
176
236
  def ignite_transition
177
- super ? 1 : 0
237
+ super
178
238
  end
179
239
 
180
240
  def ignite
181
- super ? 1 : 0
241
+ super
182
242
  end
183
243
 
184
244
  def ignite!
185
- begin
186
- super
187
- 1
188
- rescue Exception => ex
189
- 0
190
- end
245
+ super
191
246
  end
192
247
  end
193
248
 
194
- assert_equal 0, @object.can_ignite?
195
- assert_equal 0, @object.ignite_transition
196
- assert_equal 0, @object.ignite
197
- assert_equal 1, @object.ignite!
249
+ assert_equal false, @object.can_ignite?
250
+ assert_equal nil, @object.ignite_transition
251
+ assert_equal false, @object.ignite
252
+ assert_raise(StateMachine::InvalidTransition) { @object.ignite! }
253
+ end
254
+
255
+ def test_should_not_output_warning
256
+ assert_equal '', $stderr.string
257
+ end
258
+
259
+ def teardown
260
+ $stderr = @original_stderr
198
261
  end
199
262
  end
200
263
 
@@ -20,11 +20,11 @@ module SequelTest
20
20
  def new_model(create_table = :foo, &block)
21
21
  table_name = create_table || :foo
22
22
 
23
- DB.create_table!(table_name) do
23
+ DB.create_table!(::Sequel::SQL::Identifier.new(table_name)) do
24
24
  primary_key :id
25
25
  column :state, :string
26
26
  end if create_table
27
- model = Class.new(Sequel::Model(table_name)) do
27
+ model = Class.new(Sequel::Model(DB[::Sequel::SQL::Identifier.new(table_name)])) do
28
28
  self.raise_on_save_failure = false
29
29
  (class << self; self; end).class_eval do
30
30
  define_method(:name) { "SequelTest::#{table_name.to_s.capitalize}" }
@@ -1426,7 +1426,19 @@ module SequelTest
1426
1426
  parked = @model.create :state => 'parked'
1427
1427
  idling = @model.create :state => 'idling'
1428
1428
 
1429
- assert_equal [idling], @model.without_state(:parked).filter(:state => 'idling').all
1429
+ assert_equal [idling], @model.without_state(:parked).with_state(:idling).all
1430
+ end
1431
+
1432
+ def test_should_run_on_tables_with_double_underscores
1433
+ @model = new_model(:foo__bar)
1434
+ @machine = StateMachine::Machine.new(@model)
1435
+ @machine.state :parked, :first_gear
1436
+ @machine.state :idling, :value => lambda {'idling'}
1437
+
1438
+ parked = @model.create :state => 'parked'
1439
+ idling = @model.create :state => 'idling'
1440
+
1441
+ assert_equal [parked], @model.with_state(:parked).all
1430
1442
  end
1431
1443
  end
1432
1444
 
@@ -1114,6 +1114,104 @@ class MachineWithInstanceHelpersTest < Test::Unit::TestCase
1114
1114
  assert_equal 'parked', @object.send(:state)
1115
1115
  end
1116
1116
 
1117
+ def test_should_warn_if_defined_in_superclass
1118
+ require 'stringio'
1119
+ @original_stderr, $stderr = $stderr, StringIO.new
1120
+
1121
+ superclass = Class.new do
1122
+ def park
1123
+ end
1124
+ end
1125
+ klass = Class.new(superclass)
1126
+ machine = StateMachine::Machine.new(klass)
1127
+
1128
+ machine.define_helper(:instance, :park) {}
1129
+ assert_equal "Instance method \"park\" is already defined in #{superclass.to_s}, use generic helper instead.\n", $stderr.string
1130
+ ensure
1131
+ $stderr = @original_stderr
1132
+ end
1133
+
1134
+ def test_should_warn_if_defined_in_multiple_superclasses
1135
+ require 'stringio'
1136
+ @original_stderr, $stderr = $stderr, StringIO.new
1137
+
1138
+ superclass1 = Class.new do
1139
+ def park
1140
+ end
1141
+ end
1142
+ superclass2 = Class.new(superclass1) do
1143
+ def park
1144
+ end
1145
+ end
1146
+ klass = Class.new(superclass2)
1147
+ machine = StateMachine::Machine.new(klass)
1148
+
1149
+ machine.define_helper(:instance, :park) {}
1150
+ assert_equal "Instance method \"park\" is already defined in #{superclass1.to_s}, use generic helper instead.\n", $stderr.string
1151
+ ensure
1152
+ $stderr = @original_stderr
1153
+ end
1154
+
1155
+ def test_should_warn_if_defined_in_module_prior_to_helper_module
1156
+ require 'stringio'
1157
+ @original_stderr, $stderr = $stderr, StringIO.new
1158
+
1159
+ mod = Module.new do
1160
+ def park
1161
+ end
1162
+ end
1163
+ klass = Class.new do
1164
+ include mod
1165
+ end
1166
+ machine = StateMachine::Machine.new(klass)
1167
+
1168
+ machine.define_helper(:instance, :park) {}
1169
+ assert_equal "Instance method \"park\" is already defined in #{mod.to_s}, use generic helper instead.\n", $stderr.string
1170
+ ensure
1171
+ $stderr = @original_stderr
1172
+ end
1173
+
1174
+ def test_should_not_warn_if_defined_in_module_after_helper_module
1175
+ require 'stringio'
1176
+ @original_stderr, $stderr = $stderr, StringIO.new
1177
+
1178
+ klass = Class.new
1179
+ machine = StateMachine::Machine.new(klass)
1180
+
1181
+ mod = Module.new do
1182
+ def park
1183
+ end
1184
+ end
1185
+ klass.class_eval do
1186
+ include mod
1187
+ end
1188
+
1189
+ machine.define_helper(:instance, :park) {}
1190
+ assert_equal '', $stderr.string
1191
+ ensure
1192
+ $stderr = @original_stderr
1193
+ end
1194
+
1195
+ def test_should_define_if_ignoring_method_conflicts_and_defined_in_superclass
1196
+ require 'stringio'
1197
+ @original_stderr, $stderr = $stderr, StringIO.new
1198
+ StateMachine::Machine.ignore_method_conflicts = true
1199
+
1200
+ superclass = Class.new do
1201
+ def park
1202
+ end
1203
+ end
1204
+ klass = Class.new(superclass)
1205
+ machine = StateMachine::Machine.new(klass)
1206
+
1207
+ machine.define_helper(:instance, :park) {true}
1208
+ assert_equal '', $stderr.string
1209
+ assert_equal true, klass.new.park
1210
+ ensure
1211
+ StateMachine::Machine.ignore_method_conflicts = false
1212
+ $stderr = @original_stderr
1213
+ end
1214
+
1117
1215
  def test_should_define_nonexistent_methods
1118
1216
  @machine.define_helper(:instance, :state) {'parked'}
1119
1217
  assert_equal 'parked', @object.state
@@ -1186,6 +1284,104 @@ class MachineWithClassHelpersTest < Test::Unit::TestCase
1186
1284
  assert_equal [], @klass.send(:states)
1187
1285
  end
1188
1286
 
1287
+ def test_should_warn_if_defined_in_superclass
1288
+ require 'stringio'
1289
+ @original_stderr, $stderr = $stderr, StringIO.new
1290
+
1291
+ superclass = Class.new do
1292
+ def self.park
1293
+ end
1294
+ end
1295
+ klass = Class.new(superclass)
1296
+ machine = StateMachine::Machine.new(klass)
1297
+
1298
+ machine.define_helper(:class, :park) {}
1299
+ assert_equal "Class method \"park\" is already defined in #{superclass.to_s}, use generic helper instead.\n", $stderr.string
1300
+ ensure
1301
+ $stderr = @original_stderr
1302
+ end
1303
+
1304
+ def test_should_warn_if_defined_in_multiple_superclasses
1305
+ require 'stringio'
1306
+ @original_stderr, $stderr = $stderr, StringIO.new
1307
+
1308
+ superclass1 = Class.new do
1309
+ def self.park
1310
+ end
1311
+ end
1312
+ superclass2 = Class.new(superclass1) do
1313
+ def self.park
1314
+ end
1315
+ end
1316
+ klass = Class.new(superclass2)
1317
+ machine = StateMachine::Machine.new(klass)
1318
+
1319
+ machine.define_helper(:class, :park) {}
1320
+ assert_equal "Class method \"park\" is already defined in #{superclass1.to_s}, use generic helper instead.\n", $stderr.string
1321
+ ensure
1322
+ $stderr = @original_stderr
1323
+ end
1324
+
1325
+ def test_should_warn_if_defined_in_module_prior_to_helper_module
1326
+ require 'stringio'
1327
+ @original_stderr, $stderr = $stderr, StringIO.new
1328
+
1329
+ mod = Module.new do
1330
+ def park
1331
+ end
1332
+ end
1333
+ klass = Class.new do
1334
+ extend mod
1335
+ end
1336
+ machine = StateMachine::Machine.new(klass)
1337
+
1338
+ machine.define_helper(:class, :park) {}
1339
+ assert_equal "Class method \"park\" is already defined in #{mod.to_s}, use generic helper instead.\n", $stderr.string
1340
+ ensure
1341
+ $stderr = @original_stderr
1342
+ end
1343
+
1344
+ def test_should_not_warn_if_defined_in_module_after_helper_module
1345
+ require 'stringio'
1346
+ @original_stderr, $stderr = $stderr, StringIO.new
1347
+
1348
+ klass = Class.new
1349
+ machine = StateMachine::Machine.new(klass)
1350
+
1351
+ mod = Module.new do
1352
+ def park
1353
+ end
1354
+ end
1355
+ klass.class_eval do
1356
+ extend mod
1357
+ end
1358
+
1359
+ machine.define_helper(:class, :park) {}
1360
+ assert_equal '', $stderr.string
1361
+ ensure
1362
+ $stderr = @original_stderr
1363
+ end
1364
+
1365
+ def test_should_define_if_ignoring_method_conflicts_and_defined_in_superclass
1366
+ require 'stringio'
1367
+ @original_stderr, $stderr = $stderr, StringIO.new
1368
+ StateMachine::Machine.ignore_method_conflicts = true
1369
+
1370
+ superclass = Class.new do
1371
+ def self.park
1372
+ end
1373
+ end
1374
+ klass = Class.new(superclass)
1375
+ machine = StateMachine::Machine.new(klass)
1376
+
1377
+ machine.define_helper(:class, :park) {true}
1378
+ assert_equal '', $stderr.string
1379
+ assert_equal true, klass.park
1380
+ ensure
1381
+ StateMachine::Machine.ignore_method_conflicts = false
1382
+ $stderr = @original_stderr
1383
+ end
1384
+
1189
1385
  def test_should_define_nonexistent_methods
1190
1386
  @machine.define_helper(:class, :states) {[]}
1191
1387
  assert_equal [], @klass.states
@@ -1217,8 +1413,176 @@ class MachineWithClassHelpersTest < Test::Unit::TestCase
1217
1413
  end
1218
1414
  end
1219
1415
 
1220
- class MachineWithConflictingHelpersTest < Test::Unit::TestCase
1416
+ class MachineWithConflictingHelpersBeforeDefinitionTest < Test::Unit::TestCase
1417
+ def setup
1418
+ require 'stringio'
1419
+ @original_stderr, $stderr = $stderr, StringIO.new
1420
+
1421
+ @superclass = Class.new do
1422
+ def self.with_state
1423
+ :with_state
1424
+ end
1425
+
1426
+ def self.with_states
1427
+ :with_states
1428
+ end
1429
+
1430
+ def self.without_state
1431
+ :without_state
1432
+ end
1433
+
1434
+ def self.without_states
1435
+ :without_states
1436
+ end
1437
+
1438
+ def self.human_state_name
1439
+ :human_state_name
1440
+ end
1441
+
1442
+ def self.human_state_event_name
1443
+ :human_state_event_name
1444
+ end
1445
+
1446
+ attr_accessor :status
1447
+
1448
+ def state
1449
+ 'parked'
1450
+ end
1451
+
1452
+ def state=(value)
1453
+ self.status = value
1454
+ end
1455
+
1456
+ def state?
1457
+ true
1458
+ end
1459
+
1460
+ def state_name
1461
+ :parked
1462
+ end
1463
+
1464
+ def human_state_name
1465
+ 'parked'
1466
+ end
1467
+
1468
+ def state_events
1469
+ [:ignite]
1470
+ end
1471
+
1472
+ def state_transitions
1473
+ [{:parked => :idling}]
1474
+ end
1475
+
1476
+ def state_paths
1477
+ [[{:parked => :idling}]]
1478
+ end
1479
+ end
1480
+ @klass = Class.new(@superclass)
1481
+
1482
+ StateMachine::Integrations.const_set('Custom', Module.new do
1483
+ include StateMachine::Integrations::Base
1484
+
1485
+ def create_with_scope(name)
1486
+ lambda {|klass, values| []}
1487
+ end
1488
+
1489
+ def create_without_scope(name)
1490
+ lambda {|klass, values| []}
1491
+ end
1492
+ end)
1493
+
1494
+ @machine = StateMachine::Machine.new(@klass, :integration => :custom)
1495
+ @machine.state :parked, :idling
1496
+ @machine.event :ignite
1497
+ @object = @klass.new
1498
+ end
1499
+
1500
+ def test_should_not_redefine_singular_with_scope
1501
+ assert_equal :with_state, @klass.with_state
1502
+ end
1503
+
1504
+ def test_should_not_redefine_plural_with_scope
1505
+ assert_equal :with_states, @klass.with_states
1506
+ end
1507
+
1508
+ def test_should_not_redefine_singular_without_scope
1509
+ assert_equal :without_state, @klass.without_state
1510
+ end
1511
+
1512
+ def test_should_not_redefine_plural_without_scope
1513
+ assert_equal :without_states, @klass.without_states
1514
+ end
1515
+
1516
+ def test_should_not_redefine_human_attribute_name_reader
1517
+ assert_equal :human_state_name, @klass.human_state_name
1518
+ end
1519
+
1520
+ def test_should_not_redefine_human_event_name_reader
1521
+ assert_equal :human_state_event_name, @klass.human_state_event_name
1522
+ end
1523
+
1524
+ def test_should_not_redefine_attribute_writer
1525
+ assert_equal 'parked', @object.state
1526
+ end
1527
+
1528
+ def test_should_not_redefine_attribute_writer
1529
+ @object.state = 'parked'
1530
+ assert_equal 'parked', @object.status
1531
+ end
1532
+
1533
+ def test_should_not_define_attribute_predicate
1534
+ assert @object.state?
1535
+ end
1536
+
1537
+ def test_should_not_redefine_attribute_name_reader
1538
+ assert_equal :parked, @object.state_name
1539
+ end
1540
+
1541
+ def test_should_not_redefine_attribute_human_name_reader
1542
+ assert_equal 'parked', @object.human_state_name
1543
+ end
1544
+
1545
+ def test_should_not_redefine_attribute_events_reader
1546
+ assert_equal [:ignite], @object.state_events
1547
+ end
1548
+
1549
+ def test_should_not_redefine_attribute_transitions_reader
1550
+ assert_equal [{:parked => :idling}], @object.state_transitions
1551
+ end
1552
+
1553
+ def test_should_not_redefine_attribute_paths_reader
1554
+ assert_equal [[{:parked => :idling}]], @object.state_paths
1555
+ end
1556
+
1557
+ def test_should_output_warning
1558
+ expected = [
1559
+ 'Instance method "state_events"',
1560
+ 'Instance method "state_transitions"',
1561
+ 'Instance method "state_paths"',
1562
+ 'Class method "human_state_name"',
1563
+ 'Class method "human_state_event_name"',
1564
+ 'Instance method "state_name"',
1565
+ 'Instance method "human_state_name"',
1566
+ 'Class method "with_state"',
1567
+ 'Class method "without_state"',
1568
+ 'Class method "with_states"',
1569
+ 'Class method "without_states"'
1570
+ ].map {|method| "#{method} is already defined in #{@superclass.to_s}, use generic helper instead.\n"}.join
1571
+
1572
+ assert_equal expected, $stderr.string
1573
+ end
1574
+
1575
+ def teardown
1576
+ $stderr = @original_stderr
1577
+ StateMachine::Integrations.send(:remove_const, 'Custom')
1578
+ end
1579
+ end
1580
+
1581
+ class MachineWithConflictingHelpersAfterDefinitionTest < Test::Unit::TestCase
1221
1582
  def setup
1583
+ require 'stringio'
1584
+ @original_stderr, $stderr = $stderr, StringIO.new
1585
+
1222
1586
  @klass = Class.new do
1223
1587
  def self.with_state
1224
1588
  :with_state
@@ -1357,84 +1721,89 @@ class MachineWithConflictingHelpersTest < Test::Unit::TestCase
1357
1721
  def test_should_allow_super_chaining
1358
1722
  @klass.class_eval do
1359
1723
  def self.with_state(*states)
1360
- super == []
1724
+ super
1361
1725
  end
1362
1726
 
1363
1727
  def self.with_states(*states)
1364
- super == []
1728
+ super
1365
1729
  end
1366
1730
 
1367
1731
  def self.without_state(*states)
1368
- super == []
1732
+ super
1369
1733
  end
1370
1734
 
1371
1735
  def self.without_states(*states)
1372
- super == []
1736
+ super
1373
1737
  end
1374
1738
 
1375
1739
  def self.human_state_name(state)
1376
- super == 'parked'
1740
+ super
1377
1741
  end
1378
1742
 
1379
1743
  def self.human_state_event_name(event)
1380
- super == 'ignite'
1744
+ super
1381
1745
  end
1382
1746
 
1383
1747
  attr_accessor :status
1384
1748
 
1385
1749
  def state
1386
- super || 'parked'
1750
+ super
1387
1751
  end
1388
1752
 
1389
1753
  def state=(value)
1390
1754
  super
1391
- self.status = value
1392
1755
  end
1393
1756
 
1394
1757
  def state?(state)
1395
- super ? 1 : 0
1758
+ super
1396
1759
  end
1397
1760
 
1398
1761
  def state_name
1399
- super == :parked ? 1 : 0
1762
+ super
1400
1763
  end
1401
1764
 
1402
1765
  def human_state_name
1403
- super == 'parked' ? 1 : 0
1766
+ super
1404
1767
  end
1405
1768
 
1406
1769
  def state_events
1407
- super == []
1770
+ super
1408
1771
  end
1409
1772
 
1410
1773
  def state_transitions
1411
- super == []
1774
+ super
1412
1775
  end
1413
1776
 
1414
1777
  def state_paths
1415
- super == []
1778
+ super
1416
1779
  end
1417
1780
  end
1418
1781
 
1419
- assert_equal true, @klass.with_state
1420
- assert_equal true, @klass.with_states
1421
- assert_equal true, @klass.without_state
1422
- assert_equal true, @klass.without_states
1423
- assert_equal true, @klass.human_state_name(:parked)
1424
- assert_equal true, @klass.human_state_event_name(:ignite)
1782
+ assert_equal [], @klass.with_state
1783
+ assert_equal [], @klass.with_states
1784
+ assert_equal [], @klass.without_state
1785
+ assert_equal [], @klass.without_states
1786
+ assert_equal 'parked', @klass.human_state_name(:parked)
1787
+ assert_equal 'ignite', @klass.human_state_event_name(:ignite)
1425
1788
 
1426
- assert_equal 'parked', @object.state
1789
+ assert_equal nil, @object.state
1427
1790
  @object.state = 'idling'
1428
- assert_equal 'idling', @object.status
1429
- assert_equal 0, @object.state?(:parked)
1430
- assert_equal 0, @object.state_name
1431
- assert_equal 0, @object.human_state_name
1432
- assert_equal true, @object.state_events
1433
- assert_equal true, @object.state_transitions
1434
- assert_equal true, @object.state_paths
1791
+ assert_equal 'idling', @object.state
1792
+ assert_equal nil, @object.status
1793
+ assert_equal false, @object.state?(:parked)
1794
+ assert_equal :idling, @object.state_name
1795
+ assert_equal 'idling', @object.human_state_name
1796
+ assert_equal [], @object.state_events
1797
+ assert_equal [], @object.state_transitions
1798
+ assert_equal [], @object.state_paths
1799
+ end
1800
+
1801
+ def test_should_not_output_warning
1802
+ assert_equal '', $stderr.string
1435
1803
  end
1436
1804
 
1437
1805
  def teardown
1806
+ $stderr = @original_stderr
1438
1807
  StateMachine::Integrations.send(:remove_const, 'Custom')
1439
1808
  end
1440
1809
  end
@@ -458,7 +458,36 @@ class StateNotFinalTest < Test::Unit::TestCase
458
458
  end
459
459
  end
460
460
 
461
- class StateWithConflictingHelpersTest < Test::Unit::TestCase
461
+ class StateWithConflictingHelpersBeforeDefinitionTest < Test::Unit::TestCase
462
+ def setup
463
+ require 'stringio'
464
+ @original_stderr, $stderr = $stderr, StringIO.new
465
+
466
+ @superclass = Class.new do
467
+ def parked?
468
+ 0
469
+ end
470
+ end
471
+ @klass = Class.new(@superclass)
472
+ @machine = StateMachine::Machine.new(@klass)
473
+ @machine.state :parked
474
+ @object = @klass.new
475
+ end
476
+
477
+ def test_should_not_override_state_predicate
478
+ assert_equal 0, @object.parked?
479
+ end
480
+
481
+ def test_should_output_warning
482
+ assert_equal "Instance method \"parked?\" is already defined in #{@superclass.to_s}, use generic helper instead.\n", $stderr.string
483
+ end
484
+
485
+ def teardown
486
+ $stderr = @original_stderr
487
+ end
488
+ end
489
+
490
+ class StateWithConflictingHelpersAfterDefinitionTest < Test::Unit::TestCase
462
491
  def setup
463
492
  require 'stringio'
464
493
  @original_stderr, $stderr = $stderr, StringIO.new
@@ -477,18 +506,18 @@ class StateWithConflictingHelpersTest < Test::Unit::TestCase
477
506
  assert_equal 0, @object.parked?
478
507
  end
479
508
 
480
- def test_should_not_allow_super_chaining
509
+ def test_should_still_allow_super_chaining
481
510
  @klass.class_eval do
482
511
  def parked?
483
- super ? 1 : 0
512
+ super
484
513
  end
485
514
  end
486
515
 
487
- assert_raise(NoMethodError) { @object.parked? }
516
+ assert_equal false, @object.parked?
488
517
  end
489
518
 
490
- def test_should_output_warning
491
- assert_equal "#parked? is already defined, use #state?(:parked) instead.\n", $stderr.string
519
+ def test_should_not_output_warning
520
+ assert_equal '', $stderr.string
492
521
  end
493
522
 
494
523
  def teardown
@@ -496,7 +525,7 @@ class StateWithConflictingHelpersTest < Test::Unit::TestCase
496
525
  end
497
526
  end
498
527
 
499
- class EventWithConflictingMachineTest < Test::Unit::TestCase
528
+ class StateWithConflictingMachineTest < Test::Unit::TestCase
500
529
  def setup
501
530
  require 'stringio'
502
531
  @original_stderr, $stderr = $stderr, StringIO.new
@@ -1508,7 +1508,7 @@ class TransitionEqualityTest < Test::Unit::TestCase
1508
1508
  end
1509
1509
 
1510
1510
  def test_should_not_be_equal_with_different_machines
1511
- machine = StateMachine::Machine.new(@klass, :namespace => :other)
1511
+ machine = StateMachine::Machine.new(@klass, :status, :namespace => :other)
1512
1512
  machine.state :parked, :idling
1513
1513
  machine.event :ignite
1514
1514
  transition = StateMachine::Transition.new(@object, machine, :ignite, :parked, :idling)
metadata CHANGED
@@ -1,33 +1,23 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: state_machine
3
- version: !ruby/object:Gem::Version
4
- hash: 23
5
- prerelease: false
6
- segments:
7
- - 1
8
- - 0
9
- - 0
10
- version: 1.0.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Aaron Pfeifer
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-05-12 00:00:00 -04:00
12
+ date: 2011-05-30 00:00:00.000000000 -04:00
19
13
  default_executable:
20
14
  dependencies: []
21
-
22
15
  description: Adds support for creating state machines for attributes on any Ruby class
23
16
  email: aaron@pluginaweek.org
24
17
  executables: []
25
-
26
18
  extensions: []
27
-
28
19
  extra_rdoc_files: []
29
-
30
- files:
20
+ files:
31
21
  - examples/car.rb
32
22
  - examples/TrafficLight_state.png
33
23
  - examples/vehicle.rb
@@ -138,38 +128,29 @@ files:
138
128
  has_rdoc: true
139
129
  homepage: http://www.pluginaweek.org
140
130
  licenses: []
141
-
142
131
  post_install_message:
143
132
  rdoc_options: []
144
-
145
- require_paths:
133
+ require_paths:
146
134
  - lib
147
- required_ruby_version: !ruby/object:Gem::Requirement
135
+ required_ruby_version: !ruby/object:Gem::Requirement
148
136
  none: false
149
- requirements:
150
- - - ">="
151
- - !ruby/object:Gem::Version
152
- hash: 3
153
- segments:
154
- - 0
155
- version: "0"
156
- required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ! '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
157
142
  none: false
158
- requirements:
159
- - - ">="
160
- - !ruby/object:Gem::Version
161
- hash: 3
162
- segments:
163
- - 0
164
- version: "0"
143
+ requirements:
144
+ - - ! '>='
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
165
147
  requirements: []
166
-
167
148
  rubyforge_project: pluginaweek
168
- rubygems_version: 1.3.7
149
+ rubygems_version: 1.6.2
169
150
  signing_key:
170
151
  specification_version: 3
171
152
  summary: Adds support for creating state machines for attributes on any Ruby class
172
- test_files:
153
+ test_files:
173
154
  - test/unit/path_test.rb
174
155
  - test/unit/condition_proxy_test.rb
175
156
  - test/unit/state_machine_test.rb