Dynamically replace a method implementation with an object in Ruby

I would like to replace the method implementation for the object with the block that the user points to. In JavaScript, this is easy to accomplish:

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 lambda in an instance variable, and the method calls lambda, but I don't really like the utility 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 would like to have this syntax:

 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 disgusting for several parameters and still does not seem to be "correct." I could also define the set_bar method, but I don't like it :).

 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 the question is: is there a better way to do this, and if not, which way do you prefer

Edit: The @ welldan97 approach works, but I am losing the scope of a local variable, i.e.

 prefix = "*" def foo.bar x puts prefix + x.to_s end 

does not work. I suggest that I have to stick with lambda to work?

+6
source share
2 answers

use def :

 foo = Foo.new foo.bar "baz" def foo.bar x puts "*" + x.to_s end foo.bar "baz" 

yes this simple

Edit: In order not to lose scope, you can use define_singleton_method (as in @freemanoid's answer):

  prefix = "*" foo.define_singleton_method(:bar) do |x| puts prefix + x.to_s end foo.bar 'baz' 
+11
source

You can implement what you want:

 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.

+3
source

Source: https://habr.com/ru/post/911191/


All Articles