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:yes, that simple
Edit: To not loose the scope you can use
define_singleton_method(as in @freemanoid answer):