aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2006-02-04 19:14:53 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2006-02-04 19:14:53 +0000
commit2ee6229bd8cf3a87ede0cd8b573e3faf5a15dbfa (patch)
tree6448472533f1bc97162e3d498281a306030a932a
parent5cbc062c8ad82271de11ae245d2bdaeffc0c4da7 (diff)
downloadrails-2ee6229bd8cf3a87ede0cd8b573e3faf5a15dbfa.tar.gz
rails-2ee6229bd8cf3a87ede0cd8b573e3faf5a15dbfa.tar.bz2
rails-2ee6229bd8cf3a87ede0cd8b573e3faf5a15dbfa.zip
Added delegation support to Module that allows multiple delegations at once (unlike Forwardable in the stdlib) [DHH]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3535 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
-rw-r--r--activesupport/CHANGELOG11
-rw-r--r--activesupport/lib/active_support/core_ext/module.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb16
-rw-r--r--activesupport/test/core_ext/module_test.rb48
4 files changed, 77 insertions, 1 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index 4b6c3096d3..36972ab8dd 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,5 +1,16 @@
*SVN*
+* Added delegation support to Module that allows multiple delegations at once (unlike Forwardable in the stdlib) [DHH]. Example:
+
+ class Account < ActiveRecord::Base
+ has_one :subscription
+ delegate :free?, :paying?, :to => :subscription
+ delegate :overdue?, :to => "subscription.last_payment"
+ end
+
+ account.free? # => account.subscription.free?
+ account.overdue? # => account.subscription.last_payment.overdue?
+
* Fix Reloadable to handle the case where a class that has been 'removed' has not yet been garbage collected. [Nicholas Seckar]
* Don't allow Reloadable to be included into Modules.
diff --git a/activesupport/lib/active_support/core_ext/module.rb b/activesupport/lib/active_support/core_ext/module.rb
index 0f46631e65..bd6d62468a 100644
--- a/activesupport/lib/active_support/core_ext/module.rb
+++ b/activesupport/lib/active_support/core_ext/module.rb
@@ -1,2 +1,3 @@
require File.dirname(__FILE__) + '/module/inclusion'
-require File.dirname(__FILE__) + '/module/attribute_accessors' \ No newline at end of file
+require File.dirname(__FILE__) + '/module/attribute_accessors'
+require File.dirname(__FILE__) + '/module/delegation' \ No newline at end of file
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
new file mode 100644
index 0000000000..95173007ca
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -0,0 +1,16 @@
+class Module
+ def delegate(*methods)
+ options = methods.pop
+ unless options.is_a?(Hash) && to = options[:to]
+ raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key"
+ end
+
+ methods.each do |method|
+ module_eval(<<-EOS, "(__DELEGATION__)", 1)
+ def #{method}(*args, &block)
+ #{to}.__send__(#{method.inspect}, *args, &block)
+ end
+ EOS
+ end
+ end
+end \ No newline at end of file
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index d6ea595ae2..464196a0ec 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -26,6 +26,34 @@ end
class De
end
+Somewhere = Struct.new(:street, :city)
+
+Someone = Struct.new(:name, :place) do
+ delegate :street, :city, :to => :place
+ delegate :state, :to => :@place
+ delegate :upcase, :to => "place.city"
+end
+
+class Name
+ delegate :upcase, :to => :@full_name
+
+ def initialize(first, last)
+ @full_name = "#{first} #{last}"
+ end
+end
+
+$nowhere = <<-EOF
+class Name
+ delegate :nowhere
+end
+EOF
+
+$noplace = <<-EOF
+class Name
+ delegate :noplace, :tos => :hollywood
+end
+EOF
+
class ModuleTest < Test::Unit::TestCase
def test_included_in_classes
assert One.included_in_classes.include?(Ab)
@@ -34,4 +62,24 @@ class ModuleTest < Test::Unit::TestCase
assert !One.included_in_classes.include?(De)
end
+ def test_delegation_to_methods
+ david = Someone.new("David", Somewhere.new("Paulina", "Chicago"))
+ assert_equal "Paulina", david.street
+ assert_equal "Chicago", david.city
+ end
+
+ def test_delegation_down_hierarchy
+ david = Someone.new("David", Somewhere.new("Paulina", "Chicago"))
+ assert_equal "CHICAGO", david.upcase
+ end
+
+ def test_delegation_to_instance_variable
+ david = Name.new("David", "Hansson")
+ assert_equal "DAVID HANSSON", david.upcase
+ end
+
+ def test_missing_delegation_target
+ assert_raises(ArgumentError) { eval($nowhere) }
+ assert_raises(ArgumentError) { eval($noplace) }
+ end
end \ No newline at end of file