Create dynamic method that takes argument - ruby

Is it possible to declare dynamic methods with define_method that does an instance_exec of a block with arguments ? Something like this :
class D
def self.adapt (method,*args,&impl)
define_method(method) do
instance_exec(args,impl)
end
end
end
D.adapt(:foo,a,b) { a + b }
puts D.new.foo(1,2)

Yes, you can:
class D < Struct.new(:c)
def self.adapt (method, &impl)
define_method(method) do |*args|
instance_exec(*args, &impl)
end
end
end
D.adapt(:foo) { |a, b| a + b + c }
puts D.new(3).foo(1, 2)
# => 6

Related

Ruby DSL nested constructs

I am using the following code to enforce context of DSL nested constructs. What are the other ways of achieving the same functionality?
def a &block
p "a"
def b &block
p "b"
def c &block
p "c"
instance_eval &block
end
instance_eval &block
undef :c
end
instance_eval &block
undef :b
end
# Works
a do
b do
c do
end
end
end
# Doesn't Work
b do
end
c do
end
Source
You asked about other ways, not the best way. So here's some examples :
Example A
class A
def initialize
p "a"
end
def b &block
B.new.instance_eval &block
end
end
class B
def initialize
p "b"
end
def c &block
C.new.instance_eval &block
end
end
class C
def initialize
p "c"
end
end
def a &block
A.new.instance_eval &block
end
Example B
A bit shorter :
def a &block
p "a"
A.new.instance_eval &block
end
class A
def b &block
p "b"
B.new.instance_eval &block
end
class B
def c &block
p "c"
C.new.instance_eval &block
end
class C
end
end
end
Example C
If you don't plan to have a d method for an A::B::C object :
def a &block
p "a"
A.new.instance_eval &block
end
class A
def b &block
p "b"
B.new.instance_eval &block
end
class B
def c &block
p "c"
instance_eval &block
end
end
end
Example D
This was a fun one :
def new_class_and_method(klass_name, next_klass=Object)
dynamic_klass = Class.new do
define_method(next_klass.name.downcase){|&block| p next_klass.name.downcase; next_klass.new.instance_eval &block}
end
Object.const_set(klass_name, dynamic_klass)
end
new_class_and_method("A", new_class_and_method("B", new_class_and_method("C")))
def a &block
p "a"
A.new.instance_eval &block
end
Example E
I dare say this doesn't look half bad:
def new_method_and_class(x)
define_method(x) do |&block|
p x
self.class.const_get(x.capitalize).new.instance_eval &block
end
self.const_set(x.capitalize, Class.new)
end
["a", "b", "c"].inject(Object){|klass,x| klass.instance_eval{new_method_and_class(x)} }
Example F
A bit more robust :
def new_method_and_class(x, parent_klass = Object)
parent_klass.class_eval do
define_method(x) do |&block|
p x
parent_klass.const_get(x.capitalize).new.instance_eval &block if block
end
end
parent_klass.const_set(x.capitalize, Class.new)
end
["a", "b", "c"].inject(Object){|klass,x| new_method_and_class(x,klass) }
Explanation
Example B
In example B, we first define :
an a() method
an A class
both are defined in main, because we want a() to be available directly.
a() method doesn't do much expect printing "a" and passing a block to an instance of A.
Then comes b() method. We don't want it to be available from main, so we define it inside A class. We want to continue with the nested methods, so we define a B class, which is also defined inside A. The B class is actually a A::B class. The A::B#b() method also prints "b", and passes a block to an instance of B.
We continue with A::B::C inside of A::B, just like we did with A::B and A.
Example F
Example F is basically like Example B, but written dynamically.
In example B, we defined an x method and an X class in every step, with the exact same structure. It should be possible to avoid code repetition with a method called new_method_and_class(x) which uses define_method, const_set and Class.new :
new_method_and_class("a") # <- Object#a() and A are now defined
a do
puts self.inspect
end
#=> "a"
# <A:0x00000000e58bc0>
Now, we want to define a b() method and a B class, but they shouldn't be in main. new_method_and_class("b") wouldn't do. So we pass an extra parameter, called parent_klass, which defaults to Object :
parent_klass = new_method_and_class("a")
new_method_and_class("b", parent_klass)
a do
b do
puts self.inspect
end
end
# => "a"
# "b"
# <A::B:0x00000000daf368>
b do
puts "Not defined"
end
# => in `<main>': undefined method `b' for main:Object (NoMethodError)
To define the c method, we just add another line :
parent_klass = new_method_and_class("a")
parent_klass = new_method_and_class("b", parent_klass)
parent_klass = new_method_and_class("c", parent_klass)
And so on and so on.
To avoid code repetition, we can use inject with the parent_klass as accumulator value :
["a", "b", "c"].inject(Object){|klass,x| new_method_and_class(x,klass) }
Bonus - Example G
Here's a modified code from Example F which works with a basic tree structure.
# http://stackoverflow.com/questions/40641273/ruby-dsl-nested-constructs/40641743#40641743
def new_method_and_class(x, parent_klass = Object)
parent_klass.class_eval do
define_method(x) do |&block|
p x.to_s
parent_klass.const_get(x.capitalize).new.instance_eval &block if block
end
end
parent_klass.const_set(x.capitalize, Class.new)
end
def create_dsl(branch,parent_klass = Object)
case branch
when Symbol, String
new_method_and_class(branch,parent_klass)
when Array
branch.each do |child|
create_dsl(child, parent_klass)
end
when Hash
branch.each do |key, value|
create_dsl(value, new_method_and_class(key,parent_klass))
end
end
end
methods_tree = {
:a => {
:b => [
:c,
:d
],
:e => :f,
:g => nil
}
}
create_dsl(methods_tree)
a do
b do
c do
puts self.inspect
end
d do
end
end
e do
f do
end
end
g do
puts self.inspect
end
end
# =>
# "a"
# "b"
# "c"
# #<A::B::C:0x0000000243dfa8>
# "d"
# "e"
# "f"
# "g"
# #<A::G:0x0000000243d918>

