fb4b5f0f3c44460f5b6b46e2b33bd43b350d1a0f
[feedcatcher.git] / vendor / rails / activesupport / lib / active_support / core_ext / module / delegation.rb
1 class Module
2 # Provides a delegate class method to easily expose contained objects' methods
3 # as your own. Pass one or more methods (specified as symbols or strings)
4 # and the name of the target object as the final <tt>:to</tt> option (also a symbol
5 # or string). At least one method and the <tt>:to</tt> option are required.
6 #
7 # Delegation is particularly useful with Active Record associations:
8 #
9 # class Greeter < ActiveRecord::Base
10 # def hello() "hello" end
11 # def goodbye() "goodbye" end
12 # end
13 #
14 # class Foo < ActiveRecord::Base
15 # belongs_to :greeter
16 # delegate :hello, :to => :greeter
17 # end
18 #
19 # Foo.new.hello # => "hello"
20 # Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
21 #
22 # Multiple delegates to the same target are allowed:
23 #
24 # class Foo < ActiveRecord::Base
25 # belongs_to :greeter
26 # delegate :hello, :goodbye, :to => :greeter
27 # end
28 #
29 # Foo.new.goodbye # => "goodbye"
30 #
31 # Methods can be delegated to instance variables, class variables, or constants
32 # by providing them as a symbols:
33 #
34 # class Foo
35 # CONSTANT_ARRAY = [0,1,2,3]
36 # @@class_array = [4,5,6,7]
37 #
38 # def initialize
39 # @instance_array = [8,9,10,11]
40 # end
41 # delegate :sum, :to => :CONSTANT_ARRAY
42 # delegate :min, :to => :@@class_array
43 # delegate :max, :to => :@instance_array
44 # end
45 #
46 # Foo.new.sum # => 6
47 # Foo.new.min # => 4
48 # Foo.new.max # => 11
49 #
50 # Delegates can optionally be prefixed using the <tt>:prefix</tt> option. If the value
51 # is <tt>true</tt>, the delegate methods are prefixed with the name of the object being
52 # delegated to.
53 #
54 # Person = Struct.new(:name, :address)
55 #
56 # class Invoice < Struct.new(:client)
57 # delegate :name, :address, :to => :client, :prefix => true
58 # end
59 #
60 # john_doe = Person.new("John Doe", "Vimmersvej 13")
61 # invoice = Invoice.new(john_doe)
62 # invoice.client_name # => "John Doe"
63 # invoice.client_address # => "Vimmersvej 13"
64 #
65 # It is also possible to supply a custom prefix.
66 #
67 # class Invoice < Struct.new(:client)
68 # delegate :name, :address, :to => :client, :prefix => :customer
69 # end
70 #
71 # invoice = Invoice.new(john_doe)
72 # invoice.customer_name # => "John Doe"
73 # invoice.customer_address # => "Vimmersvej 13"
74 #
75 # If the object to which you delegate can be nil, you may want to use the
76 # :allow_nil option. In that case, it returns nil instead of raising a
77 # NoMethodError exception:
78 #
79 # class Foo
80 # attr_accessor :bar
81 # def initialize(bar = nil)
82 # @bar = bar
83 # end
84 # delegate :zoo, :to => :bar
85 # end
86 #
87 # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
88 #
89 # class Foo
90 # attr_accessor :bar
91 # def initialize(bar = nil)
92 # @bar = bar
93 # end
94 # delegate :zoo, :to => :bar, :allow_nil => true
95 # end
96 #
97 # Foo.new.zoo # returns nil
98 #
99 def delegate(*methods)
100 options = methods.pop
101 unless options.is_a?(Hash) && to = options[:to]
102 raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
103 end
104
105 if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/
106 raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
107 end
108
109 prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"
110
111 allow_nil = options[:allow_nil] && "#{to} && "
112
113 methods.each do |method|
114 module_eval(<<-EOS, "(__DELEGATION__)", 1)
115 def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block)
116 #{allow_nil}#{to}.__send__(#{method.inspect}, *args, &block) # client && client.__send__(:name, *args, &block)
117 end # end
118 EOS
119 end
120 end
121 end