Skip to content
This repository
Browse code

Simplify availability / class matching implementation for integrations

  • Loading branch information...
commit f95d15f64af57cf5904bf8da291e94cb682e052d 1 parent 25f6431
Aaron Pfeifer obrie authored
14 lib/state_machine/integrations.rb
@@ -73,7 +73,19 @@ module Integrations
73 73 # StateMachine::Integrations.match(MongoMapperVehicle) # => StateMachine::Integrations::MongoMapper
74 74 # StateMachine::Integrations.match(SequelVehicle) # => StateMachine::Integrations::Sequel
75 75 def self.match(klass)
76   - all.detect {|integration| integration.available? && integration.matches?(klass)}
  76 + all.detect {|integration| integration.matches?(klass)}
  77 + end
  78 +
  79 + # Attempts to find an integration that matches the given list of ancestors.
  80 + # This will look through all of the built-in integrations under the StateMachine::Integrations
  81 + # namespace and find one that successfully matches one of the ancestors.
  82 + #
  83 + # == Examples
  84 + #
  85 + # StateMachine::Integrations.match([]) # => nil
  86 + # StateMachine::Integrations.match(['ActiveRecord::Base') # => StateMachine::Integrations::ActiveModel
  87 + def self.match_ancestors(ancestors)
  88 + all.detect {|integration| integration.matches_ancestors?(ancestors)}
77 89 end
78 90
79 91 # Finds an integration with the given name. If the integration cannot be
11 lib/state_machine/integrations/active_model.rb
@@ -366,17 +366,10 @@ def self.included(base) #:nodoc:
366 366
367 367 @defaults = {}
368 368
369   - # Whether this integration is available. Only true if ActiveModel is
370   - # defined.
371   - def self.available?
372   - defined?(::ActiveModel)
373   - end
374   -
375   - # Should this integration be used for state machines in the given class?
376 369 # Classes that include ActiveModel::Observing or ActiveModel::Validations
377 370 # will automatically use the ActiveModel integration.
378   - def self.matches?(klass)
379   - %w(Observing Validations).any? {|feature| ::ActiveModel.const_defined?(feature) && klass <= ::ActiveModel.const_get(feature)}
  371 + def self.matching_ancestors
  372 + %w(ActiveModel ActiveModel::Observing ActiveModel::Validations)
380 373 end
381 374
382 375 # Adds a validation error to the given object
11 lib/state_machine/integrations/active_record.rb
@@ -407,17 +407,10 @@ module ActiveRecord
407 407 # The default options to use for state machines using this integration
408 408 @defaults = {:action => :save}
409 409
410   - # Whether this integration is available. Only true if ActiveRecord::Base
411   - # is defined.
412   - def self.available?
413   - defined?(::ActiveRecord::Base)
414   - end
415   -
416   - # Should this integration be used for state machines in the given class?
417 410 # Classes that inherit from ActiveRecord::Base will automatically use
418 411 # the ActiveRecord integration.
419   - def self.matches?(klass)
420   - klass <= ::ActiveRecord::Base
  412 + def self.matching_ancestors
  413 + %w(ActiveRecord::Base)
421 414 end
422 415
423 416 def self.extended(base) #:nodoc:
21 lib/state_machine/integrations/base.rb
@@ -18,16 +18,25 @@ def integration_name
18 18 end
19 19
20 20 # Whether this integration is available for the current library. This
21   - # is usually only true if the ORM that the integration is for is
22   - # currently defined. Default is false.
  21 + # is only true if the ORM that the integration is for is currently
  22 + # defined.
23 23 def available?
24   - false
  24 + matching_ancestors.any? && Object.const_defined?(matching_ancestors[0].split('::')[0])
25 25 end
26 26
27   - # Whether the integration should be used for the given class. Default
28   - # is false.
  27 + # The list of ancestor names that cause this integration to matched.
  28 + def matching_ancestors
  29 + []
  30 + end
  31 +
  32 + # Whether the integration should be used for the given class.
29 33 def matches?(klass)
30   - false
  34 + matches_ancestors?(klass.ancestors.map(&:name))
  35 + end
  36 +
  37 + # Whether the integration should be used for the given list of ancestors.
  38 + def matches_ancestors?(ancestors)
  39 + (ancestors & matching_ancestors).any?
