~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/activesupport/lib/active_support/core_ext/module/delegation.rb

  • Committer: Richard Lee (Canonical)
  • Date: 2010-10-15 15:17:58 UTC
  • mfrom: (190.1.3 use-case-mapper)
  • Revision ID: richard.lee@canonical.com-20101015151758-wcvmfxrexsongf9d
Merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
    file, line = caller.first.split(':', 2)
112
 
    line = line.to_i
113
 
 
114
 
    methods.each do |method|
115
 
      on_nil =
116
 
        if options[:allow_nil]
117
 
          'return'
118
 
        else
119
 
          %(raise "#{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
120
 
        end
121
 
 
122
 
      module_eval(<<-EOS, file, line)
123
 
        def #{prefix}#{method}(*args, &block)               # def customer_name(*args, &block)
124
 
          #{to}.__send__(#{method.inspect}, *args, &block)  #   client.__send__(:name, *args, &block)
125
 
        rescue NoMethodError                                # rescue NoMethodError
126
 
          if #{to}.nil?                                     #   if client.nil?
127
 
            #{on_nil}
128
 
          else                                              #   else
129
 
            raise                                           #     raise
130
 
          end                                               #   end
131
 
        end                                                 # end
132
 
      EOS
133
 
    end
134
 
  end
135
 
end