Can you add a Class Method to the Core Module Class in Ruby?
Is it possible to add a core method to the Module class in Ruby? I want to do something like this (messed around with the idea yesterday, just whipped this together in a second):
module MyModule
base do
has_many :something
end
end
# implementation, doesn't work though... reason for the question
Module.class_eval do
def self.base(&block)
class_eval do
def self.included(base)
开发者_JAVA百科 base.class_eval(&block)
end
end
end
end
If I create a module, I can't access that method:
$ module User; end
$ User.base
NoMethodError: undefined method `base' for User:Module
Any ways to do this?
Update
This works! Thanks @Jörg W Mittag. Makes it much easier to read for Rails has_many
and such:
class Module
def base(&block)
metaclass = class << self; self; end
# add the method using define_method instead of def x.say so we can use a closure
metaclass.send :define_method, :included do |base|
base.class_eval(&block) unless block.nil?
base.class_eval <<-EOF
extend #{name}::ClassMethods
include #{name}::InstanceMethods
EOF
end
block
end
end
Like this example:
module UserRoleBehavior
base do
has_many :user_roles
has_many :roles, :through => :user_roles
end
module ClassMethods
# ...
end
module InstanceMethods
# ...
end
end
class User < ActiveRecord::Base
include UserRoleBehavior
end
Cheers!
If you want to add a method to the Module
class, you simply add a method to the Module
class:
class Module
def base
# ...
end
end
In your code, you were using
def self.base
# ...
end
This syntax (def foo.bar
) is the syntax for adding a singleton method named bar
to the object referenced by foo
, which is actually really just adding a regular instance method to the singleton class of the object referenced by foo
.
So, in your code you were adding a singleton method named base
to the object referenced by self
, which inside of class_eval
is the class object itself, in this case Module
. The reason why singleton methods are called singleton methods, is because they only exist on a single object. In your case, the method base
exists only on the object Module
, not on any other object, and in particular not on the object User
. In other words: the only way to call the base
method is as Module.base
, because that is the only object that you added it to.
It took me quite a while to wade through your three(!) nested class_eval
s to find out what it is that you are trying to achieve. Which wasn't made easier by the fact that your code doesn't even actually work, even if defined on the correct class, because def
creates a new scope, and so block
is undefined in the innermost class_eval
.
Apparently, what you are trying to do is this:
class Module
def base(&block)
define_singleton_method(:included) do |base|
base.class_eval(&block)
end
end
end
I believe you add it by defining the method in the module module:
class Module
def new_method
...
end
end
Be very careful with this, as it effects all code that uses Module.
精彩评论