Ruby Invoking Module Method On Extend - ruby

Given a basic ruby class and module, is there a way to call a method from the module as soon as an instance of the class is extended ?
class Dog
def initialize(name)
#name = name
end
end
module Speech
def say_name
puts #name
end
# call to method in module ?
say_name
end
fido = Dog.new('fido')
fido.extend Speech => *'fido'*
I am aware of the 'included' method that acts sort of like a callback when a module is included, but I was hoping for something similar for extend.

Here is one trick using the method extend_object.
Extends the specified object by adding this module’s constants and methods (which are added as singleton methods). This is the callback method used by Object#extend.
class Dog
def initialize(name)
#name = name
end
end
module Speech
def Speech.extend_object(o)
super
puts o.say_name
end
def say_name
#name
end
end
fido = Dog.new('fido')
fido.extend Speech # 'fido'

Related

How to access class method from the included hook of a Ruby module

I'd like my module to define new instance methods based on its including class' instance methods. But in the included hook, the class methods are not defined yet (as the module is included at the top of the class, before the class methods are defined):
module MyModule
def self.included(includer)
puts includer.instance_methods.include? :my_class_method # false <- Problem
end
end
class MyClass
include MyModule
def my_class_method
end
end
I want the users of the module to be free to include it at the top of their class.
Is there a way to make a module define additional methods to a class?
Note: I don't have to use the included hook if there is another way to achieve this.
There'a a method_added callback you could use:
module MyModule
def self.included(includer)
def includer.method_added(name)
puts "Method added #{name.inspect}"
end
end
end
class MyClass
include MyModule
def foo ; end
end
Output:
Method added :foo
If you want to track both, existing and future methods, you might need something like this:
module MyModule
def self.on_method(name)
puts "Method #{name.inspect}"
end
def self.included(includer)
includer.instance_methods(false).each do |name|
on_method(name)
end
def includer.method_added(name)
MyModule.on_method(name)
end
end
end
Example:
class MyClass
def foo ; end
include MyModule
def bar; end
end
# Method :foo
# Method :bar

Ruby classes pass variables between

Is there any way to pass variables between classes?
I have the next code.
module Test
class Super
def initialize(name)
#name = name
end
end
class Upper
def test
puts #name
end
end
end
a=Test::Super.new('My name')
b=Test::Upper.new()
b.test()
Thank you!
No, because a is an instance of the class. Two answers for you;
1) It's better programming practice to have send a to b. So you'd do something like this; (assuming attr_reader :name)
class Upper
def test(s)
s.name
end
end
a = Test::Super.new('My Name')
u = Test::Upper.new
u.test(a)
or you could have it part of the setup; I won't give you all the code, but here's how it'd look
a = Test::Super.new('My name')
b = Test::Upper.new(a)
b.test
=> 'My name'
Neither of these examples is particularly good practice for classes but I imagine you have a more specific use case you're trying to achieve that has been anonymised for the purpose of this question :)
If for some reason instances of the class Upper need to have an access to the internals of instances of the class Super, it means you have a design flaw.
One possible way would be Super needs to expose the variable via a getter:
module Test
class Super
def initialize(name)
#name = name
end
def name
#name
end
end
end
Now you might get the name with Test::Super.new("my name").name.
Another possibility is Upper is actually a subclass of Super:
class Upper < Super
def test
puts #name
end
end
Now Test::Upper.new("my name").test will print "my name", because Upper derives the implementation from Super.
Also, one might use an instance variable on the enclosing module level:
module Test
def self.name=(name)
#name = name
end
def self.name
#name
end
class Super
def initialize(name)
Test.name = name
end
end
class Upper
def test
puts Test.name
end
end
end
This would print:
▶ Test::Super.new("my")
#⇒ #<Test::Super:0x0055dae57fe390>
▶ Test::Upper.new.test
#⇒ "my"
You can make use of class variables in module, which is supported natively by Ruby.
You can do:
module Test
##name = ''
class Super
def initialize(name)
##name = name
end
end
class Upper
def test
puts ##name
end
end
end
a=Test::Super.new('My name')
b=Test::Upper.new()
b.test()

Adapter Pattern in ruby: Accessing Your Instance Variables

