How to monkey patch the Ruby module - ruby

I have this simple ruby module. I was able to monkey patch a Ruby class but this time I want to monkey patch a simple Ruby module, I tried using
this approach but it did not work. I thought that it will work because it works when I monkey patch a simple Ruby class.
Mymodule.class_eval do
def self.function1
"monkey patched function1 >>>"
end
end
This is the original Mymodule that we want to monkey patch:
module Mymodule
def self.function1
'this is function1'
end
end
sample usage:
puts Mymodule.function1()
# -> output: this is function1
Any idea on how to monkey patch the module?

You could simply re-open the class and redefine Mymodule.function1.
module Mymodule
def self.function1
'monkey patched function1 >>>'
end
end
That being said, your proposed code worked fine too:
module Mymodule
def self.function1
'this is function1'
end
end
Mymodule.class_eval do
def self.function1
'monkey patched funtion1 >>>'
end
end
puts Mymodule.function1
# monkey patched funtion1 >>>

Related

I came across this code which is about dropbox

Can anybody explain def self.extended(base), what does it mean here or any idea?
module Paperclip
module Storage
module Dropbox
def self.extended(base)
base.instance_eval do
#options[:dropbox_options] ||= {}
#options[:path] = nil if #options[:path] ==
self.class.default_options[:path]
#options[:dropbox_visibility] ||= "public"
#path_generator = PathGenerator.new(self, #options)
#dropbox_client # Force creation of dropbox_client
end
end
end
end
end
The self.extended method is called when the module is extended. It allows methods to be executed in the context of the base (where the module is extended).
You can try it yourself and understand this with a simple example code. Just paste in a file ruby file and run it.
Example for self.extended
module A
def self.extended(base)
puts "#{self} extended in #{base}"
end
end
class Apple
extend A
end
# This should print: "A extended in Apple"
Example for self.included
module A
def self.included(base)
puts "#{self} included in #{base}"
end
end
class Apple
include A
end
# This should print: "A included in Apple"
You can read more here: http://juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby/

prepend module with ActiveSupport::Concern ? ruby 2+

