Understanding Ruby symbol as method call [duplicate] - ruby

This question already has answers here:
How to understand symbols in Ruby
(11 answers)
Closed 10 years ago.
class A
def test
"Test from instance"
end
class << self
def test
"Test from class"
end
end
end
p A.send(:test) # "Test from class"
p A.new.method(:test).call # "Test from instance"
Here symbol works as expected, but here:
s="test"
s1=:s
p s1 # :s
why :s is printed here?? I dont understand the reason behind it.
Can anyone please explain for me ?

Symbols are sort of lightweight strings (though they are not strings). The send() and method() methods can take strings or symbols; one is converted to the other in the inner workings (not sure which) and then ruby executes the method with the matching name. Hence A.send(:text) is equivalent to A.text(). If you had a variable named methodName = :text, you could do A.send(methodName) but not A.methodName().
Symbols are not variables, so you can't assign a value to a symbol. In your example, the symbol :s is unrelated to the variable s (despite the fact that they have the same "name", preceding it with a colon makes it a symbol instead of a variable). You're assigning a string value to the variable s but telling it to print the symbol :s, which it does.

Symbols are just a special kind of stringlike value that's more efficient for the runtime to deal with than a regular string. That's it. They aren't methods or variables or anything like that.
When you do A.send(:test), all you are doing is saying "hey, A, call the method named 'test'". You aren't sending the method itself, just the name; it's the logic inside send that is responsible for looking up the actual method to call.
The same thing goes when you ask for method with A.new.method(:test). All you are passing to method is the name "test", not the method defined with that name. It's up to method to use the name and find the actual method so it can return it, and it's that return value - a Method object - that you are doing call on. You can't do call on a Symbol like :test, because it's just a name.

