I am new to ruby and doing some basic string manipulation to get the hang of it and I noticed that foo = 'foo'.downcase! is not the same as downcasing after assigning a vale to foo like this:
foo = 'foo'.downcase!
if foo.include? 'foo'
print 'bar'
end
This will result in an NoMethodError: undefined method `include?' for nil:NilClass (NoMethodError)
While downcasing foo after the value has been assigned does not:
foo = 'foo'
foo.downcase!
if foo.include? 'foo'
print 'bar'
end
I am sorry if this is a trivial question but a few minutes of googling didn't return anything fruitful.
This is because String#downcase! is a destructive method (hence the !) meaning it operates on the object in place. Since it changes the object itself, its return value isn't need. The Ruby designers decided that a good use of the return value would be to indicate if any changes were made.
Downcases the contents of str, returning nil if no changes were made.
Your string is already lowercase, so downcase! returns nil which you then replace the variable with.
String#downase! returns nil if the change can't be done on the receiver string object. That's the reason foo = 'foo'.downcase! causes the local variable foo to be assginbed as nil. Because foo string is already dowcased.
That's why if foo.include? 'foo' became if nil.include? 'foo'. As we know Nilclass#include? doesn't exist, so you got the error.
But String#downcase returns the receiver itself, if no change can be done, or changed new string object if change can be done.
In the below example :
The downcase! method returns nil (reason as I mentioned in the first paragraph), but as you didn't do local variable assignment, while you are calling the method (which is the case of your first example), thus foo still holding the original object 'foo'.
foo = 'foo'
foo.downcase!
That's the reason if foo.include? 'foo' became if "foo".include? "foo". But you are calling #include? on 'foo' an instance of String and String#include? method exist, thus no error you got.
You're seeing this behaviour because of this bit:
'foo'.downcase!
The downcase! method is an in-place substitution, meaning that it affects only the object that it was called on. It will only return a result when the string is changed. For example...
foo = 'foo'.downcase! # foo == NilClass, because 'foo' is already lower-case.
bar = 'BAR'.downcase! # bar == String, because 'BAR' was changed to 'bar'
If you need to guarantee that foo is a string after doing a downcase, you can use the non-intrusive downcase method (note the missing exclamation point).
foo = 'foo'.downcase
if foo.include? 'foo'
print 'bar'
end
The above code should always work. As a rule, it's generally a good idea to work with strings as immutable, rather than using the intrusive methods, as it can simplify code quite a lot, and avoid bugs like this.
Related
I recently came across some code using a method call consisting of the format object.(arg1, arg2) without seeing a good explanation of how it works. See this sample code:
class TestServiceObject
def call
'method'
end
end
TestServiceObject.new.()
# => 'method'
What's the term for this kind of shorthand?
The dot-parentheses notation is a shorthand way for passing arguments to the implicit call method on a Ruby object:
foo = lambda {|bar| puts bar}
foo.call('baz')
#=> baz
foo.('baz')
foo.call('baz') === foo.('baz')
#=> true
Also note that the following notations are also valid (and equivalent) invocations of the call method:
foo['baz']
#=> baz
foo::('baz')
#=> baz
In your example, you're explicitly overriding the call method on the TestServiceObject class such that it returns the string 'method' when called. Accordingly, you can explicitly override the the call method to accept arguments:
class TestServiceObject
def call(foo=nil)
foo || 'method'
end
end
TestServiceObject.new.()
#=> method
TestServicesObject.new.('bar')
#=> bar
UPDATE:
As commenter #LoganSerman duly notes, the shorthand operator appears to work on anything that responds to call, which is validated in part by the following example:
m = 12.method("+")
m.call(3)
#=> 15
m.(3)
#=> 15
UPDATE 2:
As commenter #Stefan also points out from the documentation on Proc#call:
prc.() invokes prc.call() with the parameters given. It’s a syntax sugar to hide “call”.
foo.(bar, baz)
is interpreted as
foo.call(bar, baz)
just like
foo + bar
is interpreted as
foo.+(bar)
or
foo[bar, baz] = quux
is interpreted as
foo.[]=(bar, baz, quux)
The intention is to make calling function-like objects look similar to calling methods:
foo.(bar, baz) # function
foo(bar, baz) # method
Despite claims in other answers to this question, it has nothing to do with an "implicit call method" (Ruby doesn't even have implicit methods, only Scala does) or the indexing operator.
The indexing operator is translated into a different method call ([]) and not into a call to call:
o = Object.new
def o.call(*args); "`call` called with #{args.join(', ')}" end
o.(42)
# => "`call` called with 42"
o[42]
# NoMethodError: undefined method `[]' for #<Object:0xdeadbeefc0ffee>
def o.[](*args); "`[]` called with #{args.join(', ')}" end
o[42]
# => "`[]` called with 42"
obj.(args) is just a feature provided through the parser. Not technically an alias, but it simply has the same effect as invoking obj.call(args) on an object that defines the call method.
I was playing with method definition and calling to them in the main of IRB.
def show
p "hi"
end
#=> nil
show
#"hi"
#=> "hi"
self.show
#"hi"
#=> "hi"
The above are good and understood.
Now let's try something different:
def Foo
p "hi"
end
#=> nil
Foo
#NameError: uninitialized constant Foo
#from (irb):4
#from C:/Ruby193/bin/irb:12:in `<main>'
While the call to Foo has thrown an error as above,how does the below remove that?
self.Foo
#"hi"
#=> "hi"
In Ruby, you can call methods without a receiver and without an argument list. However, this means that there is an ambiguity: does foo mean "call method foo on the implicit receiver self without arguments, i.e. equivalent to self.foo()" or does it mean "dereference the variable foo"? Ruby can't know which you mean, so there are some simple rules.
For a local variable, the rule is that foo is always a method call, unless foo is statically known at parse time to be a local variable. So, when is it statically known to be a variable? When there was an assignment to that variable which was parsed (but not necessarily executed!) before the use.
Example:
foo # method call
if false
foo = 42 # will never be executed, but *will* be parsed
end
foo # variable dereference, since the `foo` assignment was parsed
For constant variables, the rule is even simpler: Foo is always interpreted as a constant dereference. Period.
So, how do you call a method with such a name? Easy: like I said, the ambiguity arises only for method calls with no argument list and no explicit receiver. So, if we add either one or both of those, Ruby will know that we are trying to call a method and not dereference a variable:
foo()
self.foo
self.foo()
Foo()
self.Foo
self.Foo()
Of course, in the example you gave above, only the first one will work. When you define a method at the top-level, it is added as a private method to Object, and private methods can only be called without an explicit receiver, even if that receiver is self. So, self.Foo won't work, because Foo is private. (Except in IRb, where, for convenience reasons, top-level methods are public.)
In Ruby, inside a class's instance method, we use a getter by
foo
and we use a setter by
self.foo = something
One doesn't need to have a self. and the other does, is there a way to make them look more similar, and not using something like self.foo as the getter, as it also looks verbose.
(update: note that getter and setter may simply get or set an instance variable, but they might also do a lot of work, such as going into the DB and check the existence of a record and if not, create it, etc)
Since local scope takes precedence, when you say foo = something, a local variable foo will be created and assigned the contents of something.
The reason you can write foo in order to use the getter is because Ruby will move up in scope when it can't find a variable with that name and it will eventually find the method.
If there is a local variable with the same name as the getter method, Ruby will use its value instead:
class Foo
attr_accessor :foo
def initialize
#foo = :one
end
def f
foo = :two
foo
end
end
Foo.new.f
# => :two
In order to make it clear that you want to access the setter, you must write self.foo = something. That will tell Ruby you want to execute the foo= method on the self object with something as parameter.
If you are willing to break the conventions, you can write your setters using jQuery style, using the same method for getter and setter, depending of whether it has arguments or not:
def foo *args
return #foo if args.empty?
#foo = args.first
end
# => nil
foo
# => nil
foo(:bar) # foo = :bar
# => :bar
foo
# => :bar
As far as I know, there isn't a way around this in Ruby. I'm pretty confident this is simply how Ruby evaluates expressions.
When given a value, Ruby will first check if there is a local variable within the context which matches the one being called. If there is (perhaps 'foo' in your case), that will be the value used. If there is no such value, then Ruby will try to look up the value as a method call (falling through to "self" as the caller). If no such method exists in the look up path, an error will be raised.
The need to use "self" in the setter is to avoid Ruby setting the value as a local variable, while the lack of the use of "self" only works in the getter instance when there is no local variable of the same name being used in that context. It is probably better and clearer, albeit slightly more verbose, to be explicit with your use of self as to avoid confusion about where values are coming from.
Start with the following scenario:
class Foo
def bar(baz={})
p baz
end
end
foo = Foo.new
p meth = foo.method(:bar) # => #<Method: Foo#bar>
p meth.parameters # => [[:opt, :baz]]
So I can figure out that the method bar is optional, but how do I find the default value ({}) for the method?
Just do this:
foo.bar
Since you are not passing in a value for baz, it will print out the default value.
Although, I'm betting you want something that would apply to any method. The only consistent way I know of, is to look at the source code.
The Answer: Somebody wrote a script that does it here.
However, looking over the script to try and understand just how it pulls out the default values makes my head hurt.
def foo
"foo"
end
alias foo2 foo
puts "foo2: " + foo2.object_id.to_s
puts "foo: " + foo.object_id.to_s
In the above example, I expected to see the same object_id output for each method call since they reference the same method. Why do I see different object_id's? When you alias a method in Ruby doesn't the alias refer to the original object, not a copy?
Starting over with a new answer in response to your various comments.
In the example code, you are calling the method, not referencing it. You want to use
method(:foo)
to actually get the method itself and not the result of calling it.
Also, object_id is not the right way to test if two methods are the same, because method(:foo) returns a new Method object each time. For an analogy that might make this clearer, if you opened the same file twice you would have two distinct file handles even though the underlying file was the same. Instead, I think you want:
method(:foo) == method(:foo2)
which, as you will see if you try it, returns true.
You're calling object_id on the object returned by foo, which is a string created in the method and thus will be different every time. You'd see the same results if you just called foo twice. It returns a new string every time. If you want a constant string, return the symbol :foo instead.
Past that, even though they share the same implementation right now, they are different methods. If you override foo to return the string "bar", foo2 will still keep returning "foo".
Try:
FOO = "foo"
def foo
FOO
end
alias foo2 foo
puts "foo2: " + foo2.object_id.to_s
puts "foo: " + foo.object_id.to_s
To get the effect you are wanting. "foo" is an expression and it gets evaluated each time the function is called. To see why this is, consider that you could just as well have written:
def foo
"It is now #{Time.now}"
end
alias foo2 foo
puts "foo2: " + foo2.object_id.to_s
puts "foo: " + foo.object_id.to_s