I have the following kind of method definition:
method_name = :foo
method_arguments = [:bar, :baz]
method_mandatory_arguments = {:quux => true}
method_body = ->{ quux ? bar + baz : bar - baz }
So I want to get a real method. But define_method has no any possibility to define method arguments dynamically. I know another way to use class_eval but I know than defining methods with class_eval is much slower than define_method.
How I can effectively archive this?
I did some benchmarks in rails console:
class Foo; end
n = 100_000
Benchmark.bm do |x|
x.report('define_method') do
n.times { |i| Foo.send(:define_method, "method1#{i}", Proc.new { |a, b, c| a + b + c }) }
end
x.report('class_eval') do
n.times { |i| Foo.class_eval %Q{ def method2#{i}(a, b, c); a + b + c; end } }
end
end
So I've got the following results:
user system total real
define_method 0.750000 0.040000 0.790000 ( 0.782988)
class_eval 9.510000 0.070000 9.580000 ( 9.580577)
There is no simple way to implement what you are asking using either class_eval or define_method. Since method_body is a lambda it can only access local variables defined right before it.
method_body = ->{ quux ? bar + baz : bar - baz }
quux = true
method_body.call # undefined local variable or method ‘quux’
quux = true
method_body = ->{ quux ? bar + baz : bar - baz }
method_body.call # undefined local variable or method ‘bar’
I would suggest you to revise your requirements. If your method body should be a lambda, define all arguments for it. Then this is a natural fit for define_method:
method_body = ->(quux, bar = 0, baz = 0){ quux ? bar + baz : bar - baz }
define_method(method_name, &method_body)
If your method body can be a string, eval is the only option:
method_body = "quux ? bar + baz : bar - baz"
eval <<RUBY
def #{method_name} #{arguments}
#{method_body}
end
RUBY
Related
Some background: I have an external library that uses explicit type checking instead of duck-typing in one of its methods. Something like:
def a_method(value)
case value
when Array then 'an Array'
when Hash then 'a Hash'
when Foo then 'a Foo'
end
end
Foo is defined in the library. I would like to pass another object to this method that should be treated like a Foo. Therefore, I'm subclassing Foo:
class Bar < Foo
end
which works just fine:
bar = Bar.new
a_method(bar)
#=> 'a Foo'
Unfortunately, Foo implements several methods that will break the way Bar is supposed to work, including method_missing and respond_to?. For example:
class Foo
def respond_to?(method_name)
false
end
end
Because Foo is Bar's superclass, Foo#respond_to? is invoked when calling Bar#respond_to?:
class Bar < Foo
def hello
end
end
bar = Bar.new
bar.respond_to?(:hello) #=> false
bar.method(:respond_to?) #=> #<Method: Bar(Foo)#respond_to?>
I would like to remove or bypass Foo's method in this case (i.e. from within Bar) so that:
bar.respond_to?(:hello) #=> true
bar.method(:respond_to?) #=> #<Method: Bar(Kernel)#respond_to?>
just as if Foo#respond_to? did not exist.
Any suggestions?
Suppose you had this class structure:
class A
def respond_to?(meth)
"A"
end
def cat
end
def tap
puts "tap in A"
end
end
class B < A
def respond_to?(meth)
"B"
end
end
class C < B
def respond_to?(meth)
"C"
end
end
class D < C
end
We have:
D.ancestors
#=> [D, C, B, A, Object, Kernel, BasicObject]
Further, you want:
D.new.respond_to?(:cat)
#=> true
If we write:
class D < C
def respond_to?(meth)
method(__method__).super_method.call(meth)
end
end
then:
D.new.respond_to?(:cat)
#=> C
This is not surprising, since it's the same as:
class D < C
def respond_to?(meth)
super
end
end
D.new.respond_to?(:cat)
#=> C
Now try:
class D < C
def respond_to?(meth)
method(__method__).super_method.super_method.call(meth)
end
end
then:
D.new.respond_to?(:cat)
#=> B
so we have skipped over C. Now redefine D#respond_to? as follows:
class D < C
def respond_to?(meth)
method(__method__).super_method.super_method.super_method.call(meth)
end
end
D.new.respond_to?(:cat)
#=> A
Once more:
class D < C
def respond_to?(meth)
method(__method__).super_method.super_method.super_method.super_method.call(meth)
end
end
D.new.respond_to?(:cat)
#=> True (invokes Kernel#respond_to?)
Therefore, you could do the following:
module JumpOver
def jump_over(over_mod, meth)
m = instance_method(meth)
loop do
m = m.super_method
break if m.owner > over_mod
end
define_method(meth, m)
end
end
class D
extend JumpOver
jump_over(A, :respond_to?)
jump_over(A, :tap)
end
D.methods.include?(:jump)
#=> true
D.instance_methods(false)
#=> [:respond_to?, :tap]
d = D.new
#=> #<D:0x007ff263161b58>
d.respond_to? :cat
#=> true
d.respond_to? :dog
#=> false
d.tap { |o| puts 'hi' }
# 'hi'
#=> #<D:0x007ff263161b58>
I don't understand the problem. Just implement a respond_to? in Bar and don't call super.
class Foo
def respond_to?(method)
puts 'in foo'
false
end
end
class Bar < Foo
def respond_to?(method)
puts 'in bar'
true
end
end
bar = Bar.new
bar.respond_to?(:quux) # => true
# >> in bar
This is, of course, a violation of LSP, but you specifically asked for it, so... :)
Ruby's method lookup seems to be "hard-coded". I couldn't find a way to alter it from within Ruby.
Based on Sergio Tulentsev's suggestion to re-implement the methods, however, I came up with a helper method to replace / overwrite every superclass (instance) method with its "super" method (or undefine it if there is none):
def self.revert_superclass_methods
superclass.instance_methods(false).each do |method|
super_method = instance_method(method).super_method
if super_method
puts "reverting #{self}##{method} to #{super_method}"
define_method(method, super_method)
else
puts "undefining #{self}##{method}"
undef_method(method)
end
end
end
This has basically the same effect for my purposes. Example usage:
module M
def foo ; 'M#foo' ; end
end
class A
include M
def to_s ; 'A#to_s' ; end
def foo ; 'A#foo' ; end
def bar ; 'A#bar' ; end
end
class B < A
def self.revert_superclass_methods
# ...
end
end
b = B.new
b.to_s #=> "A#to_s"
b.foo #=> "A#foo"
b.bar #=> "A#bar"
B.revert_superclass_methods
# reverting B#to_s to #<UnboundMethod: Object(Kernel)#to_s>
# reverting B#foo to #<UnboundMethod: Object(M)#foo>
# undefining B#bar
b.to_s #=> "#<B:0x007fb389987490>"
b.foo #=> "M#foo"
b.bar #=> undefined method `bar' for #<B:0x007fb389987490> (NoMethodError)
I often need to get the pure class name of an object, as code below:
class Foo
class Bar
end
end
obj = Foo::Bar.new
puts obj.class.name # It shows "Foo::Bar", while what I want is just "Bar"
I know it can be done by obj.class.name.split('::').last, but, shouldn't be there a method just return "Bar" ?
In vanilla Ruby, I'm pretty sure class.name.split('::').last is the best way to go. But if you happen to be using ActiveSupport (if you're on Rails, this library will be loaded) there is an inflector called demodulize.
class Foo
class Bar
end
end
obj = Foo::Bar.new
puts obj.class.name.demodulize # => Bar
Classes don't really have "names" in Ruby. A class is an object like any other object, it gets assigned to variables like any other object. A class doesn't have a name, just like a number or a string doesn't have a name.
Take this example:
foo = Class.new
Bar = foo
Baz = foo
Bar = nil
remove_const :Bar
What should the "name" of the class be in this example?
Well, there is a method called name in the Module class. What it does is the following: if the class object has been assigned to a constant for the first time, the name of that constant (note that even that is a fuzzy concept!) becomes the name of the class, otherwise the name will just be nil.
Expanding on the example above:
foo = Class.new
foo.name
# => nil
Bar = foo
foo.name
# => 'Bar'
Bar.name
# => 'Bar'
Baz = foo
Baz.name
# => 'Bar'
Bar = nil
remove_const :Bar
foo.name
# => 'Bar'
Here's another example:
foo = Class.new
Bar = foo
Baz = foo
class Baz
class Quux; end
Xyzzy = Quux
end
foo::Xyzzy.name
# => 'Bar::Quux'
Note that even though Quux is defined inside Baz and accessed via foo::Xyzzy it still prints Bar::Quux as its name.
Also, two different classes can have the same name:
Foo = Class.new
Bar = Foo
Foo = nil
remove_const :Foo
Foo = Class.new
Baz = Foo
Foo = nil
remove_const :Foo
Bar.name
# => Foo
Baz.name
# => Foo
Bar == Baz
# => false
The "name" of a class is simply a debugging help for human readers, you should never use it for anything else, and never use it programmatically (or even depend on a specific structure of the string).
based on thomax answer i've researched it a bit...
require 'benchmark'
class String
def demodulize
self[(rindex('::') || -2) + 2..-1]
end
def split_last
split('::').last
end
def demodulize_vs_split_last n = 5000000
Benchmark.bm(10) do |x|
x.report('split_last') { n.times { split_last } }
x.report('demodulize') { n.times { demodulize } }
end
end
end
and got this:
> 'Core::String'.demodulize_vs_split_last
user system total real
split_last 1.960688 0.008501 1.969189 ( 1.983187)
demodulize 1.807694 0.005815 1.813509 ( 1.826771)
for short strings it mostly similar
> 'Core::Exten::sions::String::Inf::lect::ions'.demodulize_vs_split_last
user system total real
split_last 4.386797 0.024131 4.410928 ( 4.447739)
demodulize 1.875757 0.005089 1.880846 ( 1.895737)
but split('::').last becomes much slower if your class is nested deeper
No, there isn't a method to return just the name of the class. Technically, the name is only accurate if it includes any outer modules and classes.
The #name method is defined on the Module class; see the documentation for more information.
say I've got 2 Classes:
Class Foo
attr_accessor :bar
end
Class Baz < Foo
end
I'm creating an Instance of Foo and then want to have an Instance of Baz with the Data of the Foo Instance in it:
f = Foo.new(:bar => "Hi World")
# Doesnt work?
b = Baz.new(f)
How to do it?
an instance of Baz with the data of the Foo instance in it
Since your constructor already accepts attributes as a hash, you could create a method to return Foo's attributes as a hash:
class Foo
attr_accessor :bar
def initialize(attributes={})
#bar = attributes[:bar]
end
def attributes
{:bar => bar}
end
end
class Baz < Foo
end
Now you can create a Baz instance from these attributes:
f = Foo.new(:bar => "Hi World") #=> #<Foo:0x007fd09a8614c0 #bar="Hi World">
f.attributes #=> {:bar=>"Hi World"}
b = Baz.new(f.attributes) #=> #<Baz:0x007fd09a861268 #bar="Hi World">
I'd like to replace the implementation of a method for an object with a block that the user specifies. In JavaScript, this is easily accomplished:
function Foo() {
this.bar = function(x) { console.log(x) }
}
foo = new Foo()
foo.bar("baz")
foo.bar = function(x) { console.error(x) }
foo.bar("baz")
In C# it is also quite easy
class Foo
{
public Action<string> Bar { get; set; }
public Foo()
{
Bar = x => Console.WriteLine(x);
}
}
var foo = Foo.new();
foo.Bar("baz");
foo.Bar = x => Console.Error.WriteLine(x);
foo.Bar("baz");
But how can I do the same in Ruby? I have a solution that stores a lambda in an instance variable and the method calls the lambda, but I don't really like the overhead and syntax
class Foo
def initialize
#bar = lambda {|x| puts x}
end
def bar x
#bar.call x
end
def bar= blk
#bar = blk
end
end
foo = Foo.new
foo.bar "baz"
foo.bar= lambda {|x| puts "*" + x.to_s}
foo.bar "baz"
I'd like to have a syntax like that:
foo.bar do |x|
puts "*" + x.to_s
end
foo.bar "baz"
I came up with the following code
class Foo
def bar x = nil, &blk
if (block_given?)
#bar = blk
elsif (#bar.nil?)
puts x
else
#bar.call x
end
end
end
But this is kinda ugly for more than one parameter and still doesn't feel 'right'. I could also define a set_bar method, but i don't like that either :).
class Foo
def bar x
if (#bar.nil?)
puts x
else
#bar.call x
end
end
def set_bar &blk
#bar = blk
end
end
So question is: Is there a better way do to do this and if not, what way would you prefer
Edit:
#welldan97's approach works, but i loose the local variable scope, i.e.
prefix = "*"
def foo.bar x
puts prefix + x.to_s
end
doesn't work. I suppose I have to stick with lambda for that to work?
use def:
foo = Foo.new
foo.bar "baz"
def foo.bar x
puts "*" + x.to_s
end
foo.bar "baz"
yes, that simple
Edit: To not loose the scope you can use define_singleton_method(as in #freemanoid answer):
prefix = "*"
foo.define_singleton_method(:bar) do |x|
puts prefix + x.to_s
end
foo.bar 'baz'
You can implement what you want like this:
class Foo; end
foo = Foo.new
prefix = '*'
foo.send(:define_singleton_method, :bar, proc { |x| puts prefix + x.to_s })
foo.bar('baz')
"*baz" <<<<<-- output
This is absolutely normal and correct in ruby.
If I have two objects, one being referenced in another. Then in the first object can I write a method which will give me which other objects it is being referenced in?
I am not sure how to do it out of the box, but maybe the following post might help you:
What is a ruby object? (introducing Memprof.dump)
Perhaps digging around in ObjectSpace could help:
#!/usr/bin/ruby1.8
include ObjectSpace
def print_references_to_foos
for klass in [Bar, Baz]
each_object(klass) do |o|
s = o.inspect
puts s if s =~ /#<Foo/
end
end
end
class Foo
end
class Bar
def initialize(foo)
#foo = foo
end
end
class Baz < Bar
end
foo1 = Foo.new
foo2 = Foo.new
foo3 = Foo.new
bar1 = Bar.new(foo1)
bar2 = Bar.new(foo1)
bar3 = Baz.new(foo2)
print_references_to_foos
# => #<Baz:0xb7e09158 #foo=#<Foo:0xb7e091a8>>
# => #<Bar:0xb7e0916c #foo=#<Foo:0xb7e091d0>>
# => #<Bar:0xb7e09180 #foo=#<Foo:0xb7e091d0>>
# => #<Baz:0xb7e09158 #foo=#<Foo:0xb7e091a8>>