31 40 end
32 41
33 42 # Tracks the various version overrides for an integration
12 lib/state_machine/integrations/data_mapper.rb
@@ -308,20 +308,12 @@ module DataMapper
308 308 require 'state_machine/integrations/data_mapper/versions'
309 309
310 310 # The default options to use for state machines using this integration
311   - class << self; attr_reader :defaults; end
312 311 @defaults = {:action => :save, :use_transactions => false}
313 312
314   - # Whether this integration is available. Only true if DataMapper::Resource
315   - # is defined.
316   - def self.available?
317   - defined?(::DataMapper::Resource)
318   - end
319   -
320   - # Should this integration be used for state machines in the given class?
321 313 # Classes that include DataMapper::Resource will automatically use the
322 314 # DataMapper integration.
323   - def self.matches?(klass)
324   - klass <= ::DataMapper::Resource
  315 + def self.matching_ancestors
  316 + %w(DataMapper::Resource)
325 317 end
326 318
327 319 # Loads additional files specific to DataMapper
11 lib/state_machine/integrations/mongo_mapper.rb
@@ -291,17 +291,10 @@ module MongoMapper
291 291 # The default options to use for state machines using this integration
292 292 @defaults = {:action => :save}
293 293
294   - # Whether this integration is available. Only true if MongoMapper::Document
295   - # is defined.
296   - def self.available?
297   - defined?(::MongoMapper::Document)
298   - end
299   -
300   - # Should this integration be used for state machines in the given class?
301 294 # Classes that include MongoMapper::Document will automatically use the
302 295 # MongoMapper integration.
303   - def self.matches?(klass)
304   - klass <= ::MongoMapper::Document
  296 + def self.matching_ancestors
  297 + %w(MongoMapper::Document)
305 298 end
306 299
307 300 protected
11 lib/state_machine/integrations/mongoid.rb
@@ -341,17 +341,10 @@ module Mongoid
341 341 # The default options to use for state machines using this integration
342 342 @defaults = {:action => :save}
343 343
344   - # Whether this integration is available. Only true if Mongoid::Document
345   - # is defined.
346   - def self.available?
347   - defined?(::Mongoid::Document)
348   - end
349   -
350   - # Should this integration be used for state machines in the given class?
351 344 # Classes that include Mongoid::Document will automatically use the
352 345 # Mongoid integration.
353   - def self.matches?(klass)
354   - klass <= ::Mongoid::Document
  346 + def self.matching_ancestors
  347 + %w(Mongoid::Document)
355 348 end
356 349
357 350 def self.extended(base) #:nodoc:
12 lib/state_machine/integrations/sequel.rb
@@ -271,20 +271,12 @@ module Sequel
271 271 require 'state_machine/integrations/sequel/versions'
272 272
273 273 # The default options to use for state machines using this integration
274   - class << self; attr_reader :defaults; end
275 274 @defaults = {:action => :save}
276 275
277   - # Whether this integration is available. Only true if Sequel::Model is
278   - # defined.
279   - def self.available?
280   - defined?(::Sequel::Model)
281   - end
282   -
283   - # Should this integration be used for state machines in the given class?
284 276 # Classes that include Sequel::Model will automatically use the Sequel
285 277 # integration.
286   - def self.matches?(klass)
287   - klass <= ::Sequel::Model
  278 + def self.matching_ancestors
  279 + %w(Sequel::Model)
288 280 end
289 281
290 282 # Forces the change in state to be recognized regardless of whether the
4 test/unit/integrations/base_test.rb
@@ -13,6 +13,10 @@ def test_should_not_be_available
13 13 assert !StateMachine::Integrations::Base.available?
14 14 end
15 15
  16 + def test_should_not_have_any_matching_ancestors
  17 + assert_equal [], StateMachine::Integrations::Base.matching_ancestors
  18 + end
  19 +
16 20 def test_should_not_match_any_classes
17 21 assert !StateMachine::Integrations::Base.matches?(Class.new)
18 22 end
34 test/unit/integrations_test.rb
@@ -2,7 +2,10 @@
2 2
3 3 class IntegrationMatcherTest < Test::Unit::TestCase
4 4 def setup
5   - @klass = Class.new
  5 + superclass = Class.new
  6 + self.class.const_set('Vehicle', superclass)
  7 +
  8 + @klass = Class.new(superclass)
6 9 end
7 10
8 11 def test_should_return_nil_if_no_match_found
@@ -13,20 +16,39 @@ def test_should_return_integration_class_if_match_found
13 16 integration = Module.new do
14 17 include StateMachine::Integrations::Base
15 18
16   - def self.available?
17   - true
  19 + def self.matching_ancestors
  20 + ['IntegrationMatcherTest::Vehicle']
