How to implement autovivification for Ruby structs? - ruby

Ruby has support for autovivification for hashes by passing a block to Hash.new:
hash = Hash.new { |h, k| h[k] = 42 }
hash[:foo] += 1 # => 43
I'd like to implement autovivification for structs, also. This is the best I can come up with:
Foo = Struct.new(:bar) do
def bar
self[:bar] ||= 42
end
end
foo = Foo.new
foo.bar += 1 # => 43
and of course, this only autovivifies the named accessor (foo.bar), not the [] form (foo[:bar]). Is there a better way to implement autovivification for structs, in particular one that works robustly for both the foo.bar and foo[:bar] forms?

I would go with the following approach:
module StructVivificator
def self.prepended(base)
base.send(:define_method, :default_proc) do |&λ|
instance_variable_set(:#λ, λ)
end
end
def [](name)
super || #λ && #λ.() # or more sophisticated checks
end
end
Foo = Struct.new(:bar) do
prepend StructVivificator
end
foo = Foo.new
foo.default_proc { 42 } # declare a `default_proc` as in Hash
foo[:bar] += 1 # => 43
foo.bar += 1 # => 44
foo.bar above calls foo[:bar] under the hood through method_missing magic, so the only thing to overwrite is a Struct#[] method.
Prepending a module makes it more robust, per-instance and in general more flexible.
The code above is just an example. To copy the behavior of Hash#default_proc one might (credits to #Stefan for comments):
module StructVivificator
def self.prepended(base)
raise 'Sorry, structs only!' unless base < Struct
base.singleton_class.prepend(Module.new do
def new(*args, &λ) # override `new` to accept block
super(*args).tap { #λ = λ }
end
end)
base.send(:define_method, :default_proc=) { |λ| #λ = λ }
base.send(:define_method, :default_proc) { |&λ| λ ? #λ = λ : #λ }
# override accessors (additional advantage: performance/clarity)
base.members.each do |m|
base.send(:define_method, m) { self[m] }
base.send(:define_method, "#{m}=") { |value| self[m] = value }
end
end
def [](name)
super || default_proc && default_proc.(name) # or more sophisticated checks
end
end
Now default_proc lambda will receive a name to decide how to behave in such a case.
Foo = Struct.new(:bar, :baz) do
prepend StructVivificator
end
foo = Foo.new
foo.default_proc = ->(name) { name == :bar ? 42 : 0 }
puts foo.bar # => 42
puts foo[:bar] += 1 # => 43
puts foo.bar += 1 # => 44
puts foo[:baz] += 1 # => 1

Related

eql? override doesn't work in Hash

Hash checks its keys with eql?:
foo = 'str'
bar = 'str'
foo.equal?(bar) #=> false
foo.eql?(bar) #=> true
h = { foo => 1 }
h[foo] #=> 1
h[bar] #=> 1
But this doesn't work if I use my own class as a key:
class Cl
attr_reader :a
def initialize(a)
#a = a
end
def eql?(obj)
#a == obj.a
end
end
foo = Cl.new(10)
bar = Cl.new(10)
foo.equal?(bar) #=> false
foo.eql?(bar) #=> true
h = { foo => 1 }
h[foo] #=> 1
h[bar] #=> nil
Why does the last line return nil instead of 1?
eql? must be used in conjunction with a hash method that returns a hash code:
class Cl
attr_reader :a
def initialize(a)
#a = a
end
def eql?(obj)
#a == obj.a
end
def hash
#a
end
end
See this blog post.

Dynamically replace method implementation on an object in Ruby

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.

How to make the function accept a block

I am having a class G and my custom function func which i expect to take a block like this:
class G
def func(&block)
return '1' unless block_given?
# More code
end
end
I think that now when i do
G g = new G
g.func {|t| t}
block_given? should return true but its returning false
I hve tried following variants as well to no resort
g.func do |t|
end
Any help would be appreciated.
It's working fine if you correct some minor syntax errors. Note that there is no type declaration for ruby variables and object instantiation is done through an instance method of class Class instead with keyword (like in Java):
class G
def func(&block)
return '1' unless block_given?
block.call
end
end
g = G.new
g.func { puts 'block was called' }
g.func
# Output:
# irb(main):046:0>g.func { puts 'block was called' }
# block was called
# => nil
# irb(main):047:0>g.func
# => "1"
(Adding my output, although Matt beat me to it.)
> class G
> def func(&block)
> return '1' unless block_given?
> # More code
> end
> end
=> nil
> g = G.new
=> #<G:0x856e444>
> g.func { |t| puts "hi" }
=> nil
> g.func
=> "1"

Can you eval code in the context of a caller in Ruby?

Essentially I'm wondering if the following can be done in Ruby.
So for example:
def bar(symbol)
# magic code goes here, it outputs "a = 100"
end
def foo
a = 100
bar(:a)
end
You have to pass foo's context to bar:
def foo
a = 100
bar(:a, binding)
end
def bar(sym, b)
puts "#{sym} is #{eval(sym.to_s, b)}"
end
There is no built-in way to get a callers binding in Ruby in 1.8.X or 1.9.X.
You can use https://github.com/banister/binding_of_caller to work around.
In MRI 2.0 you can use RubyVM::DebugInspector, see: https://github.com/banister/binding_of_caller/blob/master/lib/binding_of_caller/mri2.rb
Working sample in MRI 2.0:
require 'debug_inspector'
def bar(symbol)
RubyVM::DebugInspector.open do |inspector|
val = eval(symbol.to_s, inspector.frame_binding(2))
puts "#{symbol}: #{val}"
end
end
def foo
a = 100
bar(:a)
end
foo
# a: 100
Here's a easier syntax hack, using a passed in block binding:
def loginfo &block
what = yield.to_s
evaled = eval(what, block.binding)
Rails.logger.info "#{what} = #{evaled.inspect}"
end
called like this:
x = 1
loginfo{ :x }
will log out:
x = 1
Just FYI, here's a "hacky way".
This is my (re-)implementation of well-known ppp.rb:
#!/usr/bin/ruby
#
# better ppp.rb
#
require 'continuation' if RUBY_VERSION >= '1.9.0'
def ppp(*sym)
cc = nil
ok = false
set_trace_func lambda {|event, file, lineno, id, binding, klass|
if ok
set_trace_func nil
cc.call(binding)
else
ok = event == "return"
end
}
return unless bb = callcc{|c| cc = c; nil }
sym.map{|s| v = eval(s.to_s, bb); puts "#{s.inspect} = #{v}"; v }
end
a = 1
s = "hello"
ppp :a, :s
exit 0
This currently fails with 1.9.[012] due to a bug in ruby's set_trace_func.
Check article out Variable Bindings in Ruby
class Reference
def initialize(var_name, vars)
#getter = eval "lambda { #{var_name} }", vars
#setter = eval "lambda { |v| #{var_name} = v }", vars
end
def value
#getter.call
end
def value=(new_value)
#setter.call(new_value)
end
end
def ref(&block)
Reference.new(block.call, block.binding)
end
def bar(ref)
# magic code goes here, it outputs "a = 100"
p ref.value
end
def foo
a = 100
bar(ref{:a})
end
foo

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