Dynamic rescue clauses, how does this code work?

def errors_matching(&block)
m = Module.new
(class << m ;self; end).instance_eval do
define_method(:===, &block)
end
m
end
This allows me to define dynamic rescue clauses in Ruby, for example:
begin
raise 'hi'
rescue errors_matching { |e| e.class.name.include?('Runtime') } => e
puts "Ignoring #{e.message}"
end
I don't understand the first piece of code. What's the whole point of m = Module.new and then putting self (which is main in this case) inside a singleton class and doing instance_eval on it?
This:
class << m; self end
is obviously the same as:
m.singleton_class
And
m.singleton_class.instance_eval { define_method(:foo) {} }
is just the same as
m.define_singleton_method(:foo) {}
So, the whole errors_matching method is just a very convoluted way of saying:
def errors_matching(&block)
Module.new.tap do |m| m.define_singleton_method(:===, &block) end
end

List of defined super methods for a certain class method in ruby

I am working on a system with a some complex class/mixin hierarchy. As there are several numerous layers scattered over many different files, I want to quickly see what the chain of super calls is for a given method.
For example
module AAA
def to_s
"AAA " + super()
end
end
module BBB
def to_s
"BBB " + super()
end
end
class MyArray < Array
include AAA
include BBB
def to_s
"MyArray " + super()
end
end
> MyArray.new.to_s
=> "MyArray BBB AAA []"
> method_supers(MyArray,:to_s)
=> ["MyArray#to_s", "BBB#to_s", "AAA#to_s", "Array#to_s", ...]
Perhaps something like this?
class A
def foo; p :A; end
end
module B
def foo; p :B; super; end
end
module C; end
class D < A
include B, C
def foo; p :D; super; end
end
p D.ancestors.keep_if { |c| c.instance_methods.include? :foo } # [D, B, A]
If that seems right, you could amend this function accordingly:
def Object.super_methods(method)
ancestors.keep_if { |c| c.instance_methods.include? method }
end
p D.super_methods(:foo) # [D, B, A]
def method_supers(child_class,method_name)
ancestry = child_class.ancestors
methods = ancestry.map do |ancestor|
begin
ancestor.instance_method(method_name)
rescue
nil
end
end
methods.reject!(&:nil?)
methods.map {|m| m.owner.name + "#" + m.name.to_s}
end

Ruby Factory Method rpsec temperature converter

