Accessing class methods of module - ruby

I try to include/extend a module A into a module B which in turn gets included into a class C. Then I want to invoke a class method named cm of A but I don't know how.
module A
def self.included(klass)
klass.extend ClassMethods
end
module ClassMethods
def cm
puts "cm"
end
end
end
module B
include A
end
class C
extend B
end
c = C.new
c.cm # -> does not work
C.cm # -> does not work

You can really simplify this syntax with the augmentations plug-in:
https://github.com/henrik/augmentations
or the gem based on it:
https://github.com/chemica/augmentations-gem
Use:
class User
augment MyModule
end
with modules like
module MyModule
augmentation do
def self.a_class_method
# …
end
def an_instance_method
# …
end
end
end
The plug-in itself is tiny, just a few lines of code.

When including modules, you do this:
module A
def self.included(klass)
klass.include ClassMethods
puts :included
end
module ClassMethods
def cm
puts :cm
end
end
end
module B
include A
end
class C
include B
end
c = C.new
c.cm
Similarly when extending classes, you do this:
module X
def self.extended(klass)
klass.extend ClassMethods
end
module ClassMethods
def cm
puts :cm
end
end
end
module Y
def self.extended(klass)
klass.extend X
end
end
class Z
extend Y
end
Z.cm
See
Include vs Extend in Ruby

Related

lifecycle callbacks on instance and class methods in ruby

module Gym
def self.included(class_or_module)
class_or_module.send(:include, InstanceMethods)
class_or_module.extend(ClassMethods)
end
module ClassMethods
def build
end
end
module InstanceMethods
def open
end
def book_for_practice
end
def close
end
end
end
this is an example in the Ruby's Object Lifecycle Callbacks section of RubyMonk. I don't understand how it's supposed to work or what the point of this is. self.included should just document how the two modules within Gym get used, right? why does class_or_module then get sent/extended? why doesn't it get saved in some sort of arrays that document the lifecyle, like in the examples leading up to this one, such as
##extended_objects = []
def self.extended_objects
##extended_objects
end
def self.extended(class_or_module)
##extended_objects << class_or_module
It's not just documentation. self.included is a callback method that gets called as soon as the module is being included in any other module or class.
Instance methods are included via send, class or module methods via extend in that example.
Find out more in the Ruby documentation.
Let me answer your Question One by one.
1: self.included should just document how the two modules within Gym get used ?
module A
def instance_methods_1
p 'hello instance_methods_1'
end
def instance_methods_2
p 'hello instance_methods_2'
end
module KlassMethods
def klass_methods_1
p 'Hello!! klass_methods_1'
end
def klass_methods_2
p 'Hello!! klass_methods_2'
end
end
end
class B
include A # instead of writing two piece of code like this we could wrap in one using `self.included` method Hook
extend A::KlassMethods
end
B.new.instance_methods_1
B.new.instance_methods_2
B.klass_methods_1
B.klass_methods_2
Another version of same program with method hook using self.included
module A
# this is special method one of the methods hook in ruby.
def self.included(base)
base.extend(KlassMethods)
end
def instance_methods_1
p 'hello instance_methods_1'
end
def instance_methods_2
p 'hello instance_methods_2'
end
module KlassMethods
def klass_methods_1
p 'Hello!! klass_methods_1'
end
def klass_methods_2
p 'Hello!! klass_methods_2'
end
end
end
class B
include A
end
B.new.instance_methods_1
B.new.instance_methods_2
B.klass_methods_1
B.klass_methods_2
2: self.included
To know more about included hook and
Ruby Hook
3: why does class_or_module then get sent/extended ?
module AddAdditionalProperty
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def add_additional_property
p 'ClassMethods::add_additional_property'
end
end
end
module ActiveRecord
class Base
def test
p 'Base test Method'
end
end
end
ActiveRecord::Base.send(:include, AddAdditionalProperty)
ActiveRecord::Base.add_additional_property
## Another version of same Program
module AddAdditionalProperty
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def add_additional_property
p 'ClassMethods::add_additional_property'
end
end
end
module ActiveRecord
class Base
include AddAdditionalProperty
def test
p 'Base test Method'
end
end
end
ActiveRecord::Base.add_additional_property
Hope this answer help you !!!

Issue with creating DSL syntax

I have the following code:
module A
def self.included(base)
base.extend(ClassMethods)
end
def foo
a = bar
puts a
end
def bar(str="qwe")
str
end
module ClassMethods
end
end
class B
include A
def bar(str="rty")
str
end
end
B.new.foo #=> "rty"
I wish class B to look like this:
class B
include A
bar "rty"
end
B.new.foo #=> rty
or
class B
include A
HelperOptions.bar "rty" # HelperOptions class should be in module A
end
B.new.foo #=> rty
I tried using define_method, class_eval, and initialize. How can be implemented syntax bar 'rty' or HelperOptions.bar 'rty' and what shall be done in module A?
If I got your question properly you want to define a class method A.bar that defines an instance method B#bar that returns it's argument, you can do it like this:
module A
def self.included(base)
base.extend(ClassMethods)
end
def foo
puts bar
end
module ClassMethods
def bar(str)
define_method(:bar) { str }
end
end
end
class B
include A
bar 'rty'
end
B.new.bar
# => "rty"
B.new.foo
# Output: rty

