# HoboSupport - Module extensions doctest_require: '../../lib/hobo_support' {.hidden} ## `Module#included_in_class_callbacks` 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 `self.included` method. (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" ## `Module#interiting_cattr_reader` 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` >> A.nickname => "Andy" >> B.nickname => "Andy" Now we change the nickname of `B`. `A` retains it's existing nickname. >> class B; @nickname = "Bob"; end >> B.nickname => "Bob" >> A.nickname => "Andy" ## `Kernel#classy_module` 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