HoboSupport - Module extensions
Bit involved, this one :-)
When a module is included in a class, it gets a callback on the
included method, with the class passed as argument. However, if a module M2 is included in M1, and a class C includes M1, then M2 never gets to know about C. So, for example, M2 could not
alias_method_chain a class method on C.
included_in_class_callbacks makes it easy to implement a notification from M1 to M2, so that M2 does have full access to the class. All you do is insert a call to
included_in_class_callbacks(base) as the end of the module’s
(Note we’re using the metaid extensions here too)
>> module M2 def self.included_in_class(klass) klass.metaclass_eval do def name_with_shouting; name_without_shouting.upcase; end alias_method_chain :name, :shouting end end end module M1 def self.included(base) included_in_class_callbacks(base) end include M2 end class C def self.name "my name is C" end include M1 end C.name => "MY NAME IS C"
Declares a reader for an instance variable on the class. The attribute is looked up on the superclass if not defined on the receiving class. In other words, the superclass defines a default that subclasses can override. The declaration can also give a default, as shown here.
>> class A inheriting_cattr_reader :nickname => "Andy" end class B < A; end
B has the same nickname as its superclass
>> A.nickname => "Andy" >> B.nickname => "Andy"
Now we change the nickname of
A retains it’s existing nickname.
>> class B; @nickname = "Bob"; end >> B.nickname => "Bob" >> A.nickname => "Andy"
In Ruby we use modules to factor out features that are shared by more than one class. By including the module, a class gets the module’s features, just as if they were defined inside the class. This mechanism is very simple if the shared features are all instance methods, but starts to get complicated when we want to share class-level features. For example, to create a module that adds class-methods to the including class, Ruby programmers often use the ClassMethods sub-module idiom:
>> module M def self.included(base) base.send(:extend, ClassMethods) end module ClassMethods def foo; 123; end end end class C; include M; end >> C.foo => 123
It’s a shame that such a basic capability isn’t more declarative.
classy_module provides a mechanism for creating a module that, when included, will
class_eval it’s entire body (the block passed to
classy_module). This means we can write shared class features exactly as we would write them if they were inside the class:
>> MyClassyModule = classy_module do def self.foo; 123; end end >> MyClassyModule.class => Module >> class C2; include MyClassyModule; end >> C2.foo => 123
Edit this page