18 21 end
  22 + end
  23 + StateMachine::Integrations.const_set('Custom', integration)
  24 +
  25 + assert_equal integration, StateMachine::Integrations.match(@klass)
  26 + ensure
  27 + StateMachine::Integrations.send(:remove_const, 'Custom')
  28 + end
  29 +
  30 + def test_should_return_nil_if_no_match_found_with_ancestors
  31 + assert_nil StateMachine::Integrations.match_ancestors(['IntegrationMatcherTest::Fake'])
  32 + end
  33 +
  34 + def test_should_return_integration_class_if_match_found_with_ancestors
  35 + integration = Module.new do
  36 + include StateMachine::Integrations::Base
19 37
20   - def self.matches?(klass)
21   - true
  38 + def self.matching_ancestors
  39 + ['IntegrationMatcherTest::Vehicle']
22 40 end
23 41 end
24 42 StateMachine::Integrations.const_set('Custom', integration)
25 43
26   - assert_equal integration, StateMachine::Integrations.match(@klass)
  44 + assert_equal integration, StateMachine::Integrations.match_ancestors(['IntegrationMatcherTest::Fake', 'IntegrationMatcherTest::Vehicle'])
27 45 ensure
28 46 StateMachine::Integrations.send(:remove_const, 'Custom')
29 47 end
  48 +
  49 + def teardown
  50 + self.class.send(:remove_const, 'Vehicle')
  51 + end
30 52 end
31 53
32 54 class IntegrationFinderTest < Test::Unit::TestCase
34 test/unit/machine_test.rb
@@ -511,61 +511,63 @@ def setup
511 511 integration = Module.new do
512 512 include StateMachine::Integrations::Base
513 513
514   - def self.available?
515   - true
516   - end
517   -
518   - def self.matches?(klass)
519   - true
  514 + def self.matching_ancestors
  515 + ['MachineWithCustomIntegrationTest::Vehicle']
520 516 end
521 517 end
522 518
523 519 StateMachine::Integrations.const_set('Custom', integration)
  520 +
  521 + superclass = Class.new
  522 + self.class.const_set('Vehicle', superclass)
  523 +
  524 + @klass = Class.new(superclass)
524 525 end
525 526
526 527 def test_should_be_extended_by_the_integration_if_explicit
527   - machine = StateMachine::Machine.new(Class.new, :integration => :custom)
  528 + machine = StateMachine::Machine.new(@klass, :integration => :custom)
528 529 assert (class << machine; ancestors; end).include?(StateMachine::Integrations::Custom)
529 530 end
530 531
531 532 def test_should_not_be_extended_by_the_integration_if_implicit_but_not_available
532 533 StateMachine::Integrations::Custom.class_eval do
533   - def self.available?
534   - false
  534 + def self.matching_ancestors
  535 + []
535 536 end
536 537 end
537 538
538   - machine = StateMachine::Machine.new(Class.new)
  539 + machine = StateMachine::Machine.new(@klass)
539 540 assert !(class << machine; ancestors; end).include?(StateMachine::Integrations::Custom)
540 541 end
541 542
542 543 def test_should_not_be_extended_by_the_integration_if_implicit_but_not_matched
543 544 StateMachine::Integrations::Custom.class_eval do
544   - def self.matches?(klass)
545   - false
  545 + def self.matching_ancestors
  546 + []
546 547 end
547 548 end
548 549
549   - machine = StateMachine::Machine.new(Class.new)
  550 + machine = StateMachine::Machine.new(@klass)
550 551 assert !(class << machine; ancestors; end).include?(StateMachine::Integrations::Custom)
551 552 end
552 553
553 554 def test_should_be_extended_by_the_integration_if_implicit_and_available_and_matches
554   - machine = StateMachine::Machine.new(Class.new)
  555 + machine = StateMachine::Machine.new(@klass)
555 556 assert (class << machine; ancestors; end).include?(StateMachine::Integrations::Custom)
556 557 end
557 558
558 559 def test_should_not_be_extended_by_the_integration_if_nil
559   - machine = StateMachine::Machine.new(Class.new, :integration => nil)
  560 + machine = StateMachine::Machine.new(@klass, :integration => nil)
560 561 assert !(class << machine; ancestors; end).include?(StateMachine::Integrations::Custom)
561 562 end
562 563
563 564 def test_should_not_be_extended_by_the_integration_if_false
564   - machine = StateMachine::Machine.new(Class.new, :integration => false)
  565 + machine = StateMachine::Machine.new(@klass, :integration => false)
565 566 assert !(class << machine; ancestors; end).include?(StateMachine::Integrations::Custom)
566 567 end
567 568
568 569 def teardown
  570 + self.class.send(:remove_const, 'Vehicle')
569 571 StateMachine::Integrations.send(:remove_const, 'Custom')
570 572 end
571 573 end

0 comments on commit f95d15f

Please sign in to comment.
Something went wrong with that request. Please try again.