Module Baz
def foo
super
:baz
end
end
Class A
prepend Baz
def foo
:bar
end
end
A.new.foo //works fine
now if I transform my module to Concern module, it's not...
module BazConcern
extend ActiveSupport::Concern
included do
def foo
super
:baz
end
end
end
So how can we use prepend with ActiveSupport::Concern ? with ruby 2+
prepend with ActiveSupport::Concern (Rails 6.1+)
Rails 6.1 added support of prepend with ActiveSupport::Concern.
Please, see the following example:
module Imposter
extend ActiveSupport::Concern
# Same as `included`, except only run when prepended.
prepended do
end
end
class Person
prepend Imposter
end
It is also worth to mention that concerning is also updated:
class Person
concerning :Imposter, prepend: true do
end
end
Sources:
A link to the corresponding commit.
Rails allows a module with extend ActiveSupport::Concern to be prepended.
prepend and concerning docs.
It looks like there is a version of ActiveSupport::Concern that supports prepending available here: https://gist.github.com/bcardarella/5735987.
I haven't tried it out yet, but I might some day.
(Linked to from https://groups.google.com/forum/#!topic/rubyonrails-core/sSk9IEW74Ro)

Refine gem's class method

I have to wrap some behavior around an external gem in a elegant and isolated manner. Given the abstraction below, everything runs smoothly, but 'bar' is never printed.
Could someone tell me why?
My code:
module RefineGem
refine GemMainModule::GemClass do
def self.foo
p 'bar'
super
end
end
end
module Test
using RefineGem
def test
GemMainModule::GemClass.foo
end
end
class Testing
include Test
end
Testing.new.test
Gem code:
module GemMainModule
class Base
include GemMainModule::Fooable
end
class GemClass < Base
end
end
module GemMainModule
module Fooable
extend ActiveSupport::Concern
class_methods do
def foo
p 'zoo'
end
end
end
end
I doubt refinements work for class methods. You might refine the singleton_class though:
module RefineGem
refine GemMainModule::GemClass.singleton_class do
def foo
p 'bar'
super
end
end
end
I personally prefer to use Module#prepend to achieve the same functionality:
GemMainModule::GemClass.singleton_class.prepend(Module.new do
def foo
p 'bar'
super
end
end)

Ruby refinements with instance_eval

I'd like to provide some refinements to a DSL. I'm able to get refinements working with this example:
module ArrayExtras
refine Array do
def speak
puts 'array!'
end
end
end
module MyUniverse
using ArrayExtras
class Thing
def initialize
[1].speak
end
end
end
MyUniverse::Thing.new
This prints out "array!" just fine. But once I introduce instance_eval, the method can't be found:
module MyUniverse
using ArrayExtras
class DSL
def initialize(&block)
instance_eval(&block)
end
end
end
MyUniverse::DSL.new do
[1].speak
end
I get a undefined methodspeak' for [1]:Array (NoMethodError)`
Is there a way to get refinements working within an instance_eval?
Refinements are lexically scoped. You are activating the Refinement in the wrong lexical context. You need to activate it where you are calling the refined method:
module ArrayExtras
refine Array do
def speak
puts 'array!'
end
end
end
module MyUniverse
class DSL
def initialize(&block)
instance_eval(&block)
end
end
end
using ArrayExtras
MyUniverse::DSL.new do
[1].speak
end
# array!
In some cases you can achieve it by using ArrayExtras on the binding.
module MyUniverse
using ArrayExtras
class DSL
def initialize(&block)
block.binding.eval("using ArrayExtras")
instance_eval(&block)
end
end
end
MyUniverse::DSL.new do
[1].speak
end
This will however only work if you are not using your class in an instance, it only works if the binding is a module or class context, otherwise the eval will fail because main.using is permitted only at toplevel .
If you want to refine the ruby core BaseObject, you need to modify it as below.
module ArrayExtras
refine ::Array do
def speak
puts 'array!'
end
end
end
It will be found in top level class.

Calling class methods from a module that is included by that class in Ruby

In the code below, I would like to call the class method done of the class that includes the module from inside self.hello
Explanation:
A::bonjour will call Mod::hello so will B::ciao
I would like to be able to detect the "calling class" (A or B) in Mod::hello in order to be able to call the A::done or B::done
module Mod
def self.hello
puts "saying hello..."
end
end
class A
include Mod
def self.bonjour
Mod::hello
end
def self.done
puts "fini"
end
end
class B
include Mod
def self.ciao
Mod::hello
end
def self.done
puts "finitto"
end
end
While (perhaps) not as clean as Niklas' answer, it's still easily doable, and IMO cleaner than the usage pattern shown in the OP which relies on knowing which module is mixed in.
(I prefer not having to pass an argument to mixin methods like this when other means exist.)
The output:
pry(main)> A::bonjour
saying hello...
fini
pry(main)> B::ciao
saying hello...
finitto
The guts:
module Mod
module ClassMethods
def hello
puts "saying hello..."
done
end
end
def self.included(clazz)
clazz.extend ClassMethods
end
end
The modified class declarations, removing the explicit module reference:
class A
include Mod
def self.bonjour
hello
end
def self.done
puts "fini"
end
end
class B
include Mod
def self.ciao
hello
end
def self.done
puts "finitto"
end
end
You may also supply a default implementation of done:
module Mod
module ModMethods
def hello
puts "saying hello..."
done
end
def done
throw "Missing implementation of 'done'"
end
end
def self.included(clazz)
clazz.extend ModMethods
end
end
As a comment to this post points out, if the snippet in the OP is a faithful representation of the actual usecase, you might as well use extend (instead of include), leaving everything cleaner:
module Mod
def hello
puts "saying hello..."
done
end
def done
raise NotImplementError("Missing implementation of 'done'")
end
end
And the classes using extend:
class A
extend Mod
def self.bonjour
hello
end
def self.done
puts "fini"
end
end

Resources