Given a class that has method which accepts a splat, for example a method you get from ActiveRecord::FinderMethods:
class Settle
def self.find(*args)
end
end
How should I call that method from another method, say another class, so that it has the exact same signature?
class Settler
def self.find(*args)
Settle.find(*args)
end
end
Or
class Settler
def self.find(*args)
Settle.find(args)
end
end
Or something else?
Note that the exact same signature is the important part: Settler.find should work exactly similar to Settle.find.
I am not, however, interested in code that allows the Settler.find signature to magically update whenever Settle.find changes into something completely different, like e.g. .find(scope, *args). In that case, updating the version in Settler is no problem.
It should be
Settle.find(*args)
This way, all the arguments passed into Settler.find, are passed also to Settle.find and stored in args array inside of it. So the args arrays inside of both methods hold the same value.
Passing arguments as 'splat' is quite simple - it's just passing an array as separate arguments. So if you have
ar = [arg1, arg2, arg3, argn]
calling
some_method(*ar)
is equivalent to
some_method(arg1, arg2, arg3, argn)
Related
I am trying to write this inside my class:
class << self
def steps
#steps.call
end
def transitions
#transitions.call
end
def steps(&steps)
#steps = steps
end
def transitions(&transitions)
#transitions = transitions
end
end
That won't work since in Ruby, I can't do this kind of method overloading. Is there a way around this?
You can kind of do this with method aliasing and mixins, but the way you handle methods with different signatures in Ruby is with optional arguments:
def steps(&block)
block.present? ? #steps = block : #steps.call
end
This sort of delegation is a code smell, though. It usually means there's something awkward about the interface you've designed. In this case, something like this is probably better:
def steps
#steps.call
end
def steps=(&block)
#steps = block
end
This makes it clear to other objects in the system how to use this interface since it follows convention. It also allows for other cases, like passing a block into the steps method for some other use:
def steps(&block)
#steps.call(&block)
end
Ruby does not support method overloading (see "Why doesn't ruby support method overloading?" for the reason). You can, however, do something like:
def run(args*)
puts args
end
args will then be an array of the arguments passed in.
You can also pass in a hash of options to handle arguments, or you can pass in nil when you don't want to supply arguments and handle nil in your method body.
Beginner in ruby world, I would like to do something like:
[1,2.0,"a",2].select(&:is_a?(Integer))
but like this it definitely don't work...
Any ideas?
You can't do what you are asking for because when you use the & syntax you have to use a method that doesn't take parameters.
However, if you for some reason you really want to do something like that, you need to make a method that doesn't take parameters like so:
class Object
def is_an_integer?
is_a? Integer
end
end
You can then do:
[1,2.0,"a",2].select(&:is_an_integer)
&:method_name is syntactic sugar for &:method.to_proc. Enumerators like select and whatnot accept a block and yield each element of the enumerator to the passed block. That is:
[1,2,3].select &:even?
is equivalent to:
p = :even.to_proc
[1,2,3].select {|val| p.yield(val) }
Since only the parameters yielded by the enumerator are yielded to the proc, you would have to include them in the source list. That is, we might expect:
[[1, Integer]].select &:is_a?
to result in:
select {|*args|, p.yield(*args) }
However, remember that p isn't a method bound to any particular class! It's going to try to invoke the given method on the passed argument. So, it's going to try to invoke Array#is_a? with no arguments, rather than splatting the arguments out and invoking Integer#is_a?(Integer).
So, to accomplish this, we'll have to somehow create a proc that binds the passed arguments, and then calls the given method on the yielded receiver with the passed args. We can do this by adding a method to the Symbol class:
class Symbol
def with_args(*args)
proc {|receiver| receiver.send(self, *args) }
end
end
[1, "a"].select &:is_a?.with_args(Integer)
While it's perhaps not amazingly clean, it does work.
I'm trying to understand something from the Ruby Koans. In one lesson, we make two classes as follows:
class CanNotBeTreatedAsString
def to_s
"non-string-like"
end
end
not_like_a_string = CanNotBeTreatedAsString.new
not_like_a_string == "non-string-like"
class CanBeTreatedAsString
def to_s
"string-like"
end
def to_str
to_s
end
end
like_a_string = CanBeTreatedAsString.new
like_a_string.to_str == "string-like"
def acts_like_a_string?(string)
string = string.to_str if string.respond_to?(:to_str)
string.is_a?(String)
end
assert_equal false, acts_like_a_string?(CanNotBeTreatedAsString.new)
assert_equal true, acts_like_a_string?(CanBeTreatedAsString.new)
So the two classes and last two "assert" statements are what I'm not clear about. The two classes are nearly identical, except for the fact that the second class simply has another function to_str that makes a call to to_s. I don't see why the second assert statement is true (and thus the second class can be treated as a string) simply because there's a second function making a call to the first function.
There's no magic here. The second test is checking for the presence of to_str method. This is not defined for CanNotBeTreatedAsString but it is defined for CanBeTreatedAsString.
The function of respond_to? is to test if a method is defined, and in Ruby 2.0 it will further indicate if it can be called. Protected methods, which cannot be called, will no longer count. For now if respond_to?(:method_name) returns true then send(:method_name) with any required arguments would theoretically work.
The second class could use alias :to_str, :to_s to achieve the same result with less code.
The point of this lesson is to illustrate the principle known as 'duck-typing.' Basically, if it looks like a duck and quacks like a duck, then it is a duck. In this case, the only factor that determines if something is a string (or rather acts like a string) is if it responds to the to_str method.
Try running this code in an interactive ruby (irb) session and experimenting with the two classes. You will find that an instance of each class will respond to to_s but only CanBeTreatedAsString will respond to to_str. That means that, as far as Ruby is concerned, CanBeTreatedAsString is as much of a String as anything else that responds to to_str.
I know what this means:
def f(*args)
...
end
But what does this mean and why would you want to use it? Can it appear with named parameters, too?
def f(*)
...
end
def f(*) has the same effect as def f(*args), except that it does not name the globbed argument array. You might use it if you want the function to accept any number of arguments but don't actually need to refer to them within the function -- for example, if you are overriding a method but calling super without passing an explicit argument list, which results in the original arguments being passed to super.
You can write def f(a, b, *) as well.
I know what this means:
def f(*args)
...
end
But what does this mean and why would you want to use it? Can it appear with named parameters, too?
def f(*)
...
end
def f(*) has the same effect as def f(*args), except that it does not name the globbed argument array. You might use it if you want the function to accept any number of arguments but don't actually need to refer to them within the function -- for example, if you are overriding a method but calling super without passing an explicit argument list, which results in the original arguments being passed to super.
You can write def f(a, b, *) as well.