Is there a shortcut in Ruby to receive a lambda which simply returns its argument?
I'm trying to have the following function:
def some_method(x, y, decorator = ->(x) { x })
…
end
Rewritten to look somehow like
def some_method(x, y, decorator = method(:itself))
…
end
Maybe there's a way to change the receiver with Object#itself?
You have probably seen code like this plenty of times in ruby:
array.map(&:upcase)
Well, what does &:upcase actually mean? It's actually calling Symbol#to_proc.
Therefore, you could choose to define the method like this:
def some_method(x, y, decorator = :itself.to_proc)
# ...
end
Related
I have a library that has an #execute method like this
def execute(query, **args)
# ...
end
I have a class that generates the data for args (which has a lot of logic depending on user abilities)
class Abilities
def to_h
{ user: user } # and a lot more data
end
end
Now when I'm using #execute I always have to remember to use #to_h, which is pretty annoying and leads to mistakes when someone forgets it:
execute(query, abilities.to_h)
So I was wondering if my Abilities class could somehow respond to the ** (double splat) operator, so that I can simply pass the object:
execute(query, abilities)
When I try to call it like this, it throws an error:
ArgumentError: wrong number of arguments (given 2, expected 1)
So, is there any way to make my Abilities class behave like a Hash? I could derive it like this Abilities < Hash but then I have all the Hash logic on it, which seems pretty dirty.
You can implement to_hash: (or define it as an alias for to_h)
class MyClass
def to_hash
{ a: 1, b: 2 }
end
end
def foo(**kwargs)
p kwargs: kwargs
end
foo(MyClass.new)
#=> {:kwargs=>{:a=>1, :b=>2}}
If you specify the API to execute in such a way that it accepts anything that supports to_h, then you have a solution:
def execute(query, args = {})
args = args.to_h
...
end
I am trying to pass a function as an argument to a method of class. I know I need to use proc, but I am not sure I am using the right syntax. This is my attempt.
module MyApp;end
module MyApp::Stats
def self.sum(a)
a.inject(0){ |accum, i| accum + i }
end
def self.mean(a)
sum(a) / a.length.to_f
end
# Many more functions....
def self.sum_proc
Proc.new{|x| sum(x) }
end
def self.mean_proc
Proc.new{|x| mean(x)}
end
# And many more procs
end
class MyData
attr_reader :values
attr_reader :aggregates
def initialize(array)
#values = array
#aggregates = {}
end
def aggregate(aggregator)
puts aggregator.call(#values)
#I would also like to create a hash of aggregator. Something like:
#aggregates["aggregator"] = aggregator.call(#values)
end
end
I can then do
ar = [1,2,3,4,5,6,7,8,9]
data = MyData.new(ar)
And call the aggregate method in various ways:
aggregator = Proc.new{|x| MyApp::Stats.sum(x)}
data.aggregate(aggregator)
data.aggregate(Proc.new{|x| MyApp::Stats.mean(x)} )
data.aggregate(Proc.new{|x| x.count{|y| y > 3.0} })
data.aggregate(MyApp::Stats.sum_proc)
data.aggregate(MyApp::Stats.mean_proc)
I have two issues with this code. First it seems redundant as I have to define the aggregator first and then the associated proc, e.g. sum and sum_proc. Second, I wonder how I could pass any of the standard enumerator methods without defining a proc for it: say count or first.
Finally, I would like to create a hash for the aggregators so that I could do:
puts data.aggregates["sum"]
puts data.aggregates["mean"]
Methods aren't objects in Ruby. You can't pass a method as an argument because you can only pass objects as arguments.
However, you can get a Method proxy object representing a method by calling the method method and passing the name of the method as an argument. Method proxy objects duck-type Proc, so they respond to arity, parameters, and most importantly to_proc and call.
The idiomatic way of taking a single first-class procedure as an argument, is to take a block like this:
def aggregate
yield #values
end
and pass a method using the unary prefix & operator:
data.aggregate(&MyApp::Stats.:sum)
I have several classes, e.g., P, that share the same instance method some_method:
class P
...
def some_method
#id
end
end
Instances of these classes will be used as arguments at many places like this:
p = P.new
q = Q.new
...
def some_outside_method(p,q,r,s)
another_outside_method(p.some_method, q.some_method, r.some_method, s.some_method)
end
I'm wondering if there is a more elegant way of writing it. Is it possible to automatically call p's some_method whenever p is referenced as in some_outside_method(p)? It is something like to_s implicitly called by puts, but more generalized.
You can reduce duplication by doing this, for example:
def some_outside_method(p,q,r,s)
args = [p, q, r, s].map{|o| o.send(:some_method)}
another_outside_method(*args)
end
or, more briefly:
def some_outside_method(*args)
args = args.map(&:some_method)
another_outside_method(*args)
end
or, more more briefly:
def some_outside_method(*args)
another_outside_method args.map(&:some_method)
end
But don't. Simple code is better than terse and "clever" one.
I have a method that needs to do a bit of sorcery on the attached block. A sample of such a block might be
myMethod do
somemethod x
someother y
def name(a,b)
a+b
end
end
the first two method calls (somemethod x and someother y) should just be executed as normal. However I'd like to intercept the method definition (as S-expression) without actually defining a new method. I can do this if I transform the entire block to S-expressions and then search through the AST. However then I need to figure out how to call the methods. A solution to either will do. That is either
intercepting the definition, transform to S-expression (that Ruby2Ruby can understand)
transform the block to S-expressions and find the method calls and execute these
EDIT
The AST I'm looking for is something similar to
[:defn,
:name,
[:args,:a,:b],
[:call,
[lvar,:a],
:+
[lvar,:b]]]
If I understand correctly, you want to be able to define a method within a code block passed to another method, but intercept that inner method definition so that it doesn't actually get defined but is instead converted to an S-expression? So you want it to behave almost as if it were commented out and some external process had come through the source code and parsed out the implementation into an S-expression?
Something like:
myMethod do
somemethod x
someother y
# def name(a,b)
# a+b
# end
end
Where somemethod and someother still execute, the implementation of name is defined, but is ignored by the Ruby interpreter. Then you somehow want to capture this implementation as an S-expression. It obviously wouldn't be done like this, by commenting it out, but its a nice way for me to picture the behavior.
Well, the RubyParser gem might do what you want. It takes advantage of the ability to pass in a 'here document' as a string to a method, as in this example:
def x (str)
str
end
x( <<-EOF )
this is
a here
document
EOF
# => "this is\n a here\ndocument\n"
Using RubyParser, you can do something like the following:
require 'ruby_parser'
require 'pp'
pp RubyParser.new.parse( <<-EOF )
def plus(x,y)
x+y
end
EOF
# => s(:defn, :name, s(:args, :a, :b), s(:call, s(:lvar, :a), :+, s(:lvar, :b)))
It would be a trivial matter, then, to merge it with your desired code, like so:
require 'ruby_parser'
def myMethod
x = yield
end
def somemethod( x )
puts x
end
def someother( y )
puts y
end
x = 'magic'
y = 'output'
sexp = myMethod do
somemethod x
someother y
RubyParser.new.parse( <<-EOF )
def name(a,b)
a+b
end
EOF
end
pp sexp
name(1,2)
# magic
# output
# s(:defn, :name, s(:args, :a, :b), s(:call, s(:lvar, :a), :+, s(:lvar, :b)))
# NoMethodError: undefined local variable or method 'name' for main:Object
As you can see, the method definition is essentially just a string, and can be manipulated as such.
I would like to pass a class method as a parameter to have another object call it ie
do_this(Class.method_name)
and then:
def do_this(class_method)
y = class_method(local_var_x)
end
The only way I can see to do it is pass it as a string and use eval, or pass the class and method as a string, then constantize and send. Downside to eval seems to be speed and debugging?
Is there an easier way to do this?
Edit:
Good answers but realized I asked the question slightly wrong, would like to use a parameter not passed with the method.
I'd suggest an approach similar to the second solution you proposed.
do_this(Class.method(:name), x)
and then:
def do_this(method, x)
y = method.call(x)
end
See also the documentation of Object#method.
Consider using a proc object:
def do_this(myproc)
y = myproc.call
end
and then
do_this( Proc.new { klass.method(x) } )
though you should also consider using block, which is much more in the ruby style. That would look like:
def do_this
y = yield
end
and call via:
do_this { klass.method(x) }