开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