Skip to content

Commit

Permalink
Add compatibility with Ruby 1.9+
Browse files Browse the repository at this point in the history
  • Loading branch information
obrie committed Dec 31, 2008
1 parent ba6dfeb commit 670ac78
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 21 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rdoc
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
== master

* Add compatibility with Ruby 1.9+

== 0.4.3 / 2008-12-28

* Allow dm-observer integration to be optional
Expand Down
3 changes: 2 additions & 1 deletion lib/state_machine/eval_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ def evaluate_method(object, method, *args)
when String
eval(method, object.instance_eval {binding})
when Proc, Method
method.arity == 1 ? method.call(object) : method.call(object, *args)
args.unshift(object)
[0, 1].include?(method.arity) ? method.call(*args.slice(0, method.arity)) : method.call(*args)
else
raise ArgumentError, 'Methods must be a symbol denoting the method to call, a string to be evaluated, or a block to be invoked'
end
Expand Down
14 changes: 8 additions & 6 deletions lib/state_machine/integrations/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@ def self.matches?(klass)
defined?(::ActiveRecord::Base) && klass <= ::ActiveRecord::Base
end

# Loads additional files specific to ActiveRecord
def self.extended(base) #:nodoc:
require 'state_machine/integrations/active_record/observer'
end

# Runs a new database transaction, rolling back any changes by raising
# an ActiveRecord::Rollback exception if the yielded block fails
# (i.e. returns false).
Expand Down Expand Up @@ -199,7 +204,7 @@ def define_attribute_accessor
define_method("#{attribute}?") do |*args|
if args.empty?
# No arguments: querying for presence of the attribute
super
super(*args)
else
# Arguments: querying for the attribute's current value
state = args.first
Expand Down Expand Up @@ -251,11 +256,8 @@ def add_callback(type, options, &block)
def notify(type, object, transition)
qualified_event = namespace ? "#{transition.event}_#{namespace}" : transition.event
["#{type}_#{qualified_event}", "#{type}_transition"].each do |method|
object.class.class_eval do
@observer_peers.dup.each do |observer|
observer.send(method, object, transition) if observer.respond_to?(method)
end if defined?(@observer_peers)
end
object.class.changed
object.class.notify_observers(method, object, transition)
end

true
Expand Down
41 changes: 41 additions & 0 deletions lib/state_machine/integrations/active_record/observer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module StateMachine
module Integrations #:nodoc:
module ActiveRecord
# Adds support for invoking callbacks on ActiveRecord observers with more
# than one argument (e.g. the record *and* the state transition). By
# default, ActiveRecord only supports passing the record into the
# callbacks.
#
# For example:
#
# class VehicleObserver < ActiveRecord::Observer
# # The default behavior: only pass in the record
# def after_save(vehicle)
# end
#
# # Custom behavior: allow the transition to be passed in as well
# def after_transition(vehicle, transition)
# Audit.log(vehicle, transition)
# end
# end
module Observer
def self.included(base) #:nodoc:
base.class_eval do
alias_method :update_without_multiple_args, :update
alias_method :update, :update_with_multiple_args
end
end

# Allows additional arguments other than the object to be passed to the
# observed methods
def update_with_multiple_args(observed_method, object, *args) #:nodoc:
send(observed_method, object, *args) if respond_to?(observed_method)
end
end
end
end
end

