Re-defined method with a yield throws no block given - ruby

Can someone explain why the last yielder throws a no block given?
class Foo
def yielder
yield "hello"
end
end
class Mod
def initialize
##foo = Foo.new
end
def self.foo
##foo
end
end
worker = Mod.new
Mod.foo.yielder do |hello|
puts hello
end
Mod.foo.class.send(:define_method,:yielder) do
yield "new hello"
end
Mod.foo.yielder do |hello|
puts hello
end
Gives:
hello
test.rb:27:in `block in ': no block given (yield) (LocalJumpError)
from test.rb:30:in `'

A short introduction:
You don't need the Mod-instance, if you define ##foo outside initialize.
You don't need the Mod class to get the problem:
class Foo
def yielder
p 2
yield "hello"
end
end
foo = Foo.new
foo.yielder do |hello|
puts hello
end
foo.class.send(:define_method,:yielder) do
p 1
yield "new hello"
end
foo.yielder do |hello|
puts hello
end
You may shorten your example again:
class Foo
end
foo = Foo.new
foo.class.send(:define_method,:yielder) do
yield "new hello"
end
foo.yielder do |hello|
puts hello
end
This is the same as:
class Foo
define_method(:yielder) do
yield "new hello"
end
end
foo = Foo.new
foo.yielder do |hello|
puts hello
end
End of Introduction.
And now, I'm not sure if I understood correct what you want (and if I understand ruby correct ;) )
define_method accepts a block and use it as method body.
If the new method should receive a block on its own, you must define it in the interface of the definition and call it:
class Foo
define_method(:yielder) do | &prc |
prc.call("new hello")
end
end
foo = Foo.new
foo.yielder do |hello|
puts hello
end
Or the same logic in your example:
class Foo
def yielder
yield "hello"
end
end
class Mod
def initialize
##foo = Foo.new
end
def self.foo
##foo
end
end
worker = Mod.new
Mod.foo.yielder do |hello|
puts hello
end
Mod.foo.class.send(:define_method,:yielder) do | &prc |
prc.call "new hello"
end
Mod.foo.yielder do |hello|
puts hello
end
To make the code more robust, I would recommend some checks with block_given?.

Related

In Ruby, what's the best way to execute a block around a child method?

I have a parent class:
class Base
def my_method
block_method do
# EXECUTE WHATEVER'S IN THE CHILD VERSION OF my_method
# HOW TO DO?
end
end
def block_method
original_foo = 'foo'
foo = 'CUSTOM FOO'
yield
foo = original_foo
end
end
and some child classes--currently five and there will be more:
class ChildA < Base
def my_method
puts 'apple'
puts 'aardvark'
end
end
class ChildE < Base
def my_method
puts 'eel'
puts 'elephant'
end
end
I want to change what a variable refers to just for the duration of each Child's #my_method.
My thought is to do this by wrapping the functionality of each Child's #my_method in a block. But I'd rather do that in the parent class than have to wrap each child class's #my_method in the exact same block.
Any insights on how I can do this?
If there's some opposite of super, I guess that would be one way to accomplish what I want to do.
You could give the idea of what you are calling "some opposite of super" using a module and prepend like so:
module MethodThing
# added to remove binding from class since
# #my_method relies on the existence of #foo and #foo=
def self.prepended(base)
base.attr_reader(:foo) unless base.method_defined?(:foo)
base.attr_writer(:foo) unless base.method_defined?(:foo=)
end
def my_method
puts "Before: #{foo}"
original_foo = foo
self.foo= 'CUSTOM FOO'
begin
super
rescue NoMethodError
warn "(skipped) #{self.class}##{__method__} not defined"
end
self.foo = original_foo
puts "After: #{foo}"
end
end
Prepend the module on inheritance
class Base
def self.inherited(child)
child.prepend(MethodThing)
end
attr_accessor :foo
def initialize
#foo = 12
end
end
class ChildA < Base
def my_method
puts 'apple'
puts "During: #{foo}"
puts 'aardvark'
end
end
class ChildE < Base
end
Output:
ChildA.new.my_method
# Before: 12
# apple
# During: CUSTOM FOO
# aardvark
# After: 12
ChildE.new.my_method
# Before: 12
# (skipped) ChildE#my_method not defined
# After: 12
There are other strange ways to accomplish this with inheritance as well such as
class Base
class << self
attr_accessor :delegate_my_method
def method_added(method_name)
if method_name.to_s == "my_method" && self.name != "Base"
warn "#{self.name}#my_method has been overwritten use delegate_my_method instead"
end
end
end
attr_accessor :foo
def my_method
puts "Before: #{foo}"
original_foo = foo
self.foo= 'CUSTOM FOO'
begin
method(self.class.delegate_my_method.to_s).()
rescue NameError, TypeError
warn "(skipped) #{self.class} method delegation not defined"
end
self.foo = original_foo
puts "After: #{foo}"
end
end
class ChildA < Base
self.delegate_my_method = :delegation_method
def delegation_method
puts 'apple'
puts "During: #{foo}"
puts 'aardvark'
end
end
I could probably keep going with stranger and stranger ways to solve this problem but I think these will get you where you need to go.
One option would be to define a private or nodoc method which the parent class can call and is defined in each of the children.
class Parent
def my_method
block_method do
my_method_behavior
end
end
def block_method
original_foo = #foo
#foo = 'CUSTOM FOO'
yield
#foo = original_foo
end
end
class Child1 < Parent
def my_method_behavior
puts #foo
end
end
class Child2 < Parent
def my_method_behavior
puts #foo
end
end

