Why is NilClass#to_hash lacking? - ruby

Various methods defined on NilClass are handy to avoid Method undefined on NilClass errors, and frees you from using try, oror, andand, ||, && in various occasions:
to_a for multiple dimensioned array when there might not be some values for some indices:
array[i].to_a[j].to_a[k]
to_s pattern match that might fail:
string[regex].to_s*2
to_i, to_f, to_c for index search that might fail, etc:
array.index(element).to_i*3
But there is no NilClass#to_hash although there is Hash#to_hash.
If such method existed (class NilClass; def to_hash; {} end end), then we would be able to do:
to_hash for multiple embedded hash when there might not be some values for some keys:
hash[:a].to_hash[:b].to_hash[:c]
The best alternative I can think of is:
hash.fetch(:a, {}).fetch(:b, {})[:c]
but it would be nice if we had NilClass#to_hash. Why is it missing?

It's a good point, and in Ruby, you can always monkeypatch such a method into nil, and the libraries in Rails do quite a bit of that kind of monkeypatching. Personally, I try to avoid doing much of that, because altering the behavior of a widely-used class can have unforeseen side effects in other libraries. If the patch is being performed by a popular framework, that's fine, because other libraries should have been tested with that, but doing it willy nilly is something else, altogether.
I'm thinking a NilClass#to_hash would exist by now, if that would address a pain point that a lot of folks were feeling. The try syntax is not any longer, and not too horribly ugly.
hash.try(:[],a).try(:[],b).try(:[],c)

Edited
I sent a request about NilClass#to_hash to ruby development. Matz rejected it shortly. Andrew notified me in the comment below that Matz gave a comment to it. Answer to the question is thus given my Matz, and you can see that by following Andrew's link below.

I would also be interested in a NilClass#to_hash method.
Here is the answer by Matz:
to_a, etc. are explicit conversion methods, whereas to_int, to_hash,
etc. are implicit conversion methods. I don't see any good reason to
add implicit conversion methods to nil.
Here is the question by Tsuyoshi Sawada:
Various methods defined on NilClass are handy to avoid Method
undefined on NilClass errors. For example,
to_a for multiple dimensioned array when there might not be some values for some indices:
array[i].to_a[j].to_a[k]
to_s for pattern match that might fail:
string[regex].to_s*2
to_i, to_f, to_c for index search that might fail, etc:
array.index(element).to_i*3
But there is no NilClass#to_hash even though there is
Hash#to_hash. If such method existed (equivalent to class
NilClass; def to_hash; {} end end), then we would be able to do:
to_hash for multiple embedded hash when there might not be some values for some keys:
hash[:a].to_hash[:b].to_hash[:c]
Is there any reason why to_a, to_s, to_i, to_f, to_c are all
defined on NilClass but not for to_hash? If there is not, I would
like to request this feature.
Source

Related

Ruby to_s a special method? [duplicate]

I'm learning Ruby and I've seen a couple of methods that are confusing me a bit, particularly to_s vs to_str (and similarly, to_i/to_int, to_a/to_ary, & to_h/to_hash). What I've read explains that the shorter form (e.g. to_s) are for explicit conversions while the longer form are for implicit conversions.
I don't really understand how to_str would actually be used. Would something other than a String ever define to_str? Can you give a practical application for this method?
Note first that all of this applies to each pair of “short” (e.g. to_s/to_i/to_a/to_h) vs. “long” (e.g. to_str/to_int/to_ary/to_hash) coercion methods in Ruby (for their respective types) as they all have the same semantics.
They have different meanings. You should not implement to_str unless your object acts like a string, rather than just being representable by a string. The only core class that implements to_str is String itself.
From Programming Ruby (quoted from this blog post, which is worth reading all of):
[to_i and to_s] are not particularly strict: if an object has some kind of decent representation as a string, for example, it will probably have a to_s method… [to_int and to_str] are strict conversion functions: you implement them only if [your] object can naturally be used every place a string or an integer could be used.
Older Ruby documentation from the Pickaxe has this to say:
Unlike to_s, which is supported by almost all classes, to_str is normally implemented only by those classes that act like strings.
For example, in addition to Integer, both Float & Numeric implement to_int (to_i's equivalent of to_str) because both of them can readily substituted for an Integer (they are all actually numbers). Unless your class has a similarly tight relationship with String, you should not implement to_str.
To understand if you should use/implement to_s/to_str, let's look at some exemples. It is revealing to consider when these method fail.
1.to_s # returns "1"
Object.new.to_s # returns "#<Object:0x4932990>"
1.to_str # raises NoMethodError
Object.new.to_str # raises NoMethodError
As we can see, to_s is happy to turn any object into a string. On the other hand, to_str raises an error when its parameter does not look like a string.
Now let us look at Array#join.
[1,2].join(',') # returns "1,2"
[1,2].join(3) # fails, the argument does not look like a valid separator.
It is useful that Array#join converts to string the items in the array (whatever they really are) before joining them, so Array#join calls to_s on them.
However, the separator is supposed to be a string -- someone calling [1,2].join(3) is likely to be making a mistake. This is why Array#join calls to_str on the separator.
The same principle seems to hold for the other methods. Consider to_a/to_ary on a hash:
{1,2}.to_a # returns [[1, 2]], an array that describes the hash
{1,2}.to_ary # fails, because a hash is not really an array.
In summary, here is how I see it:
call to_s to get a string that describes the object.
call to_str to verify that an object really acts like a string.
implement to_s when you can build a string that describes your object.
implement to_str when your object can fully behave like a string.
I think a case when you could implement to_str yourself is maybe a ColoredString class -- a string that has a color attached to it. If it seems clear to you that passing a colored comma to join is not a mistake and should result in "1,2" (even though that string would not be colored), then do implement to_str on ColoredString.
Zverok has a great easily understandable article about when to use what (explained with to_h and to_hash).
It has to do whether your Object implementing those methods can be converted to a string
-> use to_s
or it is a type of some (enhanced) string
-> use to_str
I've seen a meaningful usage of to_hash in practice for the Configuration class in the gem 'configuration' (GitHub and Configuration.rb)
It represents -- as the name says -- the provided configuration, which in fact is a kind of hash (with additional features), rather than being convertible to one.

Provide alias for Ruby's built-in keyword

For example, I want to make Object#rescue another name so I can use in my code like:
def dangerous
something_dangerous!
dont_worry # instead of rescue here
false
end
I tried
class ::Object
alias :dont_worry :rescue
end
But cannot find the rescue method on Object:
`<class:Object>': undefined method `rescue' for class `Object' (NameError)
Another example is I would like to have when in the language to replace:
if cond
# eval when cond is truthy
end
to
when cond
# eval when cond is truthy
end
Is it possible to give a Ruby keyword alias done in Ruby?
Or I need to hack on Ruby C source code?
Thanks!
This is not possible without some deep changes to the Ruby language itself. The things you describe are not methods but keywords of the language, i.e. the actual core of what is Ruby. As such, these things are not user-changeable at all.
If you still want to change the names of the keywords, you would at least have to adapt the language parser. If you don't change semantics at all, this might do it as is. But if you want to change what these keywords represent, things get messy really quick.
Also note that Ruby in itself is sometimes quite ambiguous (e.g. with regards to parenthesis, dots, spacing) and goes to great length to resolve this in a mostly consistent way. If you change keywords, you would have to ensure that things won't get any more ambiguous. This could e.g. happen with your change of if to when. when is used as a keywords is case statements already and would thus could be a source of ambiguity when used as an if.

What is the class of keywords like def, alias, and begin in Ruby, if any?

As far as I understand, everything in Ruby is an object, and every object has a superclass, except BasicObject, which is at the top of the inheritance tree.
What is the superclass of keywords like def, begin, and alias?
They're keywords, not objects. If anything, they would be methods in Kernel, and have class Method as a result, but they're just keywords, and as such have neither class nor superclass. They're a special case and treated specially by the interpreter; they're parsed to produce the abstract syntax tree that the interpreter actually executes, and probably long gone by the time anything involving objects and classes is done. After all, how would end work as a method?
Note that not everything that looks like a keyword is one. Take, for example, loop:
loop do
puts 'Hello, world!'
end
While it may look like a keyword, it's actually the method Kernel#loop.
By far the easiest way to tell if something is a method or a keyword is to run this long, complicated code on it:
method(name_to_test)
where name_to_test is either a Symbol literal or an instance of Symbol. It uses the always-available method Object#method, which either returns the Method with that name or throws a NameError. If it operates silently -- i.e. doesn't raise any errors -- then you've got a method; if it raises an error, it's not a method. Note that it could also be a variable, not a keyword or method, but it should be easy enough to tell by looking at the previous code in the file, and a quick search through the docs.
If you want to see the current list of keywords (or don't feel like booting up IRB/your favorite IDE), check this file in the RMI source. It's a little hard to understand, but basically, if you see keyword_[thing you're looking for] in that list (possibly with a leading _ removed), it's a keyword. To make this answer as self-contained as possible, here's the (current) list of keywords, based on that:
__LINE__, __FILE__, __ENCODING__, BEGIN, END, alias, and, begin, break, case, class, def, defined, do, else, elsif, end, ensure, false, for, in, module, next, nil, not, or, redo, rescue, retry, return, self, super, then, true, undef, when, yield, if, unless, while, until
(Many thanks to engineersmnky for pointing the list out! Never would have found it on my own.)

Ruby - What's the difference between a method with and without an exclamation point?

For example, I've checked the documentation on certain methods, like sort. And it looks like the only difference between .sort and .sort! is that one sorts self in place and the other returns an array. I'm a little unclear on what that means - they seem to effectively do the same thing.
Can anyone help me understand this a little better?
When to Use Bang Methods
Technically, the exclamation point (or bang) doesn't intrinsically mean anything. It's simply an allowable character in the method name. In practice, however, so-called bang methods generally:
Changed objects in-place. For example, #sort! sorts self in place, while #sort returns a new array created by sorting self.
Some bang methods return nil if no changes were made, which can cause problems with method chains. For example:
'foo'.sub 'x', 'y'
# => "foo"
'foo'.sub! 'x', 'y'
#=> nil
Use bang methods when you want to mark a method as creating notable side effects, producing destructive operations, or otherwise requiring additional caution or attention. This is largely by convention, though, and you could make all your methods bang methods if you were so inclined.
Methods with a bang(!) are meant to signify a little more caution is required. So, either modification in place vs. not in place (if you are modifying the object in place - you better be sure that you really want to), or in other cases like find_by and find_by! (see here) where one causes an exception if no record is found and one doesn't cause an exception.
Can you guess which one does and which one does not cause an exception?
The methods with the exclamation point alter the actual object they're called on, where as the methods without will just return a new object that has been manipulated.
i.e.
pizza = 'pepperoni'
pizza.capitalize
Now the pizza variable will still equal 'pepperoni'.
If we then call
pizza.capitalize!
The pizza variable will now equal 'Pepperoni'

Array of Types in Ruby

I am trying to create instances of objects of various types by iterating and checking for validity. I need an array of types so I can do something like this:
def tryClasses(in)
types = [Foo::A, Foo::B, Foo::C]
types.each do |type|
a = type.new(in)
return a != null
end
end
How do I create and array of class types?
Doing it this way I am getting a NoMethodError (undefined method 'A' for Foo)
Apart from the obvious syntactic errors (e.g. in is a reseved word, and null is spelled nil in Ruby), the code you showed should work just fine as it is, and indeed it does when I copy&paste it into my Ruby installation. This assumes, of course, that the classes Foo::A, Foo::B and Foo::C actually exist. If they don't, then the code obviously cannot possibly work.
It is, however, completely un-Rubyish and violates just about every coding convention in the book:
indentation is 2 spaces
method names are snake_case, not camelCase
explicitly checking for equality to nil is a no-no, simply calling #nil? is much preferred
try_classes isn't exactly an intention-revealing method name
and WTF does in mean?
Rubyists much prefer higher-order methods over explicit looping
Here's a more Rubyish version of the code you wrote:
def can_create_object?(*args)
[Foo::A, Foo::B, Foo::C].none? do |klass|
klass.new(*args).nil?
end
end
However, note that I am pretty convinced that the whole idea is fundamentally flawed.

Resources