Preventing methods from being re-defined - ruby

So, I need to make a method within a class protected from re-definition. I am not really sure how else to explain it so here's the code:
module Foo
def bar
p "lol"
end
end
Now, that's the original Foo#bar method and I need it to be like a constant. So, I did come up with a solution. That was to save the method in a Constant and detect when someone tried changing it it would simply re-load it from that constant (it was a Proc object):
module Foo
Original_bar = Proc.new { p "lol" }
def bar
Original_bar.call
end
def self.method_added(method_name)
if method_name == :bar
def Foo::bar
Original_bar.call
end
end
end
end
But this isn't completely safe since once could use the same "trick" I did to bypass method_added and I am not really fond of this, since it's not very ruby-like.
Tests
A normal test:
module Foo
def bar
p "lmao"
end
end
Foo::bar # => "lol"
And another test using the trick:
def Foo::bar
p "rofl"
end
Foo::bar # => "rofl"
tl;dr
How to make a method in Ruby "unredefinable" (if that's even a word)?

If you freeze the module that should prevent method being added to it.
Note that a c extension can unfreeze variables (see evil.rb for example.

You can make your code warn you that a method has been over-ridden by turning on warnings:
$VERBOSE = true
module Foo
def self.bar
p "lmao"
end
end
def Foo::bar
p "rofl"
end
(irb):9: warning: method redefined; discarding old bar
It may be possible, but not practical, to raise an exception when warnings are issued.
This won't warn you if you first undefine Foo.bar, however.

Related

Is there a way to know if a ruby method has been redefined?

I want to know if a method on a class gets redefined.
use case: in version 1 of ruby-clock, defining a method on an object was part of the API. In version 2, doing so will break behavior, and a different API should be used.
what I ended up doing: https://github.com/jjb/ruby-clock/pull/28/files
The best would be if I could use some sort of metaprogramming to notice when it happens.:
# this is example code which does not work
class MyClass
def my_method
puts "hello"
end
# this is not real! it's an example of what i hope is somehow possible
def self.method_defined(m, *args, &block)
if :my_method == m
raise "you should not redefine my_method!"
end
end
end
Also acceptable would be if I could check back later and see if it has changed.
# this is example code which does not work
class MyClass
def my_method
puts "hello"
end
##my_method_object_id = get_method_object_id(:my_method)
end
# later, when app initialization is done, check if the user redefined the method
# this is not real! it's an example of what i hope is somehow possible
def self.method_defined(m, *args, &block)
if MyClass.get_method_object_id(:my_method) != MyClass.my_method_object_id
raise "you should not redefine my_method!"
end
end
n.b. that MyClass.method(:my_method) is a real thing in ruby, but it always produces a new object.
This sounds pretty much like XY problem. I don't think you should go this way - if you find yourself fighting with the Ruby object model, you most probably picked the wrong tool for the job.
But for the sake of completeness... There are a couple of callbacks in Module that can help with the ideas as crazy as this one. In particular, there is Module#method_added which is called each time some instance method is defined. So we can do smth. like this (very dirty example):
class C
#foo_counter = 0
def self.method_added(name)
if name == :foo
#foo_counter += 1
if #foo_counter > 1
raise "Don't redefine foo!"
end
end
end
def foo
"foo"
end
end
now if one tries to open C and redefine foo the following happens (pry session as an example):
pry(main)> class C
pry(main)* def foo
pry(main)* "bar"
pry(main)* end
pry(main)* end
RuntimeError: Don't redefine foo!
So you can do what you want to (to some extent - it's Ruby, so what stops one from redefining the annoying hook first? :)), but please don't. You'll get nothing but troubles...
You could use the callback BasicObject#singleton_method_added.
class C
#class_methods = []
def self.singleton_method_added(m)
puts "Method #{m} was just redefined" if #class_methods.include?(m)
#class_methods << m
end
def self.c
puts 'hi'
end
end
C.c
#=> "hi"
class C
def self.c
puts 'ho'
end
end
Method c was just redefined
C.c #=> "ho"

How to call any instance method in ruby object without instantiating it?

I am creating a helper module to initialize the object before calling its methods
module Initialized
extend ActiveSupport::Concern
class_methods do
def run(*args)
new(*args).run
end
def call(*args)
new(*args).call
end
def execute(*args)
new(*args).create
end
end
end
So instead of defining run, call, and execute in my helper module I need to receive any method name and check if it exists on the main class after initializing it, then call the requested instance method if exists in the main class or raise an error if not
I would say my targeted code would be something like this
module Initialized
extend ActiveSupport::Concern
class_methods do
def _(*args, methodname)
new(*args).try(:send, "#{methodname}") || raise 'Method not exist'
end
end
end
Sample usage would be
class MyClass
include Initialized
def initialize(param1)
#param1 = param1
end
def call
puts "call '#{#param1}'"
end
end
then calling
MyClass.call('method param')
I found these links but couldn't find my answer yet:
meta-dynamic-generic-programming-in-ruby
ruby-module-that-delegates-methods-to-an-object
template-methods-in-ruby
Despite the fact method_missing would do the job, I'd probably avoid it in this case in favor of a more explicit delegation. Simple and dirty example:
module InstanceDelegator
def delegate_to_instance(*methods)
methods.each do |method_name|
define_singleton_method method_name do |*args|
new(*args).public_send(method_name)
end
end
end
end
class Foo
extend InstanceDelegator
delegate_to_instance :bar # <- define explicitly which instance methods
# should be mirrored by the class ones
def bar
puts "bar is called"
end
def baz
puts "baz is called"
end
end
# and then
Foo.bar # => bar is called
Foo.baz # NoMethodError ...
# reopening works too
class Foo
delegate_to_instance :baz
end
Foo.baz # baz is called
Pros:
you don't need to redefine method_missing (less magic -> less pain when you debug the code)
you control precisely which instance methods to be wrapped with the class level "shorthand" (fewer chances to delegate something you don't want to - more robust code)
(minor) no need to raise NoMethodError explicitly - you can fully rely on the core dispatching as it is...
I found another solution instead of using a module,
I can use the class method self.method_missing
def self.method_missing(method_name, *args, &block)
obj = new(*args)
raise NoMethodError, "undefined method `#{method_name}' for #{self}:Class" unless obj.respond_to?(method_name)
obj.send(method_name, &block)
end
But the limitation is that I have to copy it into every class whenever I need to use this feature

Ruby refinements subtleties

There is a pretty good documentation of the current implementation of refinements in ruby here:
http://ruby-doc.org//core-2.2.0/doc/syntax/refinements_rdoc.html,
but there are some strange corner cases.
First, include module is orthogonal to using module (one include the instance method of module while the other activates the refinement). But there is a trick to include a refinement module itself, see
Better way to turn a ruby class into a module than using refinements?.
def to_module(klass)
Module.new do
#note that we return the refinement module itself here
return refine(klass) {
yield if block_given?
}
end
end
class Base
def foo
"foo"
end
end
class Receiver
include to_module(Base) {
def foo
"refined " + super
end
}
end
Receiver.new.foo #=> "refined foo"
Strangely this refinement module can't be used with using!
m=to_module(Base) {}
m.class #=> Module
using m
#=>TypeError: wrong argument type Class (expected Module)
So using only work on the enclosing module of the refinement modules.
Secondly I wanted to use the above yield trick to be able to pass a Proc to refine (even through it only accepts a block), without resorting to converting the Proc back to source as in
https://www.new-bamboo.co.uk/blog/2014/02/05/refinements-under-the-knife/.
But using yield as in the include example does not work:
def ref_module1(klass)
Module.new do
refine(klass) {
yield
}
end
end
class Receiver1
using ref_module1(Base) {
def foo
"refined " + super
end
}
def bar
Base.new.foo
end
end
Receiver1.new.bar #=> NoMethodError: super: no superclass method `foo'
We see that Receiver1 still use Bar#foo and not the refined method.
Howewer we can use module_eval instead:
def ref_module2(klass,&b)
Module.new do
refine(klass) {
module_eval(&b)
}
end
end
class Receiver2
using ref_module2(Base) {
def foo
"refined " + super
end
}
def bar
Base.new.foo
end
end
Receiver2.new.bar #=> "refined foo"
I don't quite understand why module_eval works here and not the yield method. Inside the refinement block, the 'default_definee' is the refinement module, so module_eval which puts the 'default_definee' to self='the refinement module' should not affect it. And indeed in the 'include' example at the beginning, I get the same result when I use module_eval or a direct yield.
Can anyone explain this behavior?
Context (or binding) is the reason why module_eval works and yield doesn't in your last set of examples. It actually has nothing to do with refinements, as demonstrated below.
Starting with module_eval:
class Foo
def run(&block)
self.class.module_eval(&block)
end
end
foo = Foo.new
foo.run {
def hello
"hello"
end
}
puts foo.hello # => "hello"
puts hello => # '<main>': undefined method 'hello' for main:Object (NameError)
In Foo#run we call module_eval on Foo. This switches the context (self) to be Foo. The result is much like we had simple defined hello inside of class Foo originally.
Now let's take a look at yield:
class Foo
def run
yield
end
end
foo = Foo.new
foo.run {
def hello
"hello"
end
}
puts hello # => "hello"
puts foo.hello # => '<main>': private method 'hello' called for ...
yield simply invokes the block in its original context, which in this example would be <main>. When the block is invoked, the end result is exactly the same as if the method were defined at the top level normally:
class Foo
def run
yield
end
end
foo = Foo.new
def hello
"hello"
end
puts hello # => "hello"
puts foo.hello # => '<main>': private method 'hello' called for ...
You might notice that foo seems to have the hello method in the yield examples. This is a side effect of defining hello as a method at the top level. It turns out that <main> is just an instance of Object, and defining top level methods is really just defining private methods on Object which nearly everything else ends up inheriting. You can see this by opening up irb and running the following:
self # => main
self.class # => Object
def some_method
end
"string".method(:some_method) # => #<Method: String(Object)#some_method>
Now back to your examples.
Here's what happens in the yield example:
def ref_module1(klass)
Module.new do
refine(klass) {
yield
}
end
end
class Receiver1
# like my yield example, this block is going to
# end up being invoked in its original context
using ref_module1(Base) {
def foo
"I'm defined on Receiver1"
end
}
def bar
# calling foo here will simply call the original
# Base#foo method
Base.new.foo
end
end
# as expected, if we call Receiver1#bar
# we get the original Base#foo method
Receiver1.new.bar # => "foo"
# since the block is executed in its original context
# the method gets defined in Receiver1 -- its original context
Receiver1.new.foo # => "I'm defined on Receiver1"
As for module_eval, it works in your examples because it causes the block to be run in the context of the new module, rather than on the Receiver1 class.

Ruby Singleton avoid using instance member

I like Ruby's singleton but I would like to make usage of it better so here is example
require 'singleton'
class Foo
include Singleton
def initialize
# code to setup singleton here
end
def self.get_bar
Foo.instance.get_bar
end
def get_bar
end
def get_nar
end
end
Usage
Foo.instance.get_bar (default) or Foo.get_bar (due to static self.get_bar method I made)
Is there elegant way to make all methods accessible without me having to write static wrapper for each method? Just seems redundant to have to write for each method .instance
UPDATE
Ruby 1.8.7
You could mix this module:
module DelegateToSingleton
def respond_to_missing?(method)
super || instance.respond_to?(method)
end
def method_missing(method, *args)
instance.send(method, *args)
end
end
into your singleton:
class Foo
extend DelegateToSingleton
include Singleton
def foo
'foo'
end
def bar
'bar'
end
end
with these results:
p Foo.foo # => "foo"
p Foo.bar # => "bar"
DelegateToSingleton::method_missing is what makes it work: Whenever Foo receives a method it doesn't know about, it just forwards it to its instance.
DelegateToSingleton::respond_to_missing? is not strictly needed, but having it is good manners whenever playing tricks with method_missing.
For Ruby earlier than 1.9.2: Override respond_to? instead of respond_to_missing?
Just separate the class from the instance:
class Foo
def initialize
end
def get_bar
end
def get_nar
end
end
MyFoo = Foo.new
MyFoo.get_bar

Including methods into blocks

Anyone know how to get this to work if it's possible?
class Foo
def self.go(&block)
class << block
include Bar
end
puts "Within Foo#go: #{block.methods.include? 'baz'}"
block.call
end
end
module Bar
def baz
puts "I'm happily in my place!"
end
end
Foo.go {
puts "Within actual block: #{methods.include? 'baz'}"
baz
}
This gets the output:
Within Foo#go: true
Within actual block: false
NameError: undefined local variable or method ‘baz’ for main:Object
EDIT: when I print out the block's class in Foo#go, it's Proc, but when I print it out within the Proc, it's Object. Could this be related?
You can't do this. The reason for what you're seeing is that there are two different contexts here. One is the context of the block, which closes over the context where it's defined. The other is the context of the Proc object wrapper, which is just the same as any other object context and completely unrelated to the context of the block itself.
I think the closest you'll get is to instance_eval the block using a context object that has the methods you want, but then the block won't have access to the self that existed where it was defined. It's up to you whether that makes sense for the method you want to write.
The other option is to pass the block an actual receiver for the baz method.
You could use eval with Proc#binding:
module Bar
def baz
puts "hi from baz!"
end
end
def go(&block)
eval('include Bar', block.binding)
block[]
end
baz #=> NameError
go { baz } #=> "hi from baz!"
baz #=> "hi from baz!"
But unless you use a mixin/mixout framework (like mixico or mixology), you'll be placing the methods from the included module into the lexical scope, so they'll still be accessible once the block returns.
require 'rubygems'
require 'mixico'
module Bar
def baz
puts "hi from baz!"
end
end
def go(&block)
Module.mix_eval(Bar, &block)
end
baz #=> NameError
go { baz } #=> "hi from baz!"
baz #=> NameError
Here's a good article on different ways to use a DSL from within a block.
Another alternative, following on from rampion, is to dup the context of the block before mixing into it, this way you're not messing up the context after you've finished.
module Bar
def baz
puts "hi from baz!"
end
end
def go(&block)
dup_context = eval('self', block.binding).dup
dup_context.send(:include, Bar)
dup_context.instance_eval &block
end
Note this will only be useful to you if you're not running any mutator methods in the block

Resources