How to pass an optional method parameter as string in Ruby - ruby

I would like to call a method in Ruby, which has an optional parameter.
I tried some ways, but none of them is working.
Can you help me, how can I call this method?
I never used Ruby before, so please help me refine the question itself. I tried googling the problem, but I think I use the wrong terms.
I read this: Ruby Methods and Optional parameters and this: A method with an optional parameter, but with no luck.
The method looks like this:
def method(param1, param2, options={})
...
if options["something"]
...
end
...
end
I tried the call, for example, like this:
method("param1", "param2", :something => true)
With my tries, the code runs but does not enter in the if condition.
I would like to call this method in the way, that the codes in the if statement would be run.

It doesn't work because you are sending symbol (:something) instead of string key ('something'). They are different objects.
Change:
method("param1", "param2", :something => true)
to
method("param1", "param2", 'something' => true)
or handle in method by if options[:something]

Call your method with the same param type, or if you want to be able to pass either symbol or string key you can handle that in your method.
def foo(a,b, opt={})
if opt[:something] || opt['something']
puts 'something'
end
end
Now you can call this with string or symbol keys:
foo('a','b', 'something' => true )
#or
foo('a','b', something: true )

Related

javascript arguments parameter in ruby

I have a function with no parameters declared in its firm, but I need to obtain them if eventually any was passed to it.
For example, in javascript I can have a function as follows:
function plus() {
return operator("+", arguments);
}
As you can see, I can obtain the function arguments via "arguments" implicit parameter.
Does ruby have something similar to javascript argument parameter?
Thanks.
PS: I did a previous research in google, stackoverflow and this book with no result, maybe there is a workaround for this and no an official way to obtain it.
How about using variable length arguments:
def plus(*args)
# Do something with `args` array
end
In ruby you can always put optional arguments in a hash, such as
def some_function(args = {})
end
and you can call it like
some_function :arg1 => some_integer, :arg2 => "some_string"

Ruby instance_exec / instance_eval with arguments

I'm trying to dynamically call a method given in a string using parameters given in the same string, I'm getting stuck on supplying the parameters though...
I currently have:
query = Query.new
while true
input = gets.split(%r{[/[[:blank:]]/,]})
puts (query.instance_exec(*input.drop(1)) { |x|
instance_eval input.at(0)
})
end
So the method name is input(0) and the arguments to this method are in the rest of input.
Is there any way to call this method with those parameters?
The method you are looking for is send. Its first argument will be the method, and the rest will be passed to that method.
query = Query.new
puts query.send(*gets.split(/\s+/)) while true
You can use while modifier.
Your regex looks complicated. I made it look simple.
Don't forget to use the splat operator *, which decomposes an array.

How can I make a custom Ruby type behave like a string?

