How can I validate a function passed to a method in Ruby? - ruby

I'm writing a library to humanize bytes in Ruby (e.g. to turn the byte count 1025 into the string 1.1K), and I'm stuck on one element of the design.
The plan is to extend Numeric with ahumanize method that returns a human-friendly string when called on a number. After looking at the source of Number::Bytes::Human (a Perl module that I like a lot for this), I decided to add two options to the method: one to use 1000 byte blocks and one to use floor rather than ceil for the default rounding function.
In order to be maximally flexible, the method's definition uses a hash for the parameters, so that users can change one or both of the options. If no parameters are passed, a default hash is used. That gives me something like this:
def humanize(params = {})
params = {:block => 1024, :r_func => lambda }.merge params
# yada yada
end
Ideally, I would like to let the user pass a function as the value of params[:r_func], but I can't figure out how to validate that it's either ceil or floor. Because I can't get a handle on this, I've ended up doing the following, which feels pretty clumsy:
def humanize(params = {})
params = {:block => 1024, :r_func => 'ceil' }.merge params
if params[:r_func].eql? 'ceil'
params[:r_func] = lambda { |x| x.ceil }
elsif params[:r_func].eql? 'floor'
params[:r_func] = lambda { |x| x.floor }
else
raise BadRound, "Rounding method must be 'ceil' or 'floor'."
end
# blah blah blah
end
If anyone knows a trick for peeking at the method that a Ruby lambda contains, I would love to hear it. (I'm also happy to hear any other design advice.) Thanks.

There's no reason to let the user pass a method in if you're going to be that draconian about what they are allowed to pass (you know there are other rounding schemes besides ceiling and floor, right?)
If you want to restrict the user to ceiling and floor, just allow them to pass the symbol :ceiling or :floor in. A more flexible design would be to allow the method to take a block which receives a single parameter, the number to be rounded. Then the user could use whatever rounding algorithm they prefer, including custom ones.
By the way, Numeric#humanize falls into that category of monkeypatches with such a popular name that you are likely to run into namespace collisions (and resulting subtle bugs) in anything but a small, personal project.

I don't see any point in having the caller pass a lambda if you're not going to actually call the thing. Make it a symbol instead and you can do something like:
raise BadRound, "Rounding method must be :ceil or :floor." unless [:ceil, :floor].include? params[:r_func]
op = lambda {|x| x.send params[:r_func]}
# blah blah blah

Why have them pass a function pointer instead of a boolean? That way you avoid the problem of having to validate the function.

Related

Dynamically define many methods with same implementation but arbitrary arguments

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

undefined method `assoc' for #<Hash:0x10f591518> (NoMethodError)

I'm trying to return a list of values based on user defined arguments, from hashes defined in the local environment.
def my_method *args
#initialize accumulator
accumulator = Hash.new(0)
#define hashes in local environment
foo=Hash["key1"=>["var1","var2"],"key2"=>["var3","var4","var5"]]
bar=Hash["key3"=>["var6"],"key4"=>["var7","var8","var9"],"key5"=>["var10","var11","var12"]]
baz=Hash["key6"=>["var13","var14","var15","var16"]]
#iterate over args and build accumulator
args.each do |x|
if foo.has_key?(x)
accumulator=foo.assoc(x)
elsif bar.has_key?(x)
accumulator=bar.assoc(x)
elsif baz.has_key?(x)
accumulator=baz.assoc(x)
else
puts "invalid input"
end
end
#convert accumulator to list, and return value
return accumulator = accumulator.to_a {|k,v| [k].product(v).flatten}
end
The user is to call the method with arguments that are keywords, and the function to return a list of values associated with each keyword received.
For instance
> my_method(key5,key6,key1)
=> ["var10","var11","var12","var13","var14","var15","var16","var1","var2"]
The output can be in any order. I received the following error when I tried to run the code:
undefined method `assoc' for #<Hash:0x10f591518> (NoMethodError)
Please would you point me how to troubleshoot this? In Terminal assoc performs exactly how I expect it to:
> foo.assoc("key1")
=> ["var1","var2"]
I'm guessing you're coming to Ruby from some other language, as there is a lot of unnecessary cruft in this method. Furthermore, it won't return what you expect for a variety of reasons.
`accumulator = Hash.new(0)`
This is unnecessary, as (1), you're expecting an array to be returned, and (2), you don't need to pre-initialize variables in ruby.
The Hash[...] syntax is unconventional in this context, and is typically used to convert some other enumerable (usually an array) into a hash, as in Hash[1,2,3,4] #=> { 1 => 2, 3 => 4}. When you're defining a hash, you can just use the curly brackets { ... }.
For every iteration of args, you're assigning accumulator to the result of the hash lookup instead of accumulating values (which, based on your example output, is what you need to do). Instead, you should be looking at various array concatenation methods like push, +=, <<, etc.
As it looks like you don't need the keys in the result, assoc is probably overkill. You would be better served with fetch or simple bracket lookup (hash[key]).
Finally, while you can call any method in Ruby with a block, as you've done with to_a, unless the method specifically yields a value to the block, Ruby will ignore it, so [k].product(v).flatten isn't actually doing anything.
I don't mean to be too critical - Ruby's syntax is extremely flexible but also relatively compact compared to other languages, which means it's easy to take it too far and end up with hard to understand and hard to maintain methods.
There is another side effect of how your method is constructed wherein the accumulator will only collect the values from the first hash that has a particular key, even if more than one hash has that key. Since I don't know if that's intentional or not, I'll preserve this functionality.
Here is a version of your method that returns what you expect:
def my_method(*args)
foo = { "key1"=>["var1","var2"],"key2"=>["var3","var4","var5"] }
bar = { "key3"=>["var6"],"key4"=>["var7","var8","var9"],"key5"=>["var10","var11","var12"] }
baz = { "key6"=>["var13","var14","var15","var16"] }
merged = [foo, bar, baz].reverse.inject({}, :merge)
args.inject([]) do |array, key|
array += Array(merged[key])
end
end
In general, I wouldn't define a method with built-in data, but I'm going to leave it in to be closer to your original method. Hash#merge combines two hashes and overwrites any duplicate keys in the original hash with those in the argument hash. The Array() call coerces an array even when the key is not present, so you don't need to explicitly handle that error.
I would encourage you to look up the inject method - it's quite versatile and is useful in many situations. inject uses its own accumulator variable (optionally defined as an argument) which is yielded to the block as the first block parameter.

Use input name in return value variable name

I'm trying to make this simple method return a value related to the name of its input. For instance if I give the method "people_array" it should return "people_array_of_arrays."
If I were using the method in IRB I would get something like:
people_array = ["George\tMichael", "Kim\tKardashian", "Kanyne\tWest"]
=> ["George\tMichael", "Kim\tKardashian", "Kanyne\tWest"]
make_array_of_arrays(people_array)
=> people_array_of_arrays
people_array
=> ["George\tMichael", "Kim\tKardashian", "Kanyne\tWest"]
people_array_of_arrays
=> [["George", "Micahel"], ["Kim", "Kardashian"], ["Kayne", "West"]]
I have written this so far, but have not been able to figure out how to return a nicely named array of arrays. All I could think of was string interpolation but that isn't exactly what I need.
def make_array_of_arrays(array)
formatted_array = []
array.each do |feed|
mini_array = feed.split("\t")
formatted_array.push(mini_array)
end
#{array}_of_arrays = formatted_array
end
I saw there was a method variablize, but that returns an instance variable which isn't exactly what I want. Any pointers?
I do not think that it can be easily done. Suppose you were able to define a local variable in some way within the method definition. But the scope of that local variable is limited to the method definition. So the moment you go outside of the method definition, the local variable name is gone. So in order to do it, you have to somehow get the binding information of the environment outside of the method definition, and define a local variable within that. I do not know if that is possible.
With instance variables, things get a little easier using instance_variable_set, but I am not sure how to implement it fully. First of all, getting the name of the original variable is tricky.
And what you are trying to do is not the right approach. You should think of different ways.
I think the best you can do is to use an instance variable instead of a local variable, and also give the name of the variable explicitly instead of the array itself:
def make_array_of_arrays(variable_name)
array = instance_variable_get("##{variable_name}")
# Your code here
instance_variable_set("##{variable_name}_of_arrays", formatted_array)
end
#people_array = ["George\tMichael", "Kim\tKardashian", "Kanyne\tWest"]
make_array_of_arrays(:people_array)
#people_array_of_arrays
#=> [["George", "Micahel"], ["Kim", "Kardashian"], ["Kayne", "West"]]
This also might be useful.
No need for meta-programming (unless I misunderstand your question). Simply return your formatted array:
def make_array_of_arrays(array)
formatted_array = []
array.each do |feed|
mini_array = feed.split("\t")
formatted_array.push(mini_array)
end
formatted_array
end
Then you can assign the return value to whatever name you want:
people_array_of_arrays = make_array_of_arrays(people_array)
Note, you can use map to simplify your make_array_of_arrays method:
def make_array_of_arrays(array)
array.map do |feed|
feed.split("\t")
end
end
The big problem here is there's no good way to access the name of a variable.
Barring that and building on Sean Vieira's addition, you could do some eval magic to get this:
def make_array_of_arrays(array, array_name)
new_array = array.map { |feed| feed.split("\t") }
eval("def #{array_name}_of_arrays; return #{new_array}; end")
end
This basically creates a function for your *_of_arrays line that returns the array you're looking for.
If you could find a way to get the name of the variable, you'd have everything you want.
Not that I really officially endorse this method. And I can't for the life of me figure out why you'd want to do this. It's very unidiomatic, and will be confusing for anyone looking at that chunk of code.
This is not easy nor advisable.
Think of Ruby Objects as people (you and me) communicating by phone (phonenumbers being object_id's). Imagine I am in your list of phonenumbers under the name (variable) 'sTeEnSlAg' , and also under 'steenslg'. Then you phone me and ask "Please give me the name you are registered under on my phone, post-fixed with "_of_arrays".
What do you think would be the polite version of my answer?

Ruby case statement with multiple variables using an Array

I'd like to compare multiple variables for a case statement, and am currently thinking overriding the case equals operator (===) for Array is the best way to do it. Is this the best way?
Here is an example use case:
def deposit_apr deposit,apr
# deposit: can be nil or 2 length Array of [nil or Float, String]
# apr: can be nil or Float
case [deposit,apr]
when [[Float,String],Float]
puts "#{deposit[0]} #{deposit[1]}, #{apr*100.0}% APR"
when [[nil,String],Float]
puts "#{apr*100.0}% APR on deposits greater than 100 #{deposit[1]}"
when [[Float,String],nil]
puts "#{deposit[0]} #{deposit[1]}"
else
puts 'N/A'
end
end
The only problem is the Array case equals operator doesn't apply the case equal to the elements of the Array.
ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
N/A
It will if I override, but am not sure what I'd be breaking if I did:
class Array
def ===(other)
result = true
self.zip(other) {|bp,ap| result &&= bp === ap}
result
end
end
Now, it all works:
ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
656.0 rupees, 6.5% APR
Am I missing something?
I found this question because I was looking to run a case statement on multiple variables, but, going through the following, came to the conclusion that needing to compare multiple variables might suggest that a different approach is needed. (I went back to my own code with this conclusion, and found that even a Hash is helping me write code that is easier to understand.)
Gems today use "no monkey patching" as a selling point. Overriding an operator is probably not the right approach. Monkey patching is great for experimentation, but it's too easy for things to go awry.
Also, there's a lot of type-checking. In a language that is designed for Duck Typing, this clearly indicates the need for a different approach. For example, what happens if I pass in integer values instead of floats? We'd get an 'N/A', even though that's not likely what we're looking for.
You'll notice that the example given in the question is difficult to read. We should be able to find a way to represent this logic more clearly to the reader (and to the writer, when they revisit the code again in a few months and have to puzzle out what's going on).
And finally, since there are multiple numbers with associated logic, it seems like there's at least one value object-type class (Deposit) that wants to be written.
For cleanliness, I'm going to assume that a nil APR can be considered a 0.0% APR.
class Deposit
def initialize(amount, unit='USD', options={})
#amount = amount.to_f # `nil` => 0.0
#unit = unit.to_s # Example assumes unit is always present
#apr = options.fetch(:apr, 0.0).to_f # `apr: nil` => 0.0
end
end
Once we have our Deposit object, we can implement the print logic without needing case statements at all.
class Deposit
# ... lines omitted
def to_s
string = "#{#amount} #{#unit}"
string << ", #{#apr * 100.0}% APR" if #apr > 0.0
string
end
end
d = Deposit.new(656.00, 'rupees', apr: 0.065)
d.to_s
# => "656.0 rupees, 6.5% APR"
e = Deposit.new(100, 'USD', apr: nil)
e.to_s
# => "100.0 USD"
f = Deposit.new(100, 'USD')
f.to_s
# => "100.0 USD"
Conclusion: If you're comparing multiple variables in a case statement, use that as a smell to suggest a deeper design issue. Multiple-variable cases might indicate that there's an object that wants to be created.
If you are worried about breaking something by changing Array behavior, and certainly that's a reasonable worry, then just put your revised operator in a subclass of Array.
it's definitely not the best way. even more - you should not redefine methods of standart classes as core functionality may depend on it - have fun debugging then.
defensive style is nice(with lot of type checks and whatnot) but it usually hurts performance and readability.
if you know that you will not pass anything else than bunch of floats and strings to that method - why do you need all those checks for?
IMO use exception catching and fix the source of problem, don't try to fix the problem somewhere in the middle

Where and when to use Lambda?

I am trying to understand why do we really need lambda or proc in ruby (or any other language for that matter)?
#method
def add a,b
c = a+b
end
#using proc
def add_proc a,b
f = Proc.new {|x,y| x + y }
f.call a,b
end
#using lambda function
def add_lambda a,b
f = lambda {|x,y| x + y}
f.call a,b
end
puts add 1,1
puts add_proc 1,2
puts add_lambda 1,3
I can do a simple addition using: 1. normal function def, 2. using proc and 3. using lambda.
But why and where use lambda in the real world? Any examples where functions cannot be used and lambda should be used.
It's true, you don't need anonymous functions (or lambdas, or whatever you want to call them). But there are a lot of things you don't need. You don't need classes—just pass all the instance variables around to ordinary functions. Then
class Foo
attr_accessor :bar, :baz
def frob(x)
bar = baz*x
end
end
would become
def new_Foo(bar,baz)
[bar,baz]
end
def bar(foo)
foo[0]
end
# Other attribute accessors stripped for brevity's sake
def frob(foo,x)
foo[0] = foo[1]*x
end
Similarly, you don't need any loops except for loop...end with if and break. I could go on and on.1 But you want to program with classes in Ruby. You want to be able to use while loops, or maybe even array.each { |x| ... }, and you want to be able to use unless instead of if not.
Just like these features, anonymous functions are there to help you express things elegantly, concisely, and sensibly. Being able to write some_function(lambda { |x,y| x + f(y) }) is much nicer than having to write
def temp(x,y)
x + f(y)
end
some_function temp
It's much bulkier to have to break off the flow of code to write out a deffed function, which then has to be given a useless name, when it's just as clear to write the operation in-line. It's true that there's nowhere you must use a lambda, but there are lots of places I'd much rather use a lambda.
Ruby solves a lot of the lambda-using cases with blocks: all the functions like each, map, and open which can take a block as an argument are basically taking a special-cased anonymous function. array.map { |x| f(x) + g(x) } is the same as array.map(&lambda { |x| f(x) + g(x) }) (where the & just makes the lambda "special" in the same way that the bare block is). Again, you could write out a separate deffed function every time—but why would you want to?
Languages other than Ruby which support that style of programming don't have blocks, but often support a lighter-weight lambda syntax, such as Haskell's \x -> f x + g x, or C#'s x => f(x) + g(x);2. Any time I have a function which needs to take some abstract behavior, such as map, or each, or on_clicked, I'm going to be thankful for the ability to pass in a lambda instead of a named function, because it's just that much easier. Eventually, you stop thinking of them as somehow special—they're about as exciting as literal syntax for arrays instead of empty().append(1).append(2).append(3). Just another useful part of the language.
1: In the degenerate case, you really only need eight instructions: +-<>[].,. <> move an imaginary "pointer" along an array; +- increment and decrement the integer in the current cell; [] perform a loop-while-non-zero; and ., do input and output. In fact, you really only need just one instruction, such as subleq a b c (subtract a from b and jump to c if the result is less than or equal to zero).
2: I've never actually used C#, so if that syntax is wrong, feel free to correct it.
Blocks are more-or-less the same thing
Well, in Ruby, one doesn't usually use lambda or proc, because blocks are about the same thing and much more convenient.
The uses are infinite, but we can list some typical cases. One normally thinks of functions as lower-level blocks performing a piece of the processing, perhaps written generally and made into a library.
But quite often one wants to automate the wrapper and provide a custom library. Imagine a function that makes an HTTP or HTTPS connection, or a straight TCP one, feeds the I/O to its client, and then closes the connection. Or perhaps just does the same thing with a plain old file.
So in Ruby we would put the function in a library and have it take a block for the user .. the client .. the "caller" to write his application logic.
In another language this would have to be done with a class that implements an interface, or a function pointer. Ruby has blocks, but they are all examples of a lambda-style design pattern.
1) It is just a convenience. You don't need to name certain blocks
special_sort(array, :compare_proc => lambda { |left, right| left.special_param <=> right.special_param }
(imagine if you had to name all these blocks)
2) #lambda is usually used to create clojures:
def generate_multiple_proc(cofactor)
lambda { |element| element * cofactor }
end
[1, 2, 3, 4].map(&generate_multiple_proc(2)) # => [2, 3, 5, 8]
[1, 2, 3, 4].map(&generate_multiple_proc(3)) # => [3, 6, 9, 12]
It comes down to style. Lambdas are a a declarative style, methods are an imperative style. Consider this:
Lambda, blocks, procs, are all different types of closure. Now the question is, when and why to use an anonymous closure. I can answer that - at least in ruby!
Closures contain the lexical context of where they were called from. If you call a method from within a method, you do not get the context of where the method was called. This is due to the way the object chain is stored in the AST.
A Closure (lambda) on the other hand, can be passed WITH lexical context through a method, allowing for lazy evaluation.
Also lambdas naturally lend themselves to recursion and enumeration.
In case of OOP, you should create a function in a class only if there should be such an operation on the class according to your domain modeling.
If you need a quick function which can be written inline such as for comparison etc, use a lambda
Also check these SO posts -
When to use lambda, when to use Proc.new?
C# Lambda expressions: Why should I use them?
When to use a lambda in Ruby on Rails?
They're used as "higher-order" functions. Basically, for cases where you pass one function to another, so that the receiving function can call the passed-in one according to its own logic.
This is common in Ruby for iteration, e.g. some_list.each { |item| ... } to do something to each item of some_list. Although notice here that we don't use the keyword lambda; as noted, a block is basically the same thing.
In Python (since we have a language-agnostic tag on this question) you can't write anything quite like a Ruby block, so the lambda keyword comes up more often. However, you can get a similar "shortcut" effect from list comprehensions and generator expressions.
I found this helpful in understanding the differences:
http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/
But in general the point is sometimes your writing a method but you don't know what you're going to want to do at a certain point in that method, so you let the caller decide.
E.g.:
def iterate_over_two_arrays(arr1, arr2, the_proc)
arr1.each do |x|
arr2.each do |y|
# ok I'm iterating over two arrays, but I could do lots of useful things now
# so I'll leave it up to the caller to decide by passing in a proc
the_proc.call(x,y)
end
end
end
Then instead of writing a iterate_over_two_arrays_and_print_sum method and a iterate_over_two_arrays_and_print_product method you just call:
iterate_over_two_arrays([1,2,3], [4,5,6], Proc.new {|x,y| puts x + y }
or
iterate_over_two_arrays([1,2,3], [4,5,6], Proc.new {|x,y| puts x * y }
so it's more flexible.

Resources