Determine arity of method with keyword arguments - ruby

I am developing a Ruby application where I am dynamically invoking methods based on JSON data. Loosely:
def items
# do something
end
def createItem( name:, data:nil )
# do something that requires a name keyword argument
end
def receive_json(json) # e.g. { "cmd":"createItem", "name":"jim" }
hash = JSON.parse(json)
cmd = hash.delete('cmd')
if respond_to?(cmd)
params = Hash[ hash.map{ |k,v| [k.to_sym, v } ]
method(cmd).arity==0 ? send(cmd) : send(cmd,params)
end
end
As shown above, some methods take no arguments, and some take keyword arguments. Under Ruby 2.1.0 (where I'm developing) the arity of both methods above is 0. However, if I send(cmd,params) always, I get an error for methods that take no parameters.
How can I use send to correctly pass along the keyword arguments when desired, but omit them when not?

Using parameters instead of arity appears to work for my needs:
method(cmd).parameters.empty? ? send(cmd) : send(cmd,opts)
More insight into the richness of the parameters return values:
def foo; end
method(:foo).parameters
#=> []
def bar(a,b=nil); end
method(:bar).parameters
#=> [[:req, :a], [:opt, :b]]
def jim(a:,b:nil); end
method(:jim).parameters
#=> [[:keyreq, :a], [:key, :b]]
Here's a generic method that picks out only those named values that your method supports, in case you have extra keys in your hash that aren't part of the keyword arguments used by the method:
module Kernel
def dispatch(name,args)
keyargs = method(name).parameters.map do |type,name|
[name,args[name]] if args.include?(name)
end.compact.to_h
keyargs.empty? ? send(name) : send(name,keyargs)
end
end
h = {a:1, b:2, c:3}
def no_params
p :yay
end
def few(a:,b:99)
p a:a, b:b
end
def extra(a:,b:,c:,z:17)
p a:a, b:b, c:c, z:z
end
dispatch(:no_params,h) #=> :yay
dispatch(:few,h) #=> {:a=>1, :b=>2}
dispatch(:extra,h) #=> {:a=>1, :b=>2, :c=>3, :z=>17}

At first, I thought params is supposed to become empty when the :cmd value is "items", in which case Jesse Sielaff's answer would be correct. But since you seem to be claiming that it isn't, I think that it is your design flaw. Instead of trying to dispatch in that way, you should rather have those methods just gobble the arguments:
def items(name:nil, data:nil)
...
end

Related

Why a block invoked by a Module can't modify objects from implementing classes in Ruby?

I have some data saved in deeply nested Hashes and Arrays and I have run into trouble with the text encoding of the data. I know for fact that the texts are encoded in "UTF-8", so I decided to go over each element and force the encoding.
So, I created a method called deep_each for the Enumerable module:
module Enumerable
def deep_each(&block)
self.each do |element|
if element.is_a? Enumerable then
element.deep_each(&block)
else
block[element]
end
end
end
end
And expected to be able to fix the data using the following method call:
deephash.deep_each {|element| element.force_encoding("UTF-8") if element.class == String}
But the result was disappointing:
deephash.deep_each {|element| element.force_encoding("UTF-8") if element.class == String}
> RuntimeError: can't modify frozen String
> from (pry):16:in `force_encoding'
Then I moved the function down the hierarchy, to the "Array" and "Hash" classes:
class Hash
def deep_each(&block)
self.each do |element|
if [Array, Hash].include? element.class then
element.deep_each(&block)
else
block[element]
end
end
end
end
class Array
def deep_each(&block)
self.each do |element|
if [Array, Hash].include? element.class then
element.deep_each(&block)
else
block[element]
end
end
end
end
Surprisingly, the same call works now.
What constraint am I violating here, and how can I define a method for all Enumerables without defining it for every single one of them?
As far as I can tell, you should get the exact same error with both your Enumerable version and your Array/Hash monkey patch. I do. Are you sure you're using the same deephash in both cases?
Normally when you loop each on a hash, you'd pass in both key and value to the block. You're passing a single value element to the block. This then is an Array with the key and value:
irb> {a:1, b:2}.each {|el| puts el.inspect }
[:a, 1]
[:b, 2]
Your deep_each checks if this is an Enumerable, and it is, so it calls deep_each on the list. Then, finally, you reach the leafs and call the block on the key and the value. The block checks if it's working with a String, and if so, forces encoding.
If your hash key is a string, you will try to mutate it. But hash keys are frozen, and so RuntimeError: can't modify frozen String is raised.
irb> {a: {b: {c: "abc"}}}.deep_each { |el| el << "efg" if String === el}
=> {:a=>{:b=>{:c=>{:d=>"abcefg"}}}}
irb> {a: {b: {"c" => "abc"}}}.deep_each { |el| el << "efg" if String === el}
RuntimeError: can't modify frozen String
str = "\xE2\x82\xAC" #Euro sign in UTF-8
puts str.encoding #=> UTF-8
puts str #=> Euro sign in a UTF-8 enabled terminal window
File.open('data.txt', 'w:utf-8') do |f|
f.write("#{str}\n")
end
Encoding.default_external = 'ISO-8859-1'
str = File.read('data.txt')
puts str.encoding #=> ISO-8859-1
arr = [
{a: str},
{b: 'world'},
]
arr[0][:a].force_encoding('utf-8')
puts arr[0][:a].encoding #=> UTF-8
puts arr[0][:a] #=> Euro sign in a UTF-8 enabled terminal window
It would be more illustrative if you posted an example of: I have run into trouble with the text encoding of the data
Finally, it looks like writing the method for each class separately
makes more sense. For the Hash I need to use each_value rather than
each
You can do something like this:
iterator_for = Hash.new(:each) #When a non-existent key is looked up, return :each
iterator_for.update({
Hash => :each_value,
})
data = [
%w{ hello world goodbye },
{"a" => "red", "b" => "blue"},
]
data.each do |element|
element.send(iterator_for[element.class]) do |x|
puts x
end
puts '-' * 20
end
--output:--
hello
world
goodbye
--------------------
red
blue
--------------------

How do I call a method, given its name, on an element of an array?

How do I call a method, given its name, on an element of an array?
For example, I could have:
thing = "each"
I want to be able to do something like:
def do_thing(thing)
array = [object1,object2]
array[0].thing
end
so that do_thing(to_s), for example, would run object1.to_s.
You can use public_send or send. public_send only sends to public methods while send can see public and private methods.
def do_thing(thing)
array = [1,2,3]
array.public_send(thing)
end
do_thing('first')
# => 1
do_thing(:last)
# => 3
Update A more general version:
def do_thing(array, index, method, *args)
array[index].public_send(method, *args)
end
do_thing([1, 2, 3], 0, :to_s)
# => "1"
do_thing([[1,2], [3, 4]], 0, :fetch, 0)
# => 1
require 'ostruct'
o = OpenStruct.new(attribute: 'foo')
do_thing([o], 0, :attribute=, 'bar')
o.attribute == 'bar'
# => true
Object#send
thing = "each"
def do_thing(thing)
array = [1,2,3]
array.send(thing)
end
From the doc:
class Klass
def hello(*args)
"Hello " + args.join(' ')
end
end
k = Klass.new
k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
Here is an example to help you out although I don't have any idea what objects are residing inside your array:
arr = [Array.new(2,10),"abc" ]
arr.each{|i| p i.send(:length)}
#>>2
#>>3

Ruby : Rubeque - difficult with *n or n

I'm doing http://www.rubeque.com/problems/queue-continuum/solutions/51a26923ba804b00020000df and I spent a while there. I can't realize why this code doesn't pass
def initialize(queue)
#q = queue
end
def pop(n=1)
#q.shift(n)
end
def push(arr)
arr.each { |x|
#q.push(x)
}
return true
end
def to_a
#q
end
but this works perfectly.
def initialize(queue)
#q = queue
end
def pop(*n)
#q.shift(*n)
end
def push(arr)
#q.push(*arr)
return true
end
def to_a
#q
end
i'm totally confused about
def pop(*n)
#q.shift(*n)
end
and
def push(arr)
#q.push(*arr)
end
why should I take (arr) as array and than change it into... *arr which is Array of array? I'm confused, please help!
The splat works in two ways.
When receiving arguments, it combines arguments into an array.
def foo *args; args end
foo(1) # => [1]
foo(1, 2, 3) # => [1, 2, 3]
When giving arguments, it decomposes an array into arguments.
def bar x, y, z; y end
bar(*[1, 2, 3]) # => 2
def baz x; x end
baz(1) # => [1]
baz(1, 2, 3) # => Error
The *arr you are wondering is the latter case. It is not an object like [1, 2, 3] (hence, not an array of arrays). It is a part of arguments (like 1, 2, 3) passed to a method.
There are other uses of splats (as in array literals, case statements, etc.), but their function is either of the two uses above.

What's the difference between to_a and to_ary?

What's the difference between to_a and to_ary?
to_ary is used for implicit conversions, while to_a is used for explict conversions.
For example:
class Coordinates
attr_accessor :x, :y
def initialize(x, y); #x, #y = x, y end
def to_a; puts 'to_a called'; [x, y] end
def to_ary; puts 'to_ary called'; [x, y] end
def to_s; "(#{x}, #{y})" end
def inspect; "#<#{self.class.name} #{to_s}>" end
end
c = Coordinates.new 10, 20
# => #<Coordinates (10, 20)>
The splat operator (*) is a form of explicit conversion to array:
c2 = Coordinates.new *c
# to_a called
# => #<Coordinates (10, 20)>
On the other hand, parallel assignment is a form of implicit conversion to array:
x, y = c
# to_ary called
puts x
# 10
puts y
# 20
And so is capturing collection members in block arguments:
[c, c2].each { |(x, y)| puts "Coordinates: #{x}, #{y}" }
# to_ary called
# Coordinates: 10, 20
# to_ary called
# Coordinates: 10, 20
Examples tested on ruby-1.9.3-p0.
This pattern seems to be used all over the Ruby language, as evidenced by method pairs like to_s and to_str, to_i and to_int and possibly more.
References:
Ruby Issue 3680
Variables
to_ary allows an object to be treated as an array, whereas to_a actually tries to convert the parameter into an array.
to_ary can be useful for parallel assignment, whereas to_a is more suited for an actual conversion.
Quoted from gabew's web space:
Calling #to_a will convert the receiver to an Array, while #to_ary will not.
ruby-1.9.2-p290 :001 > class A < Array; end
ruby-1.9.2-p290 :004 > A[].to_a.class
=> Array
ruby-1.9.2-p290 :005 > A[].to_ary.class
=> A
to_a, when called on an object returns an array representation of obj
Examples
class Example
def initialize
#str = 'example'
end
end
a = Example.new #=> #<Example:0x105a74af8 #str="example"
a.to_a #=> [#<Example:0x105a6a3a0 #str="example">]
Hash Example
h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
h.to_a #=> [["c", 300], ["a", 100], ["d", 400]]
An array can also be created by using the Array() method, provided by Kernel, which tries to call to_ary, then to_a on its argument. http://ruby-doc.org/core-2.0/Array.html#method-i-to_ary
so as far as I can see, the Array#to_ary src just returns the array that is passed in, as in
def to_ary
return self
end
if I understand correctly, to_a is used for array conversion and makes its final return using to_ary. But this may not be true in future versions according to apidock
to_a Returns an array representation of obj. For objects of class Object and others that don’t explicitly override the method, the return value is an array containing self. However, this latter behavior will soon be obsolete. http://apidock.com/ruby/Object/to_a

Are there something like Python generators in Ruby?

I am new to Ruby, is there a way to yield values from Ruby functions? If yes, how? If not, what are my options to write lazy code?
Ruby's yield keyword is something very different from the Python keyword with the same name, so don't be confused by it. Ruby's yield keyword is syntactic sugar for calling a block associated with a method.
The closest equivalent is Ruby's Enumerator class. For example, the equivalent of the Python:
def eternal_sequence():
i = 0
while True:
yield i
i += 1
is this:
def eternal_sequence
Enumerator.new do |enum|
i = 0
while true
enum.yield i # <- Notice that this is the yield method of the enumerator, not the yield keyword
i +=1
end
end
end
You can also create Enumerators for existing enumeration methods with enum_for. For example, ('a'..'z').enum_for(:each_with_index) gives you an enumerator of the lowercase letters along with their place in the alphabet. You get this for free with the standard Enumerable methods like each_with_index in 1.9, so you can just write ('a'..'z').each_with_index to get the enumerator.
I've seen Fibers used in that way, look at an example from this article:
fib = Fiber.new do
x, y = 0, 1
loop do
Fiber.yield y
x,y = y,x+y
end
end
20.times { puts fib.resume }
If you are looking to lazily generate values, #Chuck's answer is the correct one.
If you are looking to lazily iterate over a collection, Ruby 2.0 introduced the new .lazy enumerator.
range = 1..Float::INFINITY
puts range.map { |x| x+1 }.first(10) # infinite loop
puts range.lazy.map { |x| x+1 }.first(10) # [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
Ruby supports generators out of the box using Enumerable::Generator:
require 'generator'
# Generator from an Enumerable object
g = Generator.new(['A', 'B', 'C', 'Z'])
while g.next?
puts g.next
end
# Generator from a block
g = Generator.new { |g|
for i in 'A'..'C'
g.yield i
end
g.yield 'Z'
}
# The same result as above
while g.next?
puts g.next
end
https://ruby-doc.org/stdlib-1.8.7/libdoc/generator/rdoc/Generator.html
Class Enumerator and its method next behave similar
https://docs.ruby-lang.org/en/3.1/Enumerator.html#method-i-next
range = 1..Float::INFINITY
enumerator = range.each
puts enumerator.class # => Enumerator
puts enumerator.next # => 1
puts enumerator.next # => 2
puts enumerator.next # => 3

Resources