I'm trying to override a method from another gem. The code looks something like this:
module DatabaseCleaner
class Base
def orm_strategy(strategy)
# ...
end
end
end
In my gem:
require 'database_cleaner/base'
module DatabaseCleaner
class Base
def orm_strategy(strategy)
# New code
end
end
end
However, it the original is still being used. What am I doing wrong?
You are trying to override an instance method of the Foo::Bar class. You have to redefine the class's method:
module Foo
class Bar
def self.test # self == Bar
# New code
end
end
end
Related
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
I'm having some trouble understanding how to incorporate my own helper methods into a Ruby module.
My code:
module MyModule
def self.foo
bar
end
def bar
# helper for MyModule.foo
end
end
MyModule.foo
#=> NameError: undefined local variable or method `bar' for MyModule:Module
I'm not sure why MyModule cannot recognize the bar method. What aspect of scope in Ruby am I being oblivious to?
Modules can be integrated into classes as mixins. So, you need to include it in a class so it can be used with instance of that class.
As of now, you can make bar as your module method so it can be accessed as is.
module MyModule
def self.foo
bar
end
def self.bar
puts "Now it works"
end
end
MyModule.foo #=> Now it works
Ruby Docs
A Module is a collection of methods and constants. The methods in a
module may be instance methods or module methods. Instance methods
appear as methods in a class when the module is included, module
methods do not.
You are trying to call an instance method from a class method. You would have to write
module MyModule
def MyModule.foo
MyModule.bar
end
# Or you can have it this way
def MyModule::bar
# helper for MyModule.foo
end
end
MyModule.foo
to get what you want.
You're missing the scope of a method (module as well as instance) and basically it's lifecycle..
Module method way: -
Following is how you define the module with module methods.
module MyModule
def self.foo
puts "called self.foo"
bar
end
def self.bar
puts "self.bar got called"
# helper for MyModule.foo
end
end
Now, This way, you do not have to instantiate any object to call those methods.. Here is how you would call the methods (one inside the other)
MyModule.foo
Using a class to instantiate the Module and calling methods will not work as they are not instance methods.
Output -
called foo
bar got called
Instance method way: - Following is how you'll define the module with instance methods so that they will work between classes and objects..
module MyModule
def foo
puts "called foo"
bar
end
def bar
puts "bar got called"
# helper for MyModule.foo
end
end
class TestModule
include MyModule
end
Choosing to use the module methods this way you have to call the methods inside module as per below -
#instantiating module MyModule via class
myinstance = TestModule.new
myinstance.foo
Output -
called foo
bar got called
I see how to dynamically add a method to an instance in Ruby with def [instance].[methodname]; [...]; end.
However, I'm interested in attaching a method that exists in another location to a given instance. e.g.
def my_meth
puts self.foo
end
class MyCls
attr_accessor :foo
end
my_obj = MyCls.new
my_obj.my_meth
How could I simply attach my_meth to my_obj so that the method call in the final line of the foregoing code would work?
You could use include or extend to add a module to your class, eg. extend:
module Foo
def my_meth
puts self.foo
end
end
class MyCls
attr_accessor :foo
end
my_obj = MyCls.new
my_obj.extend(Foo)
my_obj.foo = "hello"
my_obj.my_meth
Unless you have a need to mix-in a module on the fly like this it's generally better to include your module like so:
class MyCls
include Foo
attr_accessor :foo
end
I'm trying to define a couple of modules to easily add in some instance and class methods to other classes, here's what I'm doing:
module Foo
module Bar
def speak
puts "hey there"
end
end
module Baz
extend Foo::Bar
def welcome
puts "welcome, this is an instance method"
end
end
end
class Talker
include Foo::Baz
end
Talker.new.welcome
Talker.speak
The output of this is:
welcome, this is an instance method
undefined method 'speak' for Talker.class (NoMethodError)
I was expecting Talker to have the 'speak' method since it includes Foo::Baz which itself extends Foo::Bar.
What am I missing?
You can try this:
module Baz
extend Foo::Bar
def self.included(base)
base.send :extend, Foo::Bar
end
def welcome
puts "welcome, this is an instance method"
end
end
This will auto-extend all classes in wich Baz is included.
PS:
extend Foo::Bar in module Baz was in original snippet, this code do not influence on method def self.included(base).
try this:
class Talker
extend Foo::Baz
end
since you want to call Talker.speak as a class method and not as an instance method (like Talker.new.speak) you have to include the Foo:Baz in a way that the class will take the methods itself.
One possibility is to use 'extend' (as above) the other is modifying it's eigenclass:
class Talker
class << self
include Foo::Baz
end
end
Have a look at the code below
initshared.rb
module InitShared
def init_shared
#shared_obj = "foobar"
end
end
myclass.rb
class MyClass
def initialize()
end
def init
file_name = Dir.pwd+"/initshared.rb"
if File.file?(file_name)
require file_name
include InitShared
if self.respond_to?'init_shared'
init_shared
puts #shared_obj
end
end
end
end
The include InitShared dosn't work since its inside the method .
I want to check for the file and then include the module and then access the variables in that module.
Instead of using Samnang's
singleton_class.send(:include, InitShared)
you can also use
extend InitShared
It does the same, but is version independent. It will include the module only into the objects own singleton class.
module InitShared
def init_shared
#shared_obj = "foobar"
end
end
class MyClass
def init
if true
self.class.send(:include, InitShared)
if self.respond_to?'init_shared'
init_shared
puts #shared_obj
end
end
end
end
MyClass.new.init
:include is a private class method, so you can't call it in instance level method. Another solution if you want to include that module only for specific instance you can replace the line with :include with this line:
# Ruby 1.9.2
self.singleton_class.send(:include, InitShared)
# Ruby 1.8.x
singleton_class = class << self; self; end
singleton_class.send(:include, InitShared)