Just for fun - add methods to an object via a block

Just for fun, again, but is it possible to take a block that contains method definitions and add those to an object, somehow? The following doesn't work (I never expected it to), but just so you get the idea of what I'm playing around with.
I do know that I can reopen a class with class << existing_object and add methods that way, but is there a way for code to pass that information in a block?
I guess I'm trying to borrow a little Java thinking here.
def new(cls)
obj = cls.new
class << obj
yield
end
obj
end
class Cat
def meow
puts "Meow"
end
end
cat = new(Cat) {
def purr
puts "Prrrr..."
end
}
cat.meow
# => Meow
# Not working
cat.purr
# => Prrrr...
EDIT | Here's the working version of the above, based on edgerunner's answer:
def new(cls, &block)
obj = cls.new
obj.instance_eval(&block)
obj
end
class Cat
def meow
puts "Meow"
end
end
cat = new(Cat) {
def purr
puts "Prrrr..."
end
}
cat.meow
# => Meow
cat.purr
# => Prrrr...
You can use class_eval(also aliased as module_eval) or instance_eval to evaluate a block in the context of a class/module or an object instance respectively.
class Cat
def meow
puts "Meow"
end
end
Cat.module_eval do
def purr
puts "Purr"
end
end
kitty = Cat.new
kitty.meow #=> Meow
kitty.purr #=> Purr
kitty.instance_eval do
def purr
puts "Purrrrrrrrrr!"
end
end
kitty.purr #=> Purrrrrrrrrr!
Yes
I suspect you thought of this and were looking for some other way, but just in case...
class A
def initialize
yield self
end
end
o = A.new do |o|
class << o
def purr
puts 'purr...'
end
end
end
o.purr
=> purr...
For the record, this isn't the usual way to dynamically add a method. Typically, a dynamic method starts life as a block itself, see, for example, *Module#define_method*.

Using methods from two different scopes?

How do I use methods from two different namespaces?
class Bar
def self.configure &block
new.instance_eval &block
end
def method2
puts "from Bar"
end
end
class Foo
def method1
puts "from Foo"
end
def start
Bar.configure do
method1
method2
end
end
end
Foo.new.start
In the above example the method1 can't be called because it is not from the Bar scope. How do I make methods from both scopes callable at the same time?
The trick is to forward missing method calls to the instance of Foo:
class Bar
def self.configure(&block)
o = new
context = eval('self', block.binding)
class << o; self; end.send(:define_method, :method_missing) do |meth, *args|
context.send(meth, *args)
end
o.instance_eval &block
end
def method2
puts "from Bar"
end
end
class Foo
def method1
puts "from Foo"
end
def start
Bar.configure do
method1
method2
end
end
end
Foo.new.start #=> "from Foo"
#=> "from Bar"
Try this:
class Bar
def initialize(foo)
puts "init"
#f = foo
end
def self.configure(foo, &block)
new(foo).instance_eval &block
end
def method2
puts "from Bar"
end
end
class Foo
def method1
puts "from Foo"
end
def start
Bar.configure(self) do
#f.method1
method2
end
end
end
This makes #f a class level instance variable of Bar, which is set when you initialize an object of Bar using new(foo) in Bar.configure. The block being passed assumes the existence of #f, which contains a reference to the object of class Foo.
It's a convoluted way of doing this - I can't think of anything better though. It'd be interesting to know the use case.
Maybe, this is the easiest way. It doesn't need to modify Bar.
class Bar
def self.configure &block
new.instance_eval &block
end
def method2
puts "from Bar"
end
end
class Foo
def method1
puts "from Foo"
end
def start
foo = self # add
Bar.configure do
foo.method1 # modify
method2
end
end
end
Foo.new.start
I would simplify your code as follows:
class Bar
def self.configure &block
obj = new
block.call(obj)
obj
end
def method2
puts "from Bar"
end
end
class Foo
def method1
puts "from Foo"
end
def start
Bar.configure do |obj|
method1
obj.method2
end
end
end
Foo.new.start
Block logic is clean and implementation doesn't require context switching. You are using the standard ruby functionality of passing parameters to the block.

