mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
AS guide: removes some duplication, and makes a second pass on method delegation
This commit is contained in:
parent
347d604792
commit
2a984806ca
1 changed files with 21 additions and 144 deletions
|
@ -667,129 +667,6 @@ end
|
|||
|
||||
NOTE: Defined in +active_support/core_ext/module/attribute_accessors.rb+.
|
||||
|
||||
h4. Method Delegation
|
||||
|
||||
The class method +delegate+ offers an easy way to forward methods.
|
||||
|
||||
For example, if +User+ has some details like the age factored out to +Profile+, it could be handy to still be able to access such attributes directly, <tt>user.age</tt>, instead of having to explicit the chain <tt>user.profile.age</tt>.
|
||||
|
||||
That can be accomplished by hand:
|
||||
|
||||
<ruby>
|
||||
class User
|
||||
has_one :profile
|
||||
|
||||
def age
|
||||
profile.age
|
||||
end
|
||||
end
|
||||
</ruby>
|
||||
|
||||
But with +delegate+ you can make that shorter and the intention even more obvious:
|
||||
|
||||
<ruby>
|
||||
class User
|
||||
has_one :profile
|
||||
|
||||
delegate :age, to => :profile
|
||||
end
|
||||
</ruby>
|
||||
|
||||
The macro accepts more than one method:
|
||||
|
||||
<ruby>
|
||||
class User
|
||||
has_one :profile
|
||||
|
||||
delegate :age, :avatar, :twitter_username, to => :profile
|
||||
end
|
||||
</ruby>
|
||||
|
||||
Methods can be delegated to objects returned by methods, as in the examples above, but also to instance variables, class variables, and constants. Just pass their names as symbols or strings, including the at signs in the last cases.
|
||||
|
||||
For example, +ActionView::Base+ delegates +erb_trim_mode=+:
|
||||
|
||||
<ruby>
|
||||
module ActionView
|
||||
class Base
|
||||
delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
|
||||
end
|
||||
end
|
||||
</ruby>
|
||||
|
||||
In fact, you can delegate to any expression passed as a string. It will be evaluated in the context of the receiver. Controllers for example delegate alerts and notices to the current flash:
|
||||
|
||||
<ruby>
|
||||
delegate :alert, :notice, :to => "request.flash"
|
||||
</ruby>
|
||||
|
||||
If the target is +nil+ calling any delegated method will raise an exception even if +nil+ responds to such method. You can override this behavior setting the option +:allow_nil+ to true, in which case the forwarded call will simply return +nil+.
|
||||
|
||||
If the target is a method, the name of delegated methods can also be prefixed. If the +:prefix+ option is set to (exactly) the +true+ object, the value of the +:to+ option is prefixed:
|
||||
|
||||
<ruby>
|
||||
class Invoice
|
||||
belongs_to :customer
|
||||
|
||||
# defines a method called customer_name
|
||||
delegate :name, :to => :customer, :prefix => true
|
||||
end
|
||||
</ruby>
|
||||
|
||||
And a custom prefix can be set as well, in that case it does not matter wheter the target is a method or not:
|
||||
|
||||
<ruby>
|
||||
class Account
|
||||
belongs_to :user
|
||||
|
||||
# defines a method called admin_email
|
||||
delegate :email, :to => :user, :prefix => 'admin'
|
||||
end
|
||||
</ruby>
|
||||
|
||||
NOTE: Defined in +active_support/core_ext/module/delegation.rb+.
|
||||
|
||||
h4. Method Removal
|
||||
|
||||
h5. +remove_possible_method+
|
||||
|
||||
The method +remove_possible_method+ is like the standard +remove_method+, except it silently returns on failure:
|
||||
|
||||
<ruby>
|
||||
class A; end
|
||||
|
||||
A.class_eval do
|
||||
remove_method(:nonexistent) # raises NameError
|
||||
remove_possible_method(:nonexistent) # no problem, continue
|
||||
end
|
||||
</ruby>
|
||||
|
||||
This may come in handy if you need to define a method that may already exist, since redefining a method issues a warning "method redefined; discarding old redefined_method_name".
|
||||
|
||||
h5. +redefine_method(method_name, &block)+
|
||||
|
||||
The method first removes method with given name (using +remove_possible_method+) and then defines new one.
|
||||
|
||||
<ruby>
|
||||
class A; end
|
||||
|
||||
A.class_eval do
|
||||
redefine_method(:foobar) do |foo|
|
||||
#do something here
|
||||
end
|
||||
|
||||
#Code above does the same as this:
|
||||
|
||||
method_name = :foobar
|
||||
remove_possible_method(method_name)
|
||||
define_method(method_name) do |foo|
|
||||
#do something here
|
||||
end
|
||||
end
|
||||
</ruby>
|
||||
|
||||
NOTE: Defined in +active_support/core_ext/module/remove_method.rb+.
|
||||
|
||||
h4. Parents
|
||||
|
||||
h5. +parent+
|
||||
|
@ -984,9 +861,9 @@ though an anonymous module is unreachable by definition.
|
|||
|
||||
NOTE: Defined in +active_support/core_ext/module/anonymous.rb+.
|
||||
|
||||
h4. Delegation
|
||||
h4. Method Delegation
|
||||
|
||||
The +delegate+ macro declares that some instance method has to be forwarded to some object.
|
||||
The macro +delegate+ offers an easy way to forward methods.
|
||||
|
||||
Let's imagine that users in some application have login information in the +User+ model but name and other data in a separate +Profile+ model:
|
||||
|
||||
|
@ -996,7 +873,7 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
</ruby>
|
||||
|
||||
With that configuration you get a user's name via his profile, +user.profile.name+, but you could write a shortcut so that client code can read it directly:
|
||||
With that configuration you get a user's name via his profile, +user.profile.name+, but it could be handy to still be able to access such attribute directly:
|
||||
|
||||
<ruby>
|
||||
class User < ActiveRecord::Base
|
||||
|
@ -1018,10 +895,21 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
</ruby>
|
||||
|
||||
When interpolated into a string, the +:to+ option should become an expression that evaluates to the object the method is delegated to:
|
||||
It is shorter, and the intention more obvious.
|
||||
|
||||
The macro accepts several methods:
|
||||
|
||||
<ruby>
|
||||
delegate :name, :age, :address, :twitter, :to => :profile
|
||||
</ruby>
|
||||
|
||||
When interpolated into a string, the +:to+ option should become an expression that evaluates to the object the method is delegated to. Typically a string or symbol. Such a expression is evaluated in the context of the receiver:
|
||||
|
||||
<ruby>
|
||||
# delegates to the Rails constant
|
||||
delegate :logger, :to => :Rails
|
||||
|
||||
# delegates to the receiver's class
|
||||
delegate :table_name, :to => 'self.class'
|
||||
</ruby>
|
||||
|
||||
|
@ -1030,39 +918,28 @@ WARNING: If the +:prefix+ option is +true+ this is less generic, see below.
|
|||
By default, if the delegation raises +NoMethodError+ and the target is +nil+ the exception is propagated. You can ask that +nil+ is returned instead with the +:allow_nil+ option:
|
||||
|
||||
<ruby>
|
||||
class User < ActiveRecord::Base
|
||||
has_one :profile
|
||||
|
||||
delegate :name, :to => :profile, :allow_nil => true
|
||||
end
|
||||
delegate :name, :to => :profile, :allow_nil => true
|
||||
</ruby>
|
||||
|
||||
With +:allow_nil+ the call +user.name+ returns +nil+ if the user has no profile instead of raising an exception.
|
||||
With +:allow_nil+ the call +user.name+ returns +nil+ if the user has no profile.
|
||||
|
||||
The option +:prefix+ adds a prefix to the name of the generated method. This may be handy for example to get a better name:
|
||||
|
||||
<ruby>
|
||||
class Account < ActiveRecord::Base
|
||||
has_one :address
|
||||
|
||||
delegate :street, :to => :address, :prefix => true
|
||||
end
|
||||
delegate :street, :to => :address, :prefix => true
|
||||
</ruby>
|
||||
|
||||
The previous example generates +Account#address_street+ rather than +Account#street+.
|
||||
The previous example generates +address_street+ rather than +street+.
|
||||
|
||||
WARNING: Since in this case the name of the generated method is composed of the target object and target method names, the +:to+ option must be a method name.
|
||||
|
||||
A custom prefix may also be configured:
|
||||
|
||||
<ruby>
|
||||
class User < ActiveRecord::Base
|
||||
has_one :attachment
|
||||
|
||||
delegate :size, :to => :attachment, :prefix => :avatar
|
||||
delegate :size, :to => :attachment, :prefix => :avatar
|
||||
</ruby>
|
||||
|
||||
In the previous example the macro generates +User#avatar_size+ rather than +User#size+.
|
||||
In the previous example the macro generates +avatar_size+ rather than +size+.
|
||||
|
||||
NOTE: Defined in +active_support/core_ext/module/delegation.rb+
|
||||
|
||||
|
|
Loading…
Reference in a new issue