I just try to re-implement extend as an exercise.
ruby's extend()
doesn't work like this:
module SimpleModule
def self.class_hello_world
puts 'i am a simple module boss'
end
def self.class_hello_name
puts "hello from #{@@name}"
end
end
class SimpleClass
custom_extend SimpleModule
For example, the following doesn't work:
module Dog
def self.greet
puts "hello"
end
end
class Cat
extend Dog
end
Cat.greet
--output:--
`<main>': undefined method `greet' for Cat:Class (NoMethodError)
extend()
works like this:
module Dog
def greet
puts "hello"
end
end
class Cat
extend Dog
end
Cat.greet
--output:--
hello
In other words, extend()
inserts the module instance methods--not the module methods(e.g. method names preceded by self
)--into Cat's singleton class (which is where Cat's class methods live). In ruby, include()
and extend()
have nothing to do with module methods (again, method names preceded by self
). Modules have two uses in ruby:
- As a namespace, e.g. containing
def self.method_name
- As a mixin, e.g. containing
def some_method
include() and extend() deal with #2.
The following solution doesn't work with @@variables
, but trying to figure out all the twist and turns that @@variables
exhibit in ruby is not worth the effort--just don't use them. Use class instance variables instead, i.e @variables
specified outside of any def's:
def my_extend(some_module)
singleton_class.include some_module
end
module Dog
def greet
puts @greeting
end
private
def sayhi
puts "hi"
end
end
class Cat
@greeting = "hello"
my_extend Dog
end
Cat.greet
#Cat.sayhi #=>`<main>': private method `sayhi' called for Cat:Class (NoMethodError)
Cat.class_eval {sayhi} #Change self to the Cat class so the implicit
#self variable that calls sayhi is equal to Cat
--output:--
hello
hi
Now, you just need to implement my_include
and substitute it inplace for include
. :)
Here's a shot at my_include()
:
class Class
def my_include(module_)
#For public and protected methods:
module_.instance_methods(include_super=false).each do |meth_name|
meth = module_.instance_method(meth_name)
define_method(meth_name) do
meth.bind(self).call
end
end
#For private methods:
module_.private_instance_methods(include_super=false).each do |meth_name|
meth = module_.instance_method(meth_name)
define_method(meth_name) do
meth.bind(self).call
end
private :"#{meth_name}"
end
end
end
module Dog
def greet
puts "hello"
end
def go
puts "run, run run"
end
private
def sayhi
puts "hi"
end
end
class Cat
my_include Dog
end
c = Cat.new
c.greet
c.go
c.sayhi
--output:--
hello
run, run run
#=>`<main>': private method `sayhi' called for #<Cat:0x007fc014136f60> (NoMethodError)
With my_extend()
:
class Class
def my_include(module_)
#For public and protected methods:
module_.instance_methods(include_super=false).each do |meth_name|
meth = module_.instance_method(meth_name)
define_method(meth_name) do
meth.bind(self).call
end
end
#For private methods:
module_.private_instance_methods(include_super=false).each do |meth_name|
meth = module_.instance_method(meth_name)
define_method(meth_name) do
meth.bind(self).call
end
private :"#{meth_name}"
end
end
def my_extend(module_)
singleton_class.my_include module_
end
end
module Dog
def greet
puts @greeting
end
private
def sayhi
puts "hi"
end
end
class Cat
@greeting = "hello"
my_extend Dog
end
Cat.greet
#Cat.sayhi #=>private method `sayhi' called for Cat:Class (NoMethodError)
Cat.class_eval {sayhi}
--output:--
hello
hi