From https://stackoverflow.com/a/1255362/509710:
p foo does puts foo.inspect, i.e. it prints the value of inspect instead of to_s, which is more suitable for debugging (because you can e.g. tell the difference between 1, "1" and "2\b1", which you can't when printing without inspect).

s="test"
s1=:s
p :s.object_id #137448
p s.object_id #77489950
p s1.object_id #137448
I have understand it now. I was assigning a symbol but expecting a string.

You set the value of s1 to be :s, so why would you expect it to return anything different?
If you look at the ruby API for the Object class, you will see both Object#send and Object#method take a symbol as a parameter, so the top example is also totally expected.

Related

Does 'upcase!' not mutate a variable in Ruby?

I'm just trying to make sure I understand what's happening here. I get that += is reassignment so maybe that's why str isn't modified but why doesn't upcase! modify str here either?
def change_me(str)
str += "?"
str.upcase!
end
question = "whats your name"
change_me(question)
puts question
'whats your name'
=> nil
Does 'upcase!' not mutate a variable in Ruby?
It is impossible for a method to mutate a variable. Ruby is an object-oriented language, so methods can mutate objects (more precisely, a method can mutate its receiver), but variables aren't objects in Ruby. (Like most other languages as well, there is almost no language where variables are objects.)
The only way to mutate a variable is through assignment. Note that we generally don't talk about "mutating" variables, we talk about "re-binding" or "re-assigning".
I'm just trying to make sure I understand what's happening here. I get that += is reassignment so maybe that's why str isn't modified but why doesn't upcase! modify str here either?
Again, you are confusing variables and objects. upcase! modifies the object that is referenced by str, but it does not modify str.
It sounds like you expect Ruby to be a pass-by-reference language, but it is not. Ruby is purely pass-by-value, always, no exceptions. More precisely, the value being passed is an unmodifiable, unforgeable pointer to an object.
Here's what happens, following the flow of execution:
question = "whats your name"
The string literal "whats your name" is evaluated, resulting in a String object with the content whats your name.
The local variable question is initialized with an immutable, unforgeable pointer to the string object created in step #1.
change_me(question)
The local variable question is dereferenced, resulting in the immutable, unforgeable pointer to the string object created in step #1.
A copy of that pointer is made.
The copy from step #4 is placed into the argument list of the call to change_me
str += "?"
Inside of the change_me method body, the parameter binding str is bound to the copied immutable unforgeable pointer from step #4 and #5.
This line desugars into str = str + "?", so what happens is:
str is dereferenced, resulting in the copied immutable, unforgeable pointer from step #4, #5, and #6.
We follow the pointer and send the message + to the object with an immutable, unforgeable pointer to the string object created by evaluating the string literal "?" as an argument.
String#+ returns a new string (or, more precisely, an immutable, unforgeable pointer to a new string).
str is re-bound to the new immutable, unforgeable pointer returned by the call to str+("?").
str.upcase!
str is dereferenced, resulting in the new immutable, unforgeable pointer from step #7c #7d.
We follow the pointer and send the message upcase! to the object.
String#upcase! will mutate the receiver object (in this case, the newly created string from step #7c) to make all letters uppercase.
String#upcase! will return either an immutable, unforgeable pointer to the receiver object itself (i.e. the pointer that was used to call the method) if it did any changes to the receiver, or it will return an immutable, unforgeable pointer to the object nil if the string was already uppercase or didn't contain any letters.
Back to change_me(question)
This return value, however, is just ignored, it is thrown away, it is not printed, not assigned to a variable, not passed as an argument to a different method, not stored in a data structure.
puts question
Okay, I will save the details now that the variable is dereferenced, etc.
The crucial part is: the variable question was never touched, it was never re-assigned, so it still contains the exact same thing it contained the whole time: the immutable, unforgeable pointer to the string object from steps #1 and #2.
We assigned this object to the variable, and we:
never re-assigned the variable, so the variable still points to the same object
never asked the object to mutate itself, so the contents of the object are still the same
Therefore, the object is still unchanged, and the variable still points to the same object, and thus we get the result that nothing has changed.
We changed the binding for the str parameter binding inside of the change_me method, but that binding is local to the method. (Parameter bindings are effectively equivalent to local variables.) Therefore, it ceased to exist the moment the method returned.
And we changed the newly created string object, but since we never obtained a pointer to this object, there is no way that we can reach it. One pointer was stored in str, but that is gone. Another pointer was returned from change_me, but we threw that away, so that is gone, too. Since there is no reference this string object, the object is unreachable.
In fact, the change_me method doesn't do anything at all that can be observed from the outside. It creates a string object, then mutates it, but no reference to this object ever leaves the method. Therefore, it is as good as if the mutation never happened, and the string object never existed in the first place.
In fact, a sufficiently clever compiler would be able to optimize your entire code to this:
puts "whats your name"
when you are doing str += "?" you are creating a new string, so str points to a different string than the one you are passing as an argument.
What you are doing is essentially this:
def change_me(str)
new_str = str + "?"
new_str.upcase!
end
That is why your previous string is not being changed. If you want the function to have side effects, you should do:
def change_me(str)
str << "?"
str.upcase!
end
However, I think modifying strings in place is a bit questionable. I think it would be safer to return a new string and overwrite your reference if needed.
Let's see if I can boil all this down a bit for you. First, have a careful look at Mario's "what you are doing is essentially this" code example. Understand that you are calling your #upcase! method on an entirely new object, since you reassigned str to a new object when you tried to tack a ? onto it.
Now, have a look at this:
def change_me(str)
str.upcase!
42
end
x = 'hello'
puts x # => hello
change_me(x)
puts x # => HELLO
As you can see, this code returns 42. Now, as Douglas Adams has told us, 42 is the meaning of life. But if so, the meaning of life is entirely irrelevant here, because as Jörg has been trying to explain to you, you don't do anything with the return value of your method call.
You will also see that your str object does get mutated here. That's because in this case, you haven't reassigned the str variable to a different object inside your method, as your code does. (Again, look carefully at Mario's first example.)
Now, if, in your method, you want to tack something onto the end of the object that you send into your method, you need to use << instead of +. Look at Mario's second code example, and give that a try.
To dig down into this and learn it thoroughly, the #object_id method is very useful. Try running this code:
def change_me(str)
p str.object_id
str += "?"
p str.object_id
str.upcase!
p str.object_id
end
def change_me_2(str)
p str.object_id
str << "?"
p str.object_id
str.upcase!
p str.object_id
end
If you spend some time evaluating object ids, you'll sort this out for yourself pretty quickly.
Finally, I second Mario's point of view that modifying strings in place is a bit questionable in practice. Unless there's some reason that you can't do it this way, I would do this:
def change_me(str)
str.upcase + '?'
end
And then:
question = "what's your name"
question = change_me(question)
Or simply:
question = change_me("what's your name")
Finally, here's a little quiz. Take your code and change the way you call it so:
def change_me(str)
str += "?"
str.upcase!
end
question = "whats your name"
puts change_me(question)
Why does this do what you intended? Now, change str.upcase! to str.upcase, and you will see that it also does what you intended. Why doesn't it make any difference whether you use the ! or not?

What does 'def function=(param)' mean in ruby? [duplicate]

This question already has answers here:
What does the equal ('=') symbol do when put after the method name in a method definition?
(4 answers)
Closed 8 years ago.
I am sorry if this is a noob question, but I can't seem to find the answer neither in stackoverflow, nor anywhere else. The code is:
def full_name=(new_full_name)
# stuff
end
When I define it in the console, and try to evoke it:
full_name # nil
full_name # [hangs, needs ^C]
full_name= arg # works like normal function
full_name # works again with no arguments, as if I passed the previous argument
So, what is going on?
Thanks.
A method ending with an = defines a setter. When defining a setter like that, ruby will always return the method argument regardless of the body definition when you call the setter.
You can then use object.full_name= "Daniel" which returns => "Daniel"
Some other common appendices with your example:
The question mark, e.g.
has_full_name?
is expected to return a boolean value if the object has a full_name
The exclamation mark, e.g.
revert_full_name!
is expected to revert the full_name variable of the object
revert_full_name
in contrast is expected to return the reverted full_name variable but not to change it in the object

ruby- method_missing return no method error

I am trying to use method_missing to convert dollar to different currencies.
class Numeric
##currency={"euro"=>2, "yen"=>6}
def method_missing(method_id,*args,&block)
method_id.to_s.gsub!(/s$/,'') #get rid of s in order to handle "euros"
##currency.has_key?(method_id) ? self*##currency[method_id] : super
end
end
> 3.euro #expect to return 6
NoMethodError: undefined method 'euro' for 3:Fixnum
from (pry):24:in 'method_missing'
> 3.euros #expect to return 6
NoMethodError: undefined method 'euros' for 3:Fixnum
from (pry):24:in 'method_missing'
I guess 3.euro isn't working because :euro.to_s.gsub!(/s$/,'') returns nil. I am not sure why it returns no method error.
Thanks for any help.
When method_missing will be called, then euro will be assigned to the method_id as a symbol. But your ##currency hash holds all keys as a strings, you need to convert them as symbols.
method_id.to_s.gsub!(/s$/,'') no way will change the actual object method_id. Because method_id.to_s will give you a new string object, and #gsub! will work on this only. No way you are changing the method_id, it remains as it is. Better to use non-bang one, because it will give you the actual object if no change made, or changed object, if change is made, then you need to assign this return value to a new variable.
With your ##currency, ##currency.has_key?(method_id) evaluated as false and super class method_missing has been called. Why? As method_id didn't convert into strings, which you expected may happened.
But, if you want to respond for both like euro or euros, then
class Numeric
##currency={"euro" => 2, "yen" => 6}
def method_missing(method_id,*args,&block)
#get rid of s in order to handle "euros"
new_method_id = method_id.to_s.gsub(/s$/,'')
##currency.has_key?(new_method_id) ? self*##currency[new_method_id] : super
end
end
3.euros # => 6
3.euro # => 6
gsub! modifies a string in place and returns nil. That won't work very well with the string you're using, which is a temporary created by to_s and never stored in a variable. Also, you are not even storing the result anywhere anyway. Why should gsub! on a string be expected to modify symbols, which are immutable anyway?
Try using gsub instead, which returns the modified string and leaves the caller alone. You'll also need to store the return value.
real_method_name = method_id.to_s.gsub(/s$/, "")
Also: The reason 3.euro didn't work is because your hash uses strings as keys but method_id is passed to the method as a symbol. If you didn't have to do string manipulations (to remove s suffixes, in this case), I would have suggested to just use symbols as your hash keys. As it is, though, you need to do string operations anyway, so my answer uses strings.

What's the cleanest way to define a constant string that involves a variable in Ruby?

For some context, a lot of my code has the same lines of text throughout it (we are using Calabash to do iOS automation, if that gives you an idea).
For example: "all label marked:'#{name}'" is used 8 times in a particular class.
I would prefer to be able to have a constant that uses that text, but if I throw it at the top of the class, of course the variable "name" has not been set yet. Without defining a method that takes a parameter and returns a string, is there a way to do something essentially like this that can exist at the top of the class, but not be evaluated until it's used?:
class ClassName
extend Calabash::Cucumber::Operations
#NAME_CONSTANT = "all label marked:'#{name}'"
def self.method_name(name)
query("#{#NAME_CONSTANT} sibling label marked:'anotherLabel' isHidden:0")
end
end
If you use the syntax I mentioned, you get this error: undefined local variable or method `name' for ClassName
You could use String#% to insert the string later.
class ClassName
#NAME_CONSTANT = "all label marked:'%{name}'"
def self.method_name(insert_name)
query("#{#NAME_CONSTANT} sibling label marked:'anotherLabel' isHidden:0" % {name: insert_name})
end
def self.query(string)
puts string
end
end
ClassName.method_name('test')
#=> "all label marked:'test' sibling label marked:'anotherLabel' isHidden:0"
I agree with #Sergio. Don't define a constant string that includes a variable. Just use a method. Including a variable in a constant seems like a bad idea. Constants shouldn't be dynamic, by definition.
If you really want to include a variable in a constant string, you can assign a lambda to a contstant, like so:
class ClassName
extend Calabash::Cucumber::Operations
NAME_CONSTANT = ->(name) { "all label marked:'#{name}'" }
def self.method_name(name)
query("#{NAME_CONSTANT.call(name)} sibling label marked:'anotherLabel' isHidden:0")
end
end
I removed the # before the constant, since including it creates a class-level instance variable, not a constant.
I really wouldn't use the code sample I posted, though. Just use a method. Avdi Grimm has a good post called "Do we need constants?" where he describes some of the benefits of using methods instead of constants.
The fundamental issue you're facing is that string interpolation occurs at the time the literal is interpreted and the scope of any referenced variables is determined by the location of the string in the code.
If you put the interpolated string in a method, then it won't have access to the local definition of any variables used in the string. You'd have to pass in the value of any variables used, as in:
def name_constant(name)
"all label marked:'#{name}'"
end
Alternatively, you'd need to declare the "constant" as an uninterpreted string as follows:
#name_constant = '"all label marked:''#{name}''"'
and then interpret it when you reference it, as follows:
eval(#name_constant)
BTW, I've ignored the issue of this not really being a "constant" and using instance variables vs. class variables.

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

Resources