I have many methods like these two:
def create_machine(name, os_type_id, settings_file='', groups=[], flags={})
soap_method = "#{self.class.name.split('::').last.to_underscore}_#{__method__}".to_sym
args = method(__method__).parameters.map { |arg| arg[1] }
soap_message = Hash[args.map { |arg| [arg, eval(arg.to_s)] }]
VirtualBoxAPI.send_request(#cl.conn, soap_method, #this.merge(soap_message))
end
def register_machine(machine)
soap_method = "#{self.class.name.split('::').last.to_underscore}_#{__method__}".to_sym
args = method(__method__).parameters.map { |arg| arg[1] }
soap_message = Hash[args.map { |arg| [arg, eval(arg.to_s)] }]
VirtualBoxAPI.send_request(#cl.conn, soap_method, #this.merge(soap_message))
end
They have the same implementation but different number of different arguments. There will be tens of such methods in each of tens of classes. So I thought I'd use some meta-programming to minimize the code repetition.
I was trying to do this via define_method and wanted to end up in something like this:
vb_method :create_machine, :args => [:name, :os_type_id], :optional_args => [:settings_file, :groups, :flags]
But I can't find a way to pass arbitrary number of named (non-splat) arguments to define_method (I thought splat argument will make documenting the methods hard to impossible also will make the resulting API inconvenient).
What would be the best way to deal with this (using Ruby 2.0)?
UPD
Another way to do this is defining a method vb_method:
def vb_method(*vb_meths)
vb_meths.each do |meth|
define_method(meth) do |message={}|
soap_method = "#{self.class.name.split('::').last.to_underscore}_#{meth}".to_sym
VirtualBoxAPI.send_request(#cl.conn, soap_method, #this.merge(message))
end
end
end
And then the class would have a call like this:
vb_method :create_machine, :register_machine
But is this case I will need to always call the methods with hash as an argument:
machine = vb.create_machine(name: 'my_vm', os_type_id: 'Windows95')
And that's exactly what I'm trying to avoid because I think in this case the resulting API can't be documented and is not convenient to use.
Stop trying to avoid option hashes. That's the "Ruby way" of doing things. They aren't impossible to document and several mainstream Ruby libraries use them this way (the first that come to mind are ActiveRecord and Mysql2).
Note that you can provide a default argument to the option hash, which serves as documentation and allows you to reduce code repetition.
Also, think about how your code would work if you could (somehow) pass an arbitrary number of named arguments to define_method. How would users remember which arguments are which? They would need to memorize the order and meaning of all the different positional arguments to all the different methods defined this way. When you have many similar methods with arguments of varying meanings, it's very difficult to keep everything straight. Keyword arguments (which is essentially what Ruby's option hashes are) were specifically created to avoid this situation.
If you're worried about error checking, define a helper method that checks the option hash for missing/unrecognized keys and raises an informative exception:
def validate_options(known, opts)
opts.each_key { |opt| raise "Unknown option: #{opt}" unless known.include?(opt) }
known.each { |opt, required| raise "Missing required option: #{opt}" if required and not opts.include?(opt) }
end
Related
Why does: respond_to? in:
class Wolf
def howl; end
end
Wolf.new.respond_to?(:howl) # => true
not require & while map in:
["1", "2", "3"].map(&:to_i) # => [1, 2, 3]
does? Also, are there any technical names for this?
When you say :method, you're using some nice syntactical sugar in ruby that creates a new Symbol object. When you throw an ampersand before it (&:method), you're using another piece of sugar. This invokes the to_proc method on the symbol.
So, these two things are identical:
method_proc = &:method
sym = :method
method_proc = method.to_proc
What's the difference between that and the other usage? Well, respond_to? has a single argument -- a symbol. So we can pass :method and be all fine and dandy. (Interestingly, objects do respond to the method named method, but that's a far more confusing question).
By comparison, Enumerable's iterators (like map, select, etc) accept a block. When we pass a Proc, it is interpreted properly as that block. So, these two pieces of code are equivalent:
[1,2,3].map { |i| i.even? }
[1,2,3].map(&:even?)
This equivalence is a little confusing, because of course Symbol has no idea that there's an even? method somewhere. To play around with it, I used evenproc = :even?.to_proc to inspect the resulting proc. It's implemented in C (at least in MRI ruby), and isn't willing to give up its source. However, its arity is -1, which means that it accepts one optional arg. My best guess is that it does something like this:
def to_proc
method_name = self.to_s
->(a) { a.send(method_name) }
end
I could dig further, but I think we've already gone way past the question. ;) Good luck!
I looked through this code and found author passes params to block using []. I tryed it myself
my_proc = proc { |x| x + 1 }
a = 0
my_proc[a] # => 1
my_proc.call(a) # => 1
What is the difference between this two calls? Is this a syntax sugar?
Both ways are exactly the same and are aliases to each other. Thus, both variants call the same method which is not determined by any special syntax. It is basically defined as:
class Proc
def call(*args)
#...
end
alias [] call
end
You might be interested to note that there is even a third way:
my_proc.(a)
This is actually syntactic sugar (i.e. is an extension of the syntax of the Ruby language language). All objects accepting #call can be "called" that way and Ruby ensures to invoke the call method.
They are functionally identical. You can use whichever style you prefer.
I wonder, is it possible to do something similar in Ruby to what I can do in Scala or other languages:
someCollection.foreach(x => println(x)) // a full version
someCollection.foreach(println) // a short version
In Ruby I can do:
some_array.each { |x| puts x }
So how can I do this?
some_array.each { puts }
UPDATE:
I'm not talking about puts in particular, it just picked it for example. There might be some_other_method which takes one parameter.
some_array.map { some_other_method }
some_array.map(some_other_method) # ???
def some_other_method a
# ... doing something with a
end
If you look up the rules for implicit η-expansion in the SLS (§6.26.5), it should be immediately obvious that it relies crucially on static type information and thus cannot possibly work in Ruby.
You can, however, explicitly obtain a Method object via reflection. Method objects respond to to_proc and like any object that responds to to_proc can thus be passed as if they were blocks using the unary prefix & operator:
some_array.each(&method(:puts))
Not quite like that, unfortunately. You can send a method name to be called on each object, e.g.:
some_array.each &:print_myself
Which is equivalent to:
some_array.each {|x| x.print_myself}
But I don't know of a clean (read: built-in) way to do what you're asking for. (Edit: #Jörg's answer does this, though it doesn't really save you any typing. There is no automatic partial function application in Ruby)
How do I model an optional value in ruby? Scala has Option[], which is what I'm looking for in ruby.
There's no equivalent in the standard library. You have to define your own. See this article.
I'm not a Ruby expert, but I don't think there is an Option equivalent. As Ruby is object oriented, nothing stops you from writing your own implementation, but it won't be as useful as in a statically typed language, where the compiler forces you to do a proper check for the empty option, which is one of the main points for using this construct. Of course there are other advantages, like the possibility to chain Option values in several ways.
Have you checked out the Rumonade gem? It gives you an Option class modeled on scala.
require 'rumonade'
[nil, 1, 123].map { |v| Option(v) }
=> [None, #<Rumonade::Some:0x7f2589297768 #value=1>, #<Rumonade::Some:0x7f2589297740 #value=123>]
[nil, 1, 123].map { |v| Option(v).map { |n| n.to_s }.select { |s| s.size > 2 } }.flatten
=> ["123"]
There is an example of a Maybe class in nkpart's adt library under examples/common_adts.rb. There are also other example ADTs and the library makes it easier to define your own.
I have just pushed a gem called nil_be_gone that gives you an Optional that you can wrap objects with. It implements method_missing to check whether the value of the Optional is nil and if so simply return another Optional wrapped nil value, otherwise it calls the method on the object and wraps it again.
nil_be_gone implements bind as and_then which allows you to chain operations on Optional types, it's return methods which retrieves the value from Optional is value and the unit operation which wraps an object in the monad is defined by self.from_value.
I don't know Scala, so I can't assert that's your answer:
In ruby, when you call a method, you can define a default value for a param:
def foo(i_am_mandatory, i_am_optionnal = :banga)
puts i_am_optionnal
end
foo(:pouet, :plip)
=> :plip
foo(:pouet)
=> :banga
In that example, you can omit i_am_optionnal, which has a default value.
HTH.
This is for an already existing public API that I cannot break, but I do wish to extend.
Currently the method takes a string or a symbol or anything else that makes sense when passed as the first parameter to send
I'd like to add the ability to send a list of strings, symbols, et cetera. I could just use is_a? Array, but there are other ways of sending lists, and that's not very ruby-ish.
I'll be calling map on the list, so the first inclination is to use respond_to? :map. But a string also responds to :map, so that won't work.
How about treating them all as Arrays? The behavior you want for Strings is the same as for an Array containing only that String:
def foo(obj, arg)
[*arg].each { |method| obj.send(method) }
end
The [*arg] trick works because the splat operator (*) turns a single element into itself or an Array into an inline list of its elements.
Later
This is basically just a syntactically sweetened version or Arnaud's answer, though there are subtle differences if you pass an Array containing other Arrays.
Later still
There's an additional difference having to do with foo's return value. If you call foo(bar, :baz), you might be surprised to get [baz] back. To solve this, you can add a Kestrel:
def foo(obj, arg)
returning(arg) do |args|
[*args].each { |method| obj.send(method) }
end
end
which will always return arg as passed. Or you could do returning(obj) so you could chain calls to foo. It's up to you what sort of return-value behavior you want.
A critical detail that was overlooked in all of the answers: strings do not respond to :map, so the simplest answer is in the original question: just use respond_to? :map.
Since Array and String are both Enumerables, there's not an elegant way to say "a thing that's an Enumberable, but not a String," at least not in the way being discussed.
What I would do is duck-type for Enumerable (responds_to? :[]) and then use a case statement, like so:
def foo(obj, arg)
if arg.respond_to?(:[])
case arg
when String then obj.send(arg)
else arg.each { |method_name| obj.send(method_name) }
end
end
end
or even cleaner:
def foo(obj, arg)
case arg
when String then obj.send(arg)
when Enumerable then arg.each { |method| obj.send(method) }
else nil
end
end
Perhaps the question wasn't clear enough, but a night's sleep showed me two clean ways to answer this question.
1: to_sym is available on String and Symbol and should be available on anything that quacks like a string.
if arg.respond_to? :to_sym
obj.send(arg, ...)
else
# do array stuff
end
2: send throws TypeError when passed an array.
begin
obj.send(arg, ...)
rescue TypeError
# do array stuff
end
I particularly like #2. I severely doubt any of the users of the old API are expecting TypeError to be raised by this method...
Let's say your function is named func
I would make an array from the parameters with
def func(param)
a = Array.new
a << param
a.flatten!
func_array(a)
end
You end up with implementing your function func_array for arrays only
with func("hello world") you'll get a.flatten! => [ "hello world" ]
with func(["hello", "world"] ) you'll get a.flatten! => [ "hello", "world" ]
Can you just switch behavior based on parameter.class.name? It's ugly, but if I understand correctly, you have a single method that you'll be passing multiple types to - you'll have to differentiate somehow.
Alternatively, just add a method that handles an array type parameter. It's slightly different behavior so an extra method might make sense.
Use Marshal to serialize your objects before sending these.
If you don't want to monkeypatch, just massage the list to an appropriate string before the send. If you don't mind monkeypatching or inheriting, but want to keep the same method signature:
class ToBePatched
alias_method :__old_takes_a_string, :takes_a_string
#since the old method wanted only a string, check for a string and call the old method
# otherwise do your business with the map on things that respond to a map.
def takes_a_string( string_or_mappable )
return __old_takes_a_string( string_or_mappable ) if String === string_or_mappable
raise ArgumentError unless string_or_mappable.responds_to?( :map )
# do whatever you wish to do
end
end
Between those 3 types I'd do this
is_array = var.respond_to?(:to_h)
is_string = var.respond_to?(:each_char)
is_symbol = var.respond_to?(:to_proc)
Should give a unique answer for [], :sym, 'str'