Ruby Instance Eval

class Setter
attr_accessor :foo
def initialize
#foo = "It aint easy being cheesy!"
end
def set
self.instance_eval { yield if block_given? }
end
end
options = Setter.new
# Works
options.instance_eval do
p foo
end
# Fails
options.set do
p foo
end
Why does the 'set' method fail?
EDIT
Figured it out...
def set
self.instance_eval { yield if block_given? }
end
Needed to be:
def set(&blk)
instance_eval(&blk)
end
Yep - yield evaluates in the context within which it was defined.
Good write up here, but a simple example shows the problem:
>> foo = "wrong foo"
>> options.set do
?> p foo
>> end
"wrong foo"

Cross-cutting logging in Ruby

I'm trying to add logging to a method from the outside (Aspect-oriented-style)
class A
def test
puts "I'm Doing something..."
end
end
class A # with logging!
alias_method :test_orig, :test
def test
puts "Log Message!"
test_orig
end
end
a = A.new
a.test
The above works alright, except that if I ever needed to do alias the method again, it goes into an infinite loop. I want something more like super, where I could extend it as many times as I needed, and each extension with alias its parent.
Another alternative is to use unbound methods:
class A
original_test = instance_method(:test)
define_method(:test) do
puts "Log Message!"
original_test.bind(self).call
end
end
class A
original_test = instance_method(:test)
counter = 0
define_method(:test) do
counter += 1
puts "Counter = #{counter}"
original_test.bind(self).call
end
end
irb> A.new.test
Counter = 1
Log Message!
#=> #....
irb> A.new.test
Counter = 2
Log Message!
#=> #.....
This has the advantage that it doesn't pollute the namespace with additional method names, and is fairly easily abstracted, if you want to make a class method add_logging or what have you.
class Module
def add_logging(*method_names)
method_names.each do |method_name|
original_method = instance_method(method_name)
define_method(method_name) do |*args,&blk|
puts "logging #{method_name}"
original_method.bind(self).call(*args,&blk)
end
end
end
end
class A
add_logging :test
end
Or, if you wanted to be able to do a bunch of aspects w/o a lot of boiler plate, you could write a method that writes aspect-adding methods!
class Module
def self.define_aspect(aspect_name, &definition)
define_method(:"add_#{aspect_name}") do |*method_names|
method_names.each do |method_name|
original_method = instance_method(method_name)
define_method(method_name, &(definition[method_name, original_method]))
end
end
end
# make an add_logging method
define_aspect :logging do |method_name, original_method|
lambda do |*args, &blk|
puts "Logging #{method_name}"
original_method.bind(self).call(*args, &blk)
end
end
# make an add_counting method
global_counter = 0
define_aspect :counting do |method_name, original_method|
local_counter = 0
lambda do |*args, &blk|
global_counter += 1
local_counter += 1
puts "Counters: global##{global_counter}, local##{local_counter}"
original_method.bind(self).call(*args, &blk)
end
end
end
class A
def test
puts "I'm Doing something..."
end
def test1
puts "I'm Doing something once..."
end
def test2
puts "I'm Doing something twice..."
puts "I'm Doing something twice..."
end
def test3
puts "I'm Doing something thrice..."
puts "I'm Doing something thrice..."
puts "I'm Doing something thrice..."
end
def other_tests
puts "I'm Doing something else..."
end
add_logging :test, :test2, :test3
add_counting :other_tests, :test1, :test3
end
First choice: subclass instead of overriding:
class AWithLogging < A\
def test
puts "Log Message!"
super
end
end
Second choice: name your orig methods more carefully:
class A # with logging!
alias_method :test_without_logging, :test
def test
puts "Log Message!"
test_without_logging
end
end
Then another aspect uses a different orig name:
class A # with frobnication!
alias_method :test_without_frobnication, :test
def test
Frobnitz.frobnicate(self)
test_without_frobnication
end
end

Resources