I am studying the adapter pattern implementation in ruby. I want to access an instance variable within the adapter module definition. Take a look at the following code:
module Adapter
module Dog
def self.speak
# I want to access the #name instance variable from my Animal instance
puts "#{name} says: woof!"
end
end
module Cat
def self.speak
# I want to access the #name instance variable from my Animal instance
puts "#{name} says: meow!"
end
end
end
class Animal
attr_accessor :name
def initialize(name)
#name = name
end
def speak
self.adapter.speak
end
def adapter
return #adapter if #adapter
self.adapter = :dog
#adapter
end
def adapter=(adapter)
#adapter = Adapter.const_get(adapter.to_s.capitalize)
end
end
To test it out I did the following:
animal = Animal.new("catdog")
animal.adapter = :cat
animal.speak
I want it to return the following:
catdog says: meow!
Instead it says:
Adapter::Cat says: meow!
Any tips on how I can get access to the Animal#name instance method from the adapter module? I think the issue is that my adapter methods are class-level methods.
Thanks!
You need to use your Module as a mixin and provide a way to keep track of which module is active, the methods don't seem to be overwritten by reincluding or reextending so I took the extend and remove methods I found here.
module Adapter
module Dog
def speak
puts "#{name} says: woof!"
end
end
module Cat
def speak
puts "#{name} says: meow!"
end
end
def extend mod
#ancestors ||= {}
return if #ancestors[mod]
mod_clone = mod.clone
#ancestors[mod] = mod_clone
super mod_clone
end
def remove mod
mod_clone = #ancestors[mod]
mod_clone.instance_methods.each {|m| mod_clone.module_eval {remove_method m } }
#ancestors[mod] = nil
end
end
class Animal
include Adapter
attr_accessor :name, :adapter
def initialize(name)
#name = name
#adapter = Adapter::Dog
extend Adapter::Dog
end
def adapter=(adapter)
remove #adapter
extend Adapter::const_get(adapter.capitalize)
#adapter = Adapter.const_get(adapter.capitalize)
end
end
animal = Animal.new("catdog")
animal.speak # catdog says: woof!
animal.adapter = :cat
animal.speak # catdog says: meow!
animal.adapter = :dog
animal.speak # catdog says: woof!
This is because name inside of the module context refers to something entirely different than the name you're expecting. The Animal class and the Cat module do not share data, they have no relationship. Coincidentally you're calling Module#name which happens to return Adapter::Cat as that's the name of the module.
In order to get around this you need to do one of two things. Either make your module a mix-in (remove self, then include it as necessary) or share the necessary data by passing it in as an argument to speak.
The first method looks like this:
module Adapter
module Dog
def self.speak(name)
puts "#{name} says: woof!"
end
end
end
class Animal
attr_accessor :name
attr_reader :adapter
def initialize(name)
#name = name
self.adapter = :dog
end
def speak
self.adapter.speak(#name)
end
def adapter=(adapter)
#adapter = Adapter.const_get(adapter.to_s.capitalize)
end
end
That doesn't seem as simple as it could be as they basically live in two different worlds. A more Ruby-esque way is this:
module Adapter
module Dog
def speak
puts "#{name} says: woof!"
end
end
end
class Animal
attr_accessor :name
attr_reader :adapter
def initialize(name)
#name = name
self.adapter = :dog
end
def adapter=(adapter)
#adapter = Adapter.const_get(adapter.to_s.capitalize)
extend(#adapter)
end
end

How to call super in an initialize method when both class inheritance and Module include is being used?

How does the look-path decide where to call "super" if I have a module included along with class inheritance. My hunch is that by default it will use the initialize method in the module. Is this correct? And if so, how do I explicitly tell the code to use the initialize method in the inherited class instead?
Posted below is an example:
I want the Employee class to inherit initialize from Other and not Subject.
module Subject
def initialize
#observers = []
end
end
class Other
def initialize
#other_stuff = []
end
end
class Employee < Other
include Subject
attr_reader :name
def initialize(name)
super()
end
end
My hunch is that by default it will use the initialize method in the module.
Correct. If a class includes a module then the methods of that module will be replace inherited methods of the same name.
And if so, how do I explicitly tell the code to use the initialize method in the inherited class instead?
You're probably best off refactoring so that you don't have this problem.
However, there are several ways you could make Other's initialize method get called instead of Subject's.
How about something like this:
module Subject
def initialize
puts "subject initialize"
#observers = []
end
end
class Other
def initialize
puts "other initialize"
#other_stuff = []
end
end
class Employee < Other
alias_method :other_initialize, :initialize
include Subject
attr_reader :name
def initialize(name)
other_initialize
end
end
Employee.new('test')
If you run this, you'll see that Other's initialize method is called. Writing code like this is not a good idea, however.

Calling a method when a Class name is used like a method

I'm wondering if one could define a class in Ruby to have the following sort of usage:
Class Book
def Book
puts self.to_s
end
def initialize(name)
#name = name
end
def to_s
#name.to_s
end
end
Usage:
Book "To Kill a Mocking Bird" #=>To Kill a Mocking Bird
The idea that I want is for this to behave like the following
An instance of the method is created (as a short hand).
The method Book is immediately called after this and performs a block of code.
(The intent of having the method named the same as the class is to have the call back when it is used like a method.)
Is this possible in Ruby?
How about this?
class Book
attr_accessor :name
def initialize(name)
self.name = name
end
def to_s
name
end
end
def Book(name)
Book.new(name)
end
puts Book("To Kill a Mocking Bird")
As a minor point of interest, Ruby's Kernel module uses such a technique (written in C) to implement methods named Array, String, and so on:
Array(12) #=> [12]
String(12) #=> '12'
Integer('0x12') #=> 18
Something like this ?
class Book
def show_me
puts self.to_s
end
def initialize(name)
#name = name
show_me
end
def to_s
#name.to_s
end
end
show_me will be executed but once you create a new book, the book object will be returned at the end.
>> Book.new "To Kill a Mocking Bird"
To Kill a Mocking Bird
=> #<Book:0x74f63f0 #name="To Kill a Mocking Bird">

Resources