Using a module inside another module with the same name

I have a module like this
module A
module ClassMethods
def a
"a"
end
def b
a
end
end
def self.included(klass)
klass.extend ClassMethods
end
end
I want to factor b into it's own module because Bs are clearly not As. The b method depends on A, and I would like the methods defined in A to be available to B.
Doing this:
module B
module ClassMethods
extend A
def b
a
end
end
def self.included(klass)
klass.extend(ClassMethods)
end
end
It does not work. Nor does include. I tried moving include or extend as well as the how constants are references. What am I missing here?
I hope I understood what you wanted. I split up module A and module B. When I include those in a new class Klass, I have access to both method a and method b, whereas method b from module B depends on method a from module A:
module A
module ClassMethods
def a
"a"
end
end
def self.included(klass)
klass.extend(ClassMethods)
end
end
module B
module ClassMethods
# include A::ClassMethods here and you can do only 'include B' inside Klass
def b
"Called from B: %s" % a
end
end
def self.included(klass)
klass.extend(ClassMethods)
end
end
class Klass
include A, B
end
p Klass.a # => "a"
p Klass.b # => "Called from B: a"
As I understand you can define the same Module in different places, no need to say include, extend or anything. As you see neither Module A nor Module B uses those keywords, they both just define ClassMethods.
Of course, you will have to include module A wherever you use method b of module B or else the method call to a will fail. You can also do include A::ClassMethods inside B::ClassMethods, that way you only need to include B inside Klass.
Add the extend A action in the included callback of the module B instead of adding it in the ClassMethod definition, like the following code does:
module A
module ClassMethods
def a
"a"
end
end
def self.included(klass)
klass.extend ClassMethods
end
end
module B
module ClassMethods
def b
a
end
end
def self.included(klass)
klass.extend(ClassMethods)
klass.extend(A)
end
end
class C
include B
end
C.b
# => "a"

Can I add class methods and instance methods from the same module?

Newbie question:
I know how include and extend work, what I am wondering is if there is a way to get both class and instance methods from a single module?
This is how I do it with two modules:
module InstanceMethods
def mod1
"mod1"
end
end
module ClassMethods
def mod2
"mod2"
end
end
class Testing
include InstanceMethods
extend ClassMethods
end
t = Testing.new
puts t.mod1
puts Testing::mod2
Thanks for taking your time ...
There is a common idiom for that. It makes use of included object model hook. This hook gets invoked every time when a module is included to a module/class
module MyExtensions
def self.included(base)
# base is our target class. Invoke `extend` on it and pass nested module with class methods.
base.extend ClassMethods
end
def mod1
"mod1"
end
module ClassMethods
def mod2
"mod2"
end
end
end
class Testing
include MyExtensions
end
t = Testing.new
puts t.mod1
puts Testing::mod2
# >> mod1
# >> mod2
I personally like to group instance method to a nested module as well. But this is less accepted practice, as far as I know.
module MyExtensions
def self.included(base)
base.extend ClassMethods
base.include(InstanceMethods)
# or this, if you have an old ruby and the line above doesn't work
# base.send :include, InstanceMethods
end
module InstanceMethods
def mod1
"mod1"
end
end
module ClassMethods
def mod2
"mod2"
end
end
end
module Foo
def self.included(m)
def m.show1
p "hi"
end
end
def show2
p "hello"
end
end
class Bar
include Foo
end
Bar.new.show2 #=> "hello"
Bar.show1 #=> "hi"
Yes. It's exactly as simple as you would expect, due to the genius of ruby:
module Methods
def mod
"mod"
end
end
class Testing
include Methods # will add mod as an instance method
extend Methods # will add mod as a class method
end
t = Testing.new
puts t.mod
puts Testing::mod
Or, you could do:
module Methods
def mod1
"mod1"
end
def mod2
"mod2"
end
end
class Testing
include Methods # will add both mod1 and mod2 as instance methods
extend Methods # will add both mod1 and mod2 as class methods
end
t = Testing.new
puts t.mod1
puts Testing::mod2
# But then you'd also get
puts t.mod2
puts Testing::mod1

Class variables and extending a module

I have a module like the following
module MyModule
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def foo
##var = 1
end
def bar
puts ##var
end
end
end
class A
include MyModule
foo
end
class B < A; end
so that
B.bar outputs '1'.
However, I would like to have .bar only be defined if .foo is called. I tried
module MyModule
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def foo
##var = 1
extend SingletonMethods
end
module SingletonMethods
def bar
puts ##var
end
end
end
The problem is that
B.bar
returns the error "uninitialized class variable ##var in MyModule::SingletonMethods". How can I make it so that a variable defined in .foo is available to .bar?
use mattr_accessor instead
I was able to access class variables from a module using the self syntax
class User
include Base
##attributes = [:slug, :email, :crypted_password]
end
module Base
def self.included(base)
base.extend ClassMethods
end
def headers
if defined? self.attributes
self.attributes
end
end
end
Now calling User.headers gives me the expected result of
[:slug, :email, :crypted_password]
If anyone can shed more light on why this works exactly so in ruby, please let me know!

Resources