In class Foo I'd like to include method Bar under certain conditions:
module Bar
def some_method
"orly"
end
end
class Foo
def initialize(some_condition)
if !some_condition
"bar"
else
class << self; include Bar; end
end
end
end
Is there any cleaner (and clearer) way to achieve the include in the method without having to do it inside the singleton class?
extend is the equivalent of include in a singleton class:
module Bar
def some_method
puts "orly"
end
end
class Foo
def initialize(some_condition)
extend(Bar) if some_condition
end
end
Foo.new(true).some_method # => "orly"
Foo.new(false).some_method # raises NoMethodError
Related
I have this module
module MyModule
def self.foo()
puts "A"
end
end
And this mixin class
class ParentClass
include MyModule
...other code
end
Can I overwrite the foo method in a child class like this or is there a better way?
class ChildClass < ParentClass
def self.foo()
puts "B"
end
... other code
end
You cannot include class methods directly:
module MyModule
def self.foo() puts "A" end
end
class ParentClass
include MyModule
end
ParentClass.methods.include?(:foo)
#=> false
ParentClass.instance_methods.include?(:foo)
#=> false
Instead, use Object#extend, which converts instance methods in MyModule to class methods in ParentClass:
module MyModule
def foo() puts "A" end
end
class ParentClass
extend MyModule
end
ParentClass.methods.include?(:foo)
#=> true
ParentClass.foo
A
class ChildClass < ParentClass
def self.foo() puts "B" end
end
ChildClass.foo
B
So, you may ask, what's the point of having class methods in a module? They can be present when the module is not used as a mixin, or has a combination of instance and class methods. The module's class methods are simply helper functions:
module A
def self.say
puts "It's a cat."
end
def say
puts "It's a dog."
end
end
class B
include A
end
A.say
It's a cat
B.new.say
It's a dog.
It is my impression that most modules that contain class methods tend to contain no instance methods; that is, they are not used as mix-ins. Readers are encouraged to correct me if I am wrong about that.
A common way to include some instance methods and extend others is to use the callback Module#included:
module A
def a; end
def self.included(klass)
klass.extend B
end
module B
def b; end
end
end
class C
include A
end
C.instance_methods.include?(:a)
#=> true
C.methods.include?(:b)
#=> true
I know, I can overwrite class method from module this way
class Foo
class << self
def some_static_method
puts 'some_static_method'
end
end
end
module BAR
class << Foo
def some_static_method
puts 'another_static_method'
end
end
end
class Foo
include BAR
end
Foo.some_static_method # => 'another_static_method'
Is it possible for an instance method?
You can do the following:
class Foo
def self.some_static_method; puts "Hello from Foo" end
end
module Bar
def self.included(base)
base.instance_eval do
def some_static_method; puts "Hello from Bar" end
end
end
end
class Foo
include Bar
end
Foo.some_static_method
This should work
UPDATE
To override instance method use:
class Foo
def some_instance_method; puts "Hello from Foo" end
end
module Bar
def self.included(base)
base.class_eval do
def some_instance_method; puts "Hello from Bar" end
end
end
end
class Foo
include Bar
end
Foo.new.some_instance_method
Your question is actually not about method overriding. It is about what class is referred to within a class ... construction in a module body.
When you do
module Bar
class << Foo
p self
end
end
# => #<Class:Foo>
the << Foo points to the singleton class of the Foo in the main environment because class << Foo cannot define the singleton class directly of a class Foo that has not been defined in advance. So it looks up for Foo that is already defined, and such class is found in the main environment.
When you do
module Bar
class Foo
p self
end
end
# => Bar::Foo
a new class Bar::Foo is created; the Foo points to this Bar::Foo that is newly created, and it does not point to the Foo in the main environment. In order to point to it, you have to explicitly specify that with ::.
module Bar
class ::Foo
p self
end
end
# => Foo
If you are using Ruby > 2.0.0 then what you can use is Module#prepend. Instead of include you can prepend an module and that way all of the module's methods are overriding any existing class instance methods with the same name. You can see a quick example here.
Prior to Ruby 2, Rails had introduced a similar hack: #alias_method_chain
Here is a nice comparison of the two approaches.
How I can get all instance method names in the baz method call, which are only present in the Bar module (without other instance methods of this class) ?
class Foo
include Bar
def a
end
def b
end
def baz
#HERE
end
end
class Foo
include Bar
def a
end
def b()
end
def baz
Bar.instance_methods(false)
end
end
puts Foo.new.baz
I have the following Ruby script, in which class Foo includes module Baz, module Baz has a included hook to make Bar extended by the including class (Foo). I am just wondering why:
class << klass
extend Bar #this doesn't work. Why?
end
does not work while:
klass.extend(Bar) works.
Here is my code:
#! /usr/bin/env ruby
module Baz
def inst_method
puts "dude"
end
module Bar
def cls_method
puts "Yo"
end
end
class << self
def included klass
# klass.extend(Bar) this works, but why the other approach below doesn't?
class << klass
extend Bar #this doesn't work. Why?
def hello
puts "hello"
end
end
end
end
end
class Foo
include Baz
end
foo = Foo.new
foo.inst_method
Foo.hello
Foo.cls_method
Within the body of class << klass, self refers to the singleton class of klass, not klass itself, whereas in klass.extend(Bar), the receiver is klass itself. The difference comes from there.
class A
end
class << A
p self # => #<Class:A> # This is the singleton class of A, not A itself.
end
p A # => A # This is A itself.
And since you want to apply extend to klass, doing it within the body of class << klass does not work.
What you want is invoke the extend method on the class object (klass) not the singleton class (class << klass).
Therefore the following code doesn't work because you are invoking the extend method on the singleton class:
class << klass
extend Bar # doesn't work because self refers to the the singleton class of klass
def hello
puts "hello"
end
end
I am trying to define some classes in Ruby that have an inheritance hierarchy, but I want to use one of the methods in the base class in the derived class. The twist is that I don't want to call the exact method I'm in, I want to call a different one. The following doesn't work, but it's what I want to do (basically).
class A
def foo
puts 'A::foo'
end
end
class B < A
def foo
puts 'B::foo'
end
def bar
super.foo
end
end
Probably, this is what you want?
class A
def foo
puts 'A::foo'
end
end
class B < A
alias bar :foo
def foo
puts 'B::foo'
end
end
B.new.foo # => B::foo
B.new.bar # => A::foo
A more general solution.
class A
def foo
puts "A::foo"
end
end
class B < A
def foo
puts "B::foo"
end
def bar
# slightly oddly ancestors includes the class itself
puts self.class.ancestors[1].instance_method(:foo).bind(self).call
end
end
B.new.foo # => B::foo
B.new.bar # => A::foo