Ruby - equivalent of Python __str__() method? - ruby

In Ruby, is there the equivalent of the __str__() method that you can define on Python classes?

You could use to_s.
http://briancarper.net/2006/09/26/ruby-to_s-vs-to_str/

FWIW, inspect is probably more like __repr__() than __str__()
from the library reference...
repr( self)
Called by the repr() built-in function and by string conversions (reverse quotes) to compute the ``official'' string representation of an object. If at all possible, this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment). If this is not possible, a string of the form "<...some useful description...>" should be returned. The return value must be a string object. If a class defines __repr__() but not __str__(), then __repr__() is also used when an ``informal'' string representation of instances of that class is required.

On the core classes it is typically 'inspect'.
Eg:
irb(main):001:0> puts "array is: #{[1,2,3].inspect}"
array is: [1, 2, 3]
=> nil
irb(main):002:0> puts "array is: #{[1,2,3]}"
array is: 123
=> nil
irb(main):003:0>

Related

Convert string into hash in ruby

I have a string like this "{ssl:true,sslAllowInvalidCertificates:true}"
(please note that the string can contain any no. of key/value pairs)
I want to convert this into hash, in Ruby, like this:
{ssl:true,sslAllowInvalidCertificates:true}
(Please note that the output is to be exactly similar to the above. It should not be in 'generic' hash notation like
{"ssl" => "true","sslAllowInvalidCertificates" => "true"}
The MongoDB client library can recognize the option only if it is exactly same as per requirement, else throws error.
How to do this in ruby?
TIA!
TL;DR
To convert your String into a Hash, you either have to parse it yourself, or call Kernel#eval on it. Either way, your real issue seems to be round-tripping this back to a string in your expected format. One way to do that is to re-open the Hash class and add a custom output method, rather than relying on the Hash#to_s method aliased to Hash#inspect.
String to Hash
If you trust the data source and have some mechanism to sanitize or check the String for potential arbitrary code execution, #eval is certainly the easiest thing you can do. I'd personally add a tiny bit of safety by making sure the String isn't tainted first, though. For example:
str = "{ssl:true,sslAllowInvalidCertificates:true}"
raise "string tainted: #{str}" if str.tainted?
hsh = eval str
#=> {:ssl=>true, :sslAllowInvalidCertificates=>true}
However, if you don't trust the source or structure of your String, you can parse and validate the information yourself using some variant of the following as a starting point:
hsh = Hash[str.scan(/\w+/).each_slice(2).to_a]
#=> {:ssl=>true, :sslAllowInvalidCertificates=>true}
Hash to Custom String
If you then want to dump it back out to your custom format as a String, you can monkeypatch the Hash class or add a singleton method to a given Hash instance to provide a #to_mongo method. For example:
class Hash
def to_mongo
str = self.map { |k, v| '%s:%s' % [k, v] }.join ?,
'{%s}' % str
end
end
Calling this method on your Hash instance will yield the results you seem to want:
hsh.to_mongo
#=> "{ssl:true,sslAllowInvalidCertificates:true}"
It seems there is some confusion surrounding the fat arrow syntax for hashes in ruby. You should be able to run eval on the string to generate the following hash:
{:ssl=>true, :sslAllowInvalidCertificates=>true}
You mention that the output cannot be in "generic" hash notation, which I assume is referring to the fat arrow notation used in your example.
Since Ruby 1.9, a new syntax can be used to create a hash
{foo: "bar"}
rather than the previous
{:foo => "bar"}
Interactive ruby consoles, such as irb and pry, try to print human friendly strings for the hash. Creating a hash with either of the two previous syntaxes will produce the same result in the console:
{:foo=>"bar"}
However, in memory, both of the objects are equivalent.
(There is the caveat that your "generic" hash example uses strings as keys. If that's what you're referring to, you can call #symbolize_keys on the hash)

Join doesn't convert the splat argument into a string in Ruby

Let's say we have this code:
def something(*someargs)
return *someargs.join(",")
end
Now, I found you can reference *someargs just like any other variable anywhere in the method definition. But I tried this...returning *someargs as a string, separated with a comma. Yet, when I call this method:
a = something(4, 5)
p a.class # => Array
p a #> ["4,5"]
why does something(4,5) still returns an array? If I do something like this:
[4, 5].join(",")
the result will be a string not in an array. So my question would be, how do I make the "something" method return an actual string which contains all the arguments as a string. And it's weird because if I do *someargs.class, the result is "Array", yet it doesn't behave like a typical array...
Try below :
def something(*someargs)
return someargs.join(",")
end
a = something(4, 5)
p a.class # => String
p a # => "4,5"
One example to explain your case:
a = *"12,11"
p a # => ["12,11"]
So when you did return *someargs.join(","), someargs.join(",") created the string as "4,5".But now you are using splat operator(*) again on the evaluated string "4,5" with the assignment operation like a = *"4,5". So finally you are getting ["4,5"].
Read more scenarios with splat operators here - Splat Operator in Ruby
Hope that helps.
An object prepended with a splat *... is not an object. You cannot reference such thing, nor can you pass it as an argument to a method because there is no such thing. However, if you have a method that can take multiple arguments, for example puts, then you can do something like:
puts *["foo", "bar"]
In this case, there is no such thing as *["foo", "bar"]. The splat operator is expanding it into multiple arguments. It is equivalent to:
puts "foo", "bar"
Regarding why someargs remains to be an array after someargs.join(","). That is because join is not a destructive method. It does not do anything to the receiver. Furthermore, an object cannot change its class by a destructive method. The only way to change the reference of someargs from an array to a string is to reassign it.

Is it safe to add this #to_proc method to String to be able to map over Ruby hashes?

You can't use Enumerable#map to look up the same value from each element an array of
hashes using the &:method_name shortcut:
# INVALID:
[{a:'bar', b:'world'}, {a:'baz', b:'boston'}].map &:[:a]
But you can get around this by adding a #to_proc method to String. You can write this new #to_proc method so that
you use pass &"key" to the enumerator to look up a value by the key.
res = [{a:'bar', b:'world'}, {a:'baz', b:'boston'}].map &":a"
puts res.inspect
#=> ["bar", "baz"]
Compare to what you have to write otherwise:
res = [{a:'bar', b:'world'}, {a:'baz', b:'boston'}].map {|x| x[:a]}
Here is how you would patch the String class to make this work:
class String
def to_proc
->(x) { x.send :[], (self[0] == ':' ? self[1..-1].to_sym : self) }
end
end
Note that this will
only work with hashes with string or symbol keys.
My question: Is this safe to do and OK as far as good Ruby practices go? It's
a rather global change, but I'm not aware of any side effects this could have,
and it would arguably make my code more concise in a lot of places.
Instead of using hashes, consider using OpenStruct:
require 'ostruct'
a = [
OpenStruct.new(a:'bar', b:'world'),
OpenStruct.new(a:'baz', b:'boston'),
]
p a.map(&:a)
# => ["bar", "baz"]
You pay the price up front by having to wrap hashes in OpenStruct, but later use becomes easier, and without the potentially confounding effects of amending the behavior of base classes.
If the hashes have behavior, consider making them regular ol' classes.

Is there a ruby equivalent to the Scala Option?

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.

Ruby: How to loop through an object that may or may not be an array?

I have an each method that is run on some user-submitted data.
Sometimes it will be an array, other times it won't be.
Example submission:
<numbers>
<number>12345</number>
</numbers>
Another example:
<numbers>
<number>12345</number>
<number>09876</number>
</numbers>
I have been trying to do an each do on that, but when there is only one number I get a TypeError (Symbol as array index) error.
I recently asked a question that was tangentally similar. You can easily force any Ruby object into an array using Array.
p Array([1,2,3]) #-> [1,2,3]
p Array(123) #-> [123]
Of course, arrays respond to each. So if you force everying into an array, your problem should be solved.
A simple workaround is to just check if your object responds to :each; and if not, wrap it in an array.
irb(main):002:0> def foo x
irb(main):003:1> if x.respond_to? :each then x else [x] end
irb(main):005:1> end
=> nil
irb(main):007:0> (foo [1,2,3]).each { |x| puts x }
1
2
3
=> [1, 2, 3]
irb(main):008:0> (foo 5).each { |x| puts x }
5
=> [5]
It looks like the problem you want to solve is not the problem you are having.
TypeError (Symbol as array index)
That error tells me that you have an array, but are treating it like a hash and passing in a symbol key when it expects an integer index.
Also, most XML parsers provide child nodes as array, even if there is only one. So this shouldn't be necesary.
In the case of arguments to a method, you can test the object type. This allows you to pass in a single object or an array, and converts to an array only if its not one so you can treat it identically form that point on.
def foo(obj)
obj = [obj] unless obj.is_a?(Array)
do_something_with(obj)
end
Or something a bit cleaner but more cryptic
def foo(obj)
obj = [*obj]
do_something_with(obj)
end
This takes advantage of the splat operator to splat out an array if it is one. So it splats it out (or doesn't change it) and you can then wrap it an array and your good to go.
I was in the same position recently except the object I was working with was either a hash or an array of hashes. If you are using Rails, you can use Array.wrap because Array(hash) converts hashes to an array.
Array({foo: "bar"}) #=> [[:foo, "bar"]]
Array.wrap({foo: "bar"}) #=> [{:foo=>"bar"}]
Array.wrap(123) #=> [123]
Array.wrap([123]) #=> [123]
I sometimes use this cheap little trick:
[might_be_an_array].flatten.each { |x| .... }
Use the splat operator:
[*1] # => [1]
[*[1,2]] # => [1,2]
Like Mark said, you're looking for "respond_to?" Another option would be to use the conditional operator like this:
foo.respond_to? :each ? foo.each{|x| dostuff(x)} : dostuff(foo);
What are you trying to do with each number?
You should try to avoid using respond_to? message as it is not a very object oriented aproach.
Check if is it possible to find in the xml generator code where it is assigning an integer value when there is just one <"number"> tag and modify it to return an array.
Maybe it is a complex task, but I would try to do this in order to get a better OO design.
I don't know much anything about ruby, but I'd assume you could cast (explicitly) the input to an array - especially given that if the input is simply one element longer it's automatically converted to an array.
Have you tried casting it?
If your input is x, use x.to_a to convert your input into an array.
[1,2,3].to_a
=> [1, 2, 3]
1.to_a
=> [1]
"sample string".to_a
=> ["sample string"]
Edit: Newer versions of Ruby seem to not define a default .to_a for some standard objects anymore. You can always use the "explicit cast" syntax Array(x) to achieve the same effect.

Resources