If I have a custom Ruby class representing some string type, as in
class MyString
end
Which functions should I implement in order to make the following use cases possible:
Passing a Ruby string whenever a MyString is expected
Passing a MyString whenever a Ruby string is expected
Comparing a Ruby string with a MyString value (it shouldn't matter whether I use s == t or t == s).
I saw various interesting functions like to_s, cmp, == and eq already, but it's not clear to me when each of them is called.
My concrete use case is that I'm writing a Ruby extension using the C API which exposes functions taking (and returning) values of a custom string type (QString, to be precise) which my extension also registers. However, I'd like to make those custom strings behave as intuitive as possible. Unfortunately I can't just return Ruby strings from my C code since it should be possible to call Qt methods on the strings.
There are at least three approaches:
class MyString < String; ...; end
Define #to_s
Define #to_str
Doing both #2 and #3 will make the object act very much like a real String even if it isn't a subclass.
#to_s is an explicit converter, meaning it must appear in Ruby code to work.
#to_str is an implicit converter, meaning the Ruby interpreter will attempt to call it when it wants a String but is given something else.
Update:
Here is an example of some fun you can have with to_str:
begin
open 1, 'r'
rescue TypeError => e
p e
end
class Fixnum
def to_str; to_s; end
end
open 1, 'r'
When run, the first open fails with TypeError but the second proceeds to looking for 1.
#<TypeError: can't convert Fixnum into String>
fun.rb:9:in `initialize': No such file or directory - 1 (Errno::ENOENT)
from fun.rb:9:in `open'
Although it's tempting to sub-class String to give it a new initialize method that will import these QString-type strings, you may just want to tack on an extension to String that helps with the conversion so you don't have to re-implement a version of String itself.
For instance, with two methods you could pretty much have this done:
class String
def self.from_qstring(qstring)
new(...)
end
def to_qstring
# ...
end
end
Having multiple storage types for String is not going to be a problem until you start comparing them, but given that Ruby's String is quite robust, writing a work-alike is difficult.
It's not generally a good idea to subclass classes that were built by someone else in Ruby, because too many things can go wrong. (You might, for example, override an internal method without knowing it.)
1) define Mystring.to_s to get automatic conversion from a Mystring to a String.
2) Not sure what you mean by this. If you want a String method that returns a Mystring, you will have to monkey-patch String:
Class String
def to_mystring
return Mystring.new(self)
end
end
3) to get t == s (assuming s is an instance of String and t an instance of Mystring) define <=>. To get s == t you will have to monkey patch String again, though.
Since I was looking for something similar, but none of the other answers worked for me, I'll post what did work for me.
Found in this blog post which discourage the use of inheriting String and instead use simple delegator.
Inheriting from SimpleDelegator create an object which delegate everything to a string of your choice but on which you add behavior as you see fit.
class ChunkyBacon < SimpleDelegator
def initialize(content)
#content = content
super #content
end
def chunky_bacon?
#content == 'chunky_bacon'
end
end
test = ChunkyBacon.new('choco pizza') # => 'choco pizza'
test.chunky_bacon? # => false

Ruby: How to evalulate multiple methods per send command?

Let's say I have an XML::Element...I want to do something like:
my_xml_element.send("parent.next_sibling.next_sibling")
In your case it's better to use instance_eval
"Test".instance_eval{chop!.chop!} #=> "Te"
And for your code:
my_xml_element.instance_eval{parent.next_sibling.next_sibling}
Actually, Khelll was almost right.
Use this:
methods_chain = "parent.next_sibling.next_sibling"
result = my_xml_element.instance_eval( eval methods_chain )
This code is up to 20 times faster than using split() and allows you to pass chain methods with args, like this:
methods = "power!(2).div(57).+(12).to_f"
42.instance_eval { eval methods }
uh, that's not really what he was asking for if I'm understanding his question correctly. I mean send takes a string or a symbol as an arg, and your solution doesn't. I don't think there's a built in method that will do what you want, but I whipped up a method that will, with a test.
require 'test/unit'
class Example
def multi_send(str)
str.split('.').inject(self){|klass, method| klass.send(method) }
end
end
class MyTest < Test::Unit::TestCase
def test_multi_send
a = Example.new
methods = "class.to_s.downcase.chop!"
assert a.multi_send(methods) == 'exampl'
end
end
I think the question is that you specifically have a series of methods defined as a string, and you want to invoke that on some object, right?
class Object
def call_method_chain(method_chain)
return self if method_chain.empty?
method_chain.split('.').inject(self){|o,m| o.send(m.intern)}
end
end
>> User.first.profile.address.state.name
=> "Virginia"
>> User.first.call_method_chain("profile.address.state.name")
=> "Virginia"
The problem with Alfuken's answer based on eval is
A) eval is pretty unsafe although fast
B) if you have an extra (consecutive) dot for method invocation like this
"example".instance_eval{eval "chop!..chop!"}.class
=> "examp".."examp"
"example".instance_eval {eval "chop!..chop!"}.class
=> Range # this is not what is desired

Elegant way of duck-typing strings, symbols and arrays?

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'

Resources