ActiveRecord::Observer.class_eval do
include StateMachine::Integrations::ActiveRecord::Observer
end
6 changes: 3 additions & 3 deletions lib/state_machine/state.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,14 @@ def context(&block)
owner_class.class_eval <<-end_eval, __FILE__, __LINE__
def #{method}(*args, &block)
attribute = #{attribute.dump}
self.class.state_machines[attribute].state(send(attribute)).call(self, #{method.dump}, *args, &block)
self.class.state_machines[attribute].state(send(attribute)).call(self, #{method.to_s.dump}, *args, &block)
end
end_eval
end

# Track the method defined for the context so that it can be invoked
# at a later point in time
methods[method] = context.instance_method(method)
methods[method.to_sym] = context.instance_method(method)
end

# Include the context so that it can be bound to the owner class (the
Expand All @@ -121,7 +121,7 @@ def #{method}(*args, &block)
# If the method has never been defined for this state, then a NoMethodError
# will be raised.
def call(object, method, *args, &block)
if context_method = methods[method.to_s]
if context_method = methods[method.to_sym]
# Method is defined by the state: proxy it through
context_method.bind(object).call(*args, &block)
else
Expand Down
18 changes: 18 additions & 0 deletions test/unit/eval_helpers_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,24 @@ def test_should_call_proc_with_object_as_argument
end
end

class EvalHelperProcWithoutArgumentsTest < Test::Unit::TestCase
include StateMachine::EvalHelpers

def setup
@object = Object.new
@proc = lambda {|*args| args}
class << @proc
def arity
0
end
end
end

def test_should_call_proc_with_no_arguments
assert_equal [], evaluate_method(@object, @proc, 1, 2, 3)
end
end

class EvalHelperProcWithArgumentsTest < Test::Unit::TestCase
include StateMachine::EvalHelpers

Expand Down
2 changes: 1 addition & 1 deletion test/unit/event_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -433,5 +433,5 @@ def test_should_use_event_name_for_edge_label
end
end
rescue LoadError
$stderr.puts 'Skipping GraphViz StateMachine::Guard tests. `gem install ruby-graphviz` and try again.'
$stderr.puts 'Skipping GraphViz StateMachine::Event tests. `gem install ruby-graphviz` and try again.'
end
4 changes: 2 additions & 2 deletions test/unit/integrations/data_mapper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

# Establish database connection
DataMapper.setup(:default, 'sqlite3::memory:')
DataObjects::Sqlite3.logger = DataObjects::Logger.new("#{File.dirname(__FILE__)}/../../data_mapper.log", 0)
DataObjects::Sqlite3.logger = DataObjects::Logger.new("#{File.dirname(__FILE__)}/../../data_mapper.log", :info)

module DataMapperTest
class BaseTestCase < Test::Unit::TestCase
Expand Down Expand Up @@ -432,5 +432,5 @@ def test_should_invoke_callbacks_in_specific_order
end
end
rescue LoadError
$stderr.puts 'Skipping DataMapper Core tests. `gem install dm-core` and try again.'
$stderr.puts 'Skipping DataMapper tests. `gem install dm-core rspec hoe launchy do_sqlite3` and try again.'
end
2 changes: 1 addition & 1 deletion test/unit/machine_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1532,5 +1532,5 @@ def test_should_allow_path_and_format_to_be_customized
end
end
rescue LoadError
$stderr.puts 'Skipping GraphViz tests. `gem install ruby-graphviz` and try again.'
$stderr.puts 'Skipping GraphViz StateMachine::Machine tests. `gem install ruby-graphviz` and try again.'
end
14 changes: 7 additions & 7 deletions test/unit/state_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ def test_should_include_new_module_in_owner_class
end

def test_should_define_each_context_method_in_owner_class
%w(color glow).each {|method| assert @klass.instance_methods.include?(method)}
%w(color glow).each {|method| assert @klass.method_defined?(method)}
end

def test_should_not_use_context_methods_as_owner_class_methods
Expand All @@ -279,8 +279,8 @@ def test_should_not_use_context_methods_as_owner_class_methods
end

def test_should_include_context_methods_in_state_methods
assert_equal @color_method, @state.methods['color']
assert_equal @glow_method, @state.methods['glow']
assert_equal @color_method, @state.methods[:color]
assert_equal @glow_method, @state.methods[:glow]
end
end

Expand Down Expand Up @@ -318,7 +318,7 @@ def test_should_include_new_module_in_owner_class
end

def test_should_define_each_context_method_in_owner_class
%w(color glow).each {|method| assert @klass.instance_methods.include?(method)}
%w(color glow).each {|method| assert @klass.method_defined?(method)}
end

def test_should_not_use_context_methods_as_owner_class_methods
Expand All @@ -327,8 +327,8 @@ def test_should_not_use_context_methods_as_owner_class_methods
end

def test_should_include_context_methods_in_state_methods
assert_equal @color_method, @state.methods['color']
assert_equal @glow_method, @state.methods['glow']
assert_equal @color_method, @state.methods[:color]
assert_equal @glow_method, @state.methods[:glow]
end
end

Expand Down Expand Up @@ -381,7 +381,7 @@ def color
end

def test_should_track_latest_defined_method
assert_equal @current_color_method, @state.methods['color']
assert_equal @current_color_method, @state.methods[:color]
end
end

Expand Down

0 comments on commit 670ac78

Please sign in to comment.