问题
I am using Ruby 1.9.2 and the Ruby on Rails v3.2.2 gem. I am trying to learn Metaprogramming "the right way" and at this time I am aliasing an instance method in the included do ... end
block provided by the RoR ActiveSupport::Concern module:
module MyModule
extend ActiveSupport::Concern
included do
# Builds the instance method name.
my_method_name = build_method_name.to_sym # => :my_method
# Defines the :my_method instance method in the including class of MyModule.
define_singleton_method(my_method_name) do |*args|
# ...
end
# Aliases the :my_method instance method in the including class of MyModule.
singleton_class = class << self; self end
singleton_class.send(:alias_method, :my_new_method, my_method_name)
end
end
"Newbiely" speaking, with a search on the Web I came up with the singleton_class = class << self; self end
statement and I used that (instead of the class << self ... end
block) in order to scope the my_method_name
variable, making the aliasing generated dynamically.
I would like to understand exactly why and how the singleton_class
works in the above code and if there is a better way (maybe, a more maintainable and performant one) to implement the same (aliasing, defining the singleton method and so on), but "the right way" since I think it isn't so.
回答1:
I recommend Yehuda Katz's post on metaprogamming on Ruby's self. Here's my humble summary in response to your question:
In Ruby, all objects have a singleton class (also known as metaclass). Objects inherit first from their singleton class invisibly, then from their explicit class. Ruby classes themselves have their own singleton classes since classes are objects as well. The class <<
idiom is simply Ruby's syntax for accessing the scope of an object's singleton class.
class Person
class << self
# self in this scope is Person's singleton class
end
end
person = Person.new
person_singleton_class = class << person; self; end
Your version of Rails actually provides singleton_class
as a shortcut. Since singleton_class
is an available method, you don't need to assign it to a variable in the expression singleton_class = class << self; self end
:
Person.singleton_class
person = Person.new
person.singleton_class
Since a class inherits directly from its singleton class, this is where we want to add class methods dynamically when metaprogramming. Ruby provides a few ways to open up the scope of an object while maintaining access to the surrounding scope: class_eval
and instance_eval
. There are subtle differences in the way these behave (Yehuda's post explains this), but you may use either to enter the scope of your singleton class, resolve methods on the singleton class as self
and still have access to my_method_name
from the surrounding scope.
All that said, you could make a few small changes to your module:
module MyModule
extend ActiveSupport::Concern
included do
# Builds the instance method name.
my_method_name = build_method_name.to_sym # => :my_method
# Defines the :my_method instance method in the including class of MyModule.
define_singleton_method(my_method_name) do |*args|
# ...
end
singleton_class.class_eval do
# method resolution in scope of singleton class
alias_method :my_new_method, my_method_name
end
end
end
来源:https://stackoverflow.com/questions/12937209/understanding-the-singleton-class-when-aliasing-a-instance-method