not quite understanding factory method here...
here is the respec line:
Temperature.from_celsius(50).in_celsius.should == 50
Here is what I have now:
getting errors...not quite sure how to satisfy this. thanks
class Temperature
attr_accessor :f
attr_accessor :c
def initialize(args)
#f = args[:f]
#c = args[:c]
end
def in_fahrenheit
#f or
(#c*9.0/5.0)+32
end
def in_celsius
#c or
(#f-32)*(5.0/9.0)
end
def self.from_celsius(c)
new c
end
end
This should help
class Temperature
def initialize c
#c = c
end
def in_celsius
#c
end
def in_fahrenheit
#c *9.0 /5.0 +32
end
# factory pattern typically instantiates a new object
def self.from_celsius(c)
new c
end
end
puts Temperature.from_celsius(50).in_celsius #=> 50
puts Temperature.from_celsius(100).in_fahrenheit #=> 212
I would recommend against attr_accessor :c unless you want users to have public access to temp.c. Without it, users will be forced to use temp.in_celsius or temp.in_fahrenheit
You need to assign to :c in the initialize method. Then you need self.from_celsius to return a new instance of Temperature. You probably want something like this:
class Temperature
attr_accessor :c
def initialize c
#c = c
end
def in_celsius
#c
end
def in_fahrenheit
9/5 * #c + 32
end
def self.from_celsius(num)
Temperature.new(num)
end
def self.from_fahrenheit(num)
Temperature.new((num-32)*5/9)
end
end
Now rspec shows true
1.9.1p378 :047 > Temperature.from_celsius(50).in_celsius.should == 50
=> true
1.9.1p378 :131 > Temperature.from_fahrenheit(32).in_celsius.should == 0
=> true
The reason you're getting "error: Can't covert symbol to integer –" is because you're in your Temperature.from_celsius(50) you're passing it an integer when you're supposed to pass it a key & symbol for the options hash.
initialized
class Temperature
def initialize(opts = {})
#options = opts
end
class factory method
def self.from_celsius(x)
Temperature.new(:c => x)
end
instance method
def in_celsius
if #options[:c] == nil
return (#options[:f]-32) * (5/9.to_f)
else
return #options[:c]
end
end

How can I get a reference to a method?

Is it possible in Ruby to get a reference to methods of an object ( I would like to know if this can be done without procs/lambdas ) , for example , consider the following code :
class X
def initialize
#map = {}
setup_map
end
private
def setup_map
# #map["a"] = get reference to a method
# #map["b"] = get reference to b method
# #map["c"] = get referebce to c method
end
public
def call(a)
#map["a"](a) if a > 10
#map["b"](a) if a > 20
#map["c"](a) if a > 30
end
def a(arg)
puts "a was called with #{arg}"
end
def b(arg)
puts "b was called with #{arg}"
end
def c(arg)
puts "c was called with #{arg}"
end
end
Is it possible to do such thing ? I would like to avoid procs/lambdas because I want to be able to change the behaviour of A,B,C by subclassing .
You want Object#method:
---------------------------------------------------------- Object#method
obj.method(sym) => method
------------------------------------------------------------------------
Looks up the named method as a receiver in obj, returning a Method
object (or raising NameError). The Method object acts as a closure
in obj's object instance, so instance variables and the value of
self remain available.
class Demo
def initialize(n)
#iv = n
end
def hello()
"Hello, #iv = #{#iv}"
end
end
k = Demo.new(99)
m = k.method(:hello)
m.call #=> "Hello, #iv = 99"
l = Demo.new('Fred')
m = l.method("hello")
m.call #=> "Hello, #iv = Fred"
Now your code becomes:
private
def setup_map
#map = {
'a' => method(:a),
'b' => method(:b),
'c' => method(:c)
}
# or, more succinctly
# #map = Hash.new { |_map,name| _map[name] = method(name.to_sym) }
end
public
def call(arg)
#map["a"][arg] if arg > 10
#map["b"][arg] if arg > 20
#map["c"][arg] if arg > 30
end
You can do this with lambdas while maintaining the ability to change behavior in subclasses:
class X
def initialize
#map = {}
setup_map
end
private
def setup_map
#map["a"] = lambda { |a| a(a) }
#map["b"] = lambda { |a| b(a) }
#map["c"] = lambda { |a| c(a) }
end
public
def call(a)
#map["a"].call(a) if a > 10
#map["b"].call(a) if a > 20
#map["c"].call(a) if a > 30
end
def a(arg)
puts "a was called with #{arg}"
end
def b(arg)
puts "b was called with #{arg}"
end
def c(arg)
puts "c was called with #{arg}"
end
end
Ruby methods aren't first-class objects; it implements OO with message passing.
class X
def call(a)
self.send(:a, a) if a > 10
self.send(:b, a) if a > 20
self.send(:c, a) if a > 30
end
def a(arg)
puts "a was called with #{arg}"
end
def b(arg)
puts "b was called with #{arg}"
end
def c(arg)
puts "c was called with #{arg}"
end
end
Or just call them directly:
def call(a)
self.a(a) if a > 10
self.b(a) if a > 20
self.c(a) if a > 30
end
You can get a reference to the method by object.method(:method_name).
Eg: To get a reference to system method.
m = self.method(:system)
m.call('ls')

Resources