Ruby to_s a special method? [duplicate] - ruby

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.

Related

Syntax sugar for variable replacement with `round`

We have do-and-replace functions like map!, reject!, reverse!, rotate!. Also we have binary operations in short form like +=, -=.
Do we have something for mathematical round? We need to use a = a.round, and it's a bit weird for me to repeat the variable name. Do you know how to shorten it?
OK, smart guys have already explained, why there is no syntactic sugar for Float#round. Just out of curiosity I’m gonna show, how you might implement this sugar yourself [partially]. Since Float class has no ~# method defined, and you do rounding quite often, you might monkeypatch Float class:
class Float
def ~#
self.round # self is redundant, left just for clarity
end
end
or, in this simple case, just (credits to #sawa):
alias_method :~#, :round
and now:
~5.2
#⇒ 5
a = 2.45 && ~a
#⇒ 2
Since Numerics are immutable, it’s still impossible to modify it inplace, but the above might save you four keyboard hits per rounding.
As for destructive methods, it is impossible since numerals are immutable, and it would not make sense. Would you want a numeral 5.2 that behaves as 5?
As for syntax sugar, it would be a mess if every single method had one. So there isn't. And since syntax sugar is defined in the core level, you cannot do anything in an ordinary Ruby script to create a new one.
Ruby's numeric types are immutable: they are value objects. Therefore you won't find any methods that mutate a number in place.
Because the numeric types are immutable, certain optimizations are possible that would not be possible with mutable numbers. In c-ruby, for example, a reference, which may point to any kind of object, is normally a pointer to an object. But if the reference is to a Fixnum, then the reference contains the integer itself, rather than pointing to an instance of Fixnum. Ruby does a number of magic tricks to hide this optimization, making it appear that an integer really is an instance of a Fixnum.
To make numbers mutable would make this optimization impossible, so I don't expect that Ruby will ever have mutable numeric types.

Ruby as a "pure" object oriented language --- inconsistency with Ruby puts?

I've often read that Ruby is a pure object oriented language since commands are typically given as messages passed to the object.
For example:
In Ruby one writes: "A".ord to get the ascii code for A and 0x41.chr to emit the character given its ascii code.
This is in contrast to Python's: ord("A") and chr(0x41)
So far so good --- Ruby's syntax is message passing.
But the apparent inconsistency appears when considering the string output command:
Now one has: puts str or puts(str) instead of str.puts
Given the pure object orientation expectation for Ruby's syntax, I would have expected the output command to be a message passed to the string object, i.e. calling a method from the string class, hence str.puts
Any explanations? Am I missing something?
Thanks
I would have expected the output command to be a message passed to the string object, i.e. calling a method from the string class, hence str.puts
This is incorrect expectation, let's start with that. Why would you tell a string to puts itself? What would it print itself to? It knows nothing (and should know nothing) of files, I/O streams, sockets and other places you can print things to.
When you say puts str, it's actually seen as self.puts str (implicit receiver). That is, the message is sent to the current object.
Now, all objects include Kernel module. Therefore, all objects have Kernel#puts in their lists of methods. Any object can puts (including current object, self).
As the doc says,
puts str
is translated to
$stdout.puts str
That is, by default, the implementation is delegated to standard output (print to console). If you want to print to a file or a socket, you have to invoke puts on an instance of file or socket classes. This is totally OO.
Ruby isn't entirely OO (for example, methods are not objects), but in this case, it is. puts is Kernel#puts, which is shorthand for $stdout.puts. That is, you're calling the puts method of the $stdout stream and passing a string as the parameter to be output to the stream. So, when you call
puts "foo"
You're really calling:
$stdout.puts("foo")
Which is entirely consistent with OO.
puts is a method on an output streams e.g.
$stdout.puts("this", "is", "a", "test")
Printing something to somewhere at least involves two things: what is written and where it is written to. Depending on what you focus on, there can be different implementations, even in OOP. Besides that, Ruby has a way to make a method look more like a function (i.e., not being particularly tied to a receiver as in OOP) for methods that are used all over the place. So there are at least three logical options that could be thought of for such methods like printing.
An OOP method defined on the object to be printed
An OOP method defined on the object where it should be printed
A function-style method
For the second option, IO#write is one example; The receiver is the destination of writing.
The puts without an explicit receiver is actually Kernel#puts, and takes neither of the two as the arguments; it is an example of the third option; you are correct to point out that this is not so OOP, but Matz especially provided the Kernel module to be able to do things like this: a function-style method.
The first option is what you are expecting; it is nothing wrong. It happens that there is no well known method of this type, but it was proposed in the Ruby core by one of the developers, but unfortunately, it did not make it. Actually, I felt the same thing as you, and have something similar in my personal library called Object#intercept. A simplified version is this:
class Object
def intercept
tap{|x| p x}
end
end
:foo.intercept # => :foo
You can replace p with puts if you want.

How to get rid of TypeError

I have my class
class A
def to_s
'string'
end
def inspect
'string'
end
end
and trying to do something like this
a = A.new
'somestring' + a
Error is "in `+': can't convert A into String (TypeError)"
Is there any way to fix it?
P.S. using 'somestring' + a.to_s isn't an option.
Answer found. to_str method needed.
#to_s is for representing an object as a String. In your case, you need to convert the object to a String, because String#+ only deals with Strings. In Ruby, type conversions are done with the three letter form of the to_X methods, i.e. to_int, to_ary, to_hash (someone can't count to three ...), to_str and so on.
So, you need to implement to_str, in order to make that work.
However, you should only implement to_str, if your object actually is a string! If it's something else, you must not implement to_str and rather have the clients explicitly represent it as a string using to_s.
Basically, implementing to_str in Ruby is like inheriting from String in Java: it means that A IS-A String.
As an example, look at which classes in the Ruby core library actually implement the type conversion methods:
the only class that implements to_ary, is Array itself,
the only class that implements to_hash, is Hash itself and
the only class that implements to_str, is String itself.
This should show you that implementing to_str is a very serious matter that should not be undertaken lightly.
The only conversion method that does not have only a trivial no-op implementation, is to_int, which is also implemented by Float and Numeric. And actually, I think that is a mistake, since there is an infinite number of Floats which aren't Integers. And Numeric is a superclass of Integer, so saying that every Numeric IS-A Integer is even more strange.

Ruby and :symbols

I have just started using Ruby and I am reading "Programming Ruby 1.9 - The Pragmatic Programmer's Guide". I came across something called symbols, but as a PHP developer I don't understand what they do and what they are good for.
Can anyone help me out with this?
It's useful to think of symbols in terms of "the thing called." In other words, :banana is referring to "the thing called banana." They're used extensively in Ruby, mostly as Hash (associative array) keys.
They really are similar to strings, but behind the scenes, very different. One key difference is that only one of a particular symbol exists in memory. So if you refer to :banana 10 times in your code, only one instance of :banana is created and they all refer to that one. This also implies they're immutable.
Symbols are similar to string literals in the sense that share the same memory space, but it is important to remark they are not string equivalents.
In Ruby, when you type "this" and "this" you're using two different memory locations; by using symbols you'll use only one name during the program execution. So if you type :this in several places in your program, you'll be using only one.
From Symbol doc:
Symbol objects represent names and some strings inside the Ruby interpreter. They are generated using the :name and :"string" literals syntax, and by the various to_sym methods. The same Symbol object will be created for a given name or string for the duration of a program‘s execution, regardless of the context or meaning of that name. Thus if Fred is a constant in one context, a method in another, and a class in a third, the Symbol :Fred will be the same object in all three contexts.
So, you basically use it where you want to treat a string as a constant.
For instance, it is very common to use it with the attr_accessor method, to define getter/setter for an attribute.
class Person
attr_accessor :name
end
p = Person.new
p.name= "Oscar"
But this would do the same:
class DontDoThis
attr_accessor( "name" )
end
ddt = DontDoThis.new
ddt.name= "Dont do it"
Think of a Symbol as a either:
A method name that you plan to use later
A constant / enumeration that you want to store and compare against
For example:
s = "FooBar"
length = s.send(:length)
>>> 6
#AboutRuby has a good answer, using the terms "the thing called".
:banana is referring to "the thing
called banana."
He notes that you can refer to :banana many times in the code and its the same object-- even in different scopes or off in some weird library. :banana is the thing called banana, whatever that might mean when you use it.
They are used as
keys to arrays, so you look up :banana you only have one entry. In most languages if these are Strings you run the risk of having multiple Strings in memory with the text "banana" and not having the code detect they are the same
method/proc names. Most people are familiar with how C distinguishes a method from its call with parentheses: my_method vs. my_method(). In Ruby, since parentheses are optional, these both indicate a call to that method. The symbol, however, is convenient to use as a standin for methods (even though there really is no relationship between a symbol and a method).
enums (and other constants). Since they don't change they exhibit many of the properties of these features from other languages.

What does :this means in Ruby on Rails?

I'm new to the Ruby and Ruby on Rails world. I've read some guides, but i've some trouble with the following syntax.
I think that the usage of :condition syntax is used in Ruby to define a class attribute with some kind of accessor, like:
class Sample
attr_accessor :condition
end
that implicitly declares the getter and setter for the "condition" property.
While i was looking at some Rails sample code, i found the following examples that i don't fully understand.
For example:
#post = Post.find(params[:id])
Why it's accessing the id attribute with this syntax, instead of:
#post = Post.find(params[id])
Or, for example:
#posts = Post.find(:all)
Is :all a constant here? If not, what does this code really means? If yes, why the following is not used:
#posts = Post.find(ALL)
Thanks
A colon before text indicates a symbol in Ruby. A symbol is kind of like a constant, but it's almost as though a symbol receives a unique value (that you don't care about) as its constant value.
When used as a hash index, symbols are almost (but not exactly) the same as using strings.
Also, you can read "all" from :all by calling to_s on the symbol. If you had a constant variable ALL, there would be no way to determine that it meant "all" other than looking up its value. This is also why you can use symbols as arguments to meta-methods like attr_accessor, attr_reader, and the like.
You might want to read up on Ruby symbols.
:all is a symbol. Symbols are Ruby's version of interned strings. You can think of it like this: There is an invisible global table called symbols which has String keys and Fixnum values. Any string can be converted into a symbol by calling .to_sym, which looks for the string in the table. If the string is already in the table, it returns the the Fixnum, otherwise, it enters it into the table and returns the next Fixnum. Because of this, symbols are treated at run-time like Fixnums: comparison time is constant (in C parlance, comparisons of symbols can be done with == instead of strcmp)
You can verify this by looking at the object_id of objects; when two thing's object_ids are the same, they're both pointing at the same object.
You can see that you can convert two strings to symbols, and they'll both have the same object id:
"all".to_sym.object_id == "all".to_sym.object_id #=> true
"all".to_sym.object_id == :all.object_id #=> true
But the converse is not true: (each call to Symbol#to_s will produce a brand new string)
:all.to_s.object_id == :all.to_s.object_id #=> false
Don't look at symbols as a way of saving memory. Look at them as indicating that the string ought to be immutable. 13 Ways of Looking at a Ruby Symbol gives a variety of ways of looking at a symbol.
To use a metaphor: symbols are for multiple-choice tests, strings are for essay questions.
This has nothing to do with Rails, it's just Ruby's Symbols. :all is a symbol which is effectively just a basic string.

Resources