When should I use "class Object", "class Module", "module Kernel" and nothing?
I'm new to ruby metaprogramming, and I see people metaprogramming code in different places, like class Object
, class Module
, module Kernel
and "nothing" (ie, out of a class/module definition block).
E.g.: I'm creating a c_attr_accessor
method to access class variables, and I'm not sure where I must put the code, since it works in any of those cases.
How to decide what place is more appropriate to put a new gl开发者_如何学运维obal code?
Each of these examples fall into different cases.
If you are writing methods that apply to all objects, then you open the Object
class so all objects can access it. If you are writing methods that apply to all modules, then you open Module
. Whenever you open a class to add methods, the methods should apply to all instances of the class and nothing else.
Extending the Kernel
module is different: people do this to add methods that should be available to every scope, but not really as methods to be explicitly called on an object, by making them private.
When you are outside of any class
or module
statement, you are in the scope of the main
object, and methods you define default to being private methods of Object
. This is fine for small or simple programs, but you will eventually want to use proper modules as namespaces to organize your methods.
As a final note on the subject, you always need to be sure that you really want methods you add to built-in classes and modules to be available to everything in your application, including external inclusions because they all share the built-ins.
Now to apply this to answer your question. Because you are defining a method that creates accessors for class variables, you should put it in the class Class
as it applies to all classes and nothing else. Finally, you will likely only use it in class definitions (within a class
statement), so we should make it private:
class Class
private
def c_attr_accessor(name)
# ...
end
end
class User
c_attr_accessor :class_variable_name
# ...
end
If you don't really need it in every class (maybe just a few), then create a "mixin module" to extend every class that needs this feature:
module ClassVariableAccessor
private
def c_attr_accessor(name)
# ...
end
end
class User
extend ClassVariableAccessor
c_attr_accessor :class_variable_name
# ...
end
Note that you are using Object#extend
to add c_attr_accessor
only to the object User
(remember that classes are objects; you will hear that a lot if you are new to Ruby metaprogramming).
There is another way to implement the last example, which works by explicitly extending its base class through the Module#included(base_class)
"hook method" called whenever the module included, and the base class is passed to base_class
:
module ClassVariableAccessor
def included(base_class)
base_class.extend ClassMethods
end
module ClassMethods
def c_attr_accessor(name)
# ...
end
end
end
class User
include ClassVariableAccessor
c_attr_accessor :class_variable_name
# ...
end
I recommend this last solution because it is the most general and uses a simple interface that should not need to be updated. I hope this is not too much!
Have you tried looking up where the normal attribute accessors are defined? I'd either define it in the same class/module, or create my own module in which all my new methods go.
精彩评论