I have a variable lets call it #foo. I expect it to be a string so I call #foo.downcase. Sometimes it's nil and i'll get this error:
NoMethodError: undefined method `downcase' for nil:NilClass
from (irb):4
from /Users/schneems/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>
What I want to do is write some code to tell me that nil is actually #foo
NoMethodError: undefined method `downcase' on variable #foo, variable is a nil:NilClass
from (irb):4
from /Users/schneems/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>
To do something like this, I would need to get the name of the variable programatically.
Question: Is it possible to get the name of a variable programmatically ruby?
I'm looking for something that produces an output like this:
#foo.magical_variable_method #=> '#foo'
bar.magical_variable_method #=> 'bar'
AweSome.magical_variable_method #=> 'AweSome'
$wat.magical_variable_method #=> '$wat'
I don't want the value of the variable, nor do i care if it is nil. I want the human readable name of the variable. Is it possible to get the name of a variable programmatically ruby?
What you want is kinda mess.
Here's proper way for this task:
#foo && #foo.downcase
Or you could use rails library:
require 'active_support'
#foo.try(:downcase)
There isn't currently any way to do this.
Variables are just references, not objects themselves. If you use dot notation to call a method, like downcase, that method is operating on an object, not a variable. And the object in your example is the singleton nil; if nil in one place were to have a property assigned to it naming the variable it was assigned to, that same property would apply to all nils.
Even more generally, one object may have many variables/references pointing to it, so there would be no good way of determining which variable name should be saved in the object. However, there is some sort of special treatment in Ruby for assigning a class to a constant; in that case, the class object does remember the name of the first constant it's assigned to, e.g.:
$ irb
1.9.3p194 :001 > Foo = Class.new do
1.9.3p194 :002 > attr_accessor :foo
1.9.3p194 :003?> end
=> Foo
1.9.3p194 :004 > Bar = Foo
=> Foo
1.9.3p194 :005 > Foo.name
=> "Foo"
1.9.3p194 :006 > Bar.name
=> "Foo"
You can get the instance variable from the symbol with instance_variable_get(:#foo)
which would just give you the value.
But you could do this:
puts "I'm gonna call #foo now, people"
some_obj.instance_variable_get(:#foo)
You could also do a method_missing hook for the attrs you wanted to watch. Don't create accessor methods for #foo and catch the call in method_missing and forward it to some generic implementation such as instance_variable_get
Untested attempt:
def method_missing(method_name, *args)
super(method_name, *args) unless watched_attributes.include?(method_name)
attr = ":##{method_name.to_str}"
log.debug "Calling watched attribute #{attr}"
val = instance_variable_get(method_name)
log.debug "#{attr} was nil omg!" unless val
val
end
Edit: Probably the best way (see mu's comment) is to just make sure you're working with a string:
#foo.to_s.downcase
You don't need to get the "name" to do this. You can just use a conditional:
# Downcased if #foo's a string, empty string otherwise:
(String === #foo)? #foo.downcase : ''
Or, if you're going to be doing this in lots of places, and don't want to have to wrap each one in a conditional, monkey-patch NilClass and add a dummy downcase method:
class NilClass
def downcase
return ''
end
end
Hope that helps!
Related
IRB appears to have strange behaviour when defining a method called !.
To reproduce this, enter the following into IRB:
def !
puts "foo"
end
Upon creating the method, IRB infinitely prints foo:
irb(main):001:0> def !
irb(main):002:1> puts "foo"
irb(main):003:1> end
foo
foo
foo
...
As far as I know, you can't directly call a method named ! from Ruby syntax; you have to use send instead. Edit: You can invoke ! as a prefix operator; it's just negation: !x
Why does this definition cause IRB to loop infinitely? Does IRB rely on a method named ! for printing its prompt or something similar?
I'm using Ruby 2.4.3 and IRB 0.9.6 on Windows 10.
tl;dr: Overriding ! outside of a class is a very weird thing to do! There are countless ways that you can "break" ruby by doing crazy things like this - so you may find it fun to play around with such strange ideas, but obviously don't do this in important code!
In ruby, all classes inherit from the top-level base class: BasicObject. This class defines top-level object negation - i.e. Whenever you write
!foo
this is actually calling a method called ! on your object foo:
foo.send(:!)
This makes it possible (although it's a very rare thing to do!) to redefine the method on a specific class. For example, when implementing the null object pattern you could do something like this:
class NullObject
def !
true
end
end
my_null = NullObject.new
!!my_null #=> false
(Normally, the only objects that would return false in the above line are nil and false!)
Now then, back to your example. What you actually did here was define a method called ! on the class Object (and didn't call super to trigger the original method!). In other words, you basically re-defined the response a fundamental method that gets used all over the place internally. Something, somewhere (??) got confused by this bizarre behaviour and failed non-gracefully.
irb(main):001:0> def !
irb(main):002:1> puts "foo"
irb(main):003:1> super # <-- !! This stops it from breaking completely !!
irb(main):004:1> end
=> :!
irb(main):005:0* method(:!)
foo
foo
=> #<Method: Object#!>
irb(main):006:0> method(:!).source_location
foo
foo
=> ["(irb)", 1]
irb(main):007:0> method(:!).super_method
foo
foo
=> #<Method: BasicObject#!>
Here are some other ways you could re-define methods to cause bizarre behaviour/errors, for example:
def nil?
true
end
# Will now die in weird ways!
class String
def ===(other)
true
end
end
"ruby" === "awesome"
#=> true
Given this script
def hash
puts "why?"
end
x = {}
x[[1,2]] = 42
It outputs the following
why?
/tmp/a.rb:6:in `hash': no implicit conversion of nil into Integer (TypeError)
from /tmp/a.rb:6:in `<main>'
It seems that the hash function defned in the script is overriding Array#hash in that case. Since the return value of my hash method is nil and not an Integer, it throws an exception. The following script seems to confirm this
puts [1,2,3].hash
def hash
puts "why?"
end
puts [1,2,3].hash
The output is
-4165381473644269435
why?
/tmp/b.rb:6:in `hash': no implicit conversion of nil into Integer (TypeError)
from /tmp/b.rb:6:in `<main>'
I tried looking into the Ruby source code but could not figure out why this happens. Is this behavior documented?
You're not overriding Array#hash, you're shadowing Kernel#hash by creating Object#hash:
puts method(:hash)
def hash
puts "why?"
end
puts method(:hash)
That prints:
#<Method: Object(Kernel)#hash>
#<Method: Object#hash>
Fix it so we can see more:
def hash
puts "why?"
super
end
x = {}
x[[1,2]] = 42
Now the output is:
why?
why?
And no error. Try it with x[[1,2,3,4,5,6,7]] = 42 and you'll instead see why? printed seven times. Once for each array element, since the array's hash method uses the hashes of its elements. And Integer#hash doesn't exist, it inherits its hash method from Object/Kernel, so yours gets used.
This is due to a kind of hack in Ruby top level. Have you ever wondered how this works?
def foo
end
p self
foo
class Bar
def test
p self
foo
end
end
Bar.new.test # no error
How are two totally different objects (main and a Bar) able to call foo like it's a private method call? The reason is because... it is a private method call.
When you define a method at the top level of your Ruby script, it gets included (via Object) in every object. That's why you can call top-level methods like they are global functions.
But why does this break only hash and not other common methods? def to_s;end won't break to_s, for example. The reason is because hash is recursive: most* class implementations ultimately call down to Object#hash for their implementations. By redefining that base case, you break it globally. For other methods like to_s you won't see a global change because it's way up the inheritance chain and doesn't get invoked.
* the only objects this doesn't break are a few literals that probably have hard-coded hash values e.g. [] {} "" true etc.
Imagine I want to write my own math operators like "+"
The simple version would be:
def plus(a,b)
return a+b
end
But this is not what the real "+" does.
I want 3.add(4) # =>7
But how do I tell ruby to take the object that I used my method on?
I tried
def add(c)
return self+c
end
But I get the error message:
:in <main>': private methodadd' called for 3:Fixnum (NoMethodError)
The problem
You defined the method:
def add(c)
return self + c
end
and attempted to use it thus:
3.add(4) #=> NoMethodError: private method `add' called for 3:Fixnum
Understanding this error message
This error message tells you exactly what the problem is. I think your problem is simply that you don't understand how Ruby invokes methods on objects.
When Ruby sees 3.add(4) it first looks at the receiver, 3, and determines:
3.class #=> Fixnum
This tells it where the method add is defined: in the class Fixnum or in one of Fixnum's ancestor's classes or modules.
So it looks for it there, doesn't find it, and issues an error message. We can confirm it's not there:
Fixnum.instance_methods.include?(:add)
#=> false
So where is add defined?
You did define it, though, so where is it? Let's find out:
method(:add).owner
#=> Object
Object.instance_methods.include?(:add)
#=> false
Object.instance_methods returns an array of all of public instance methods defined on Object and Object's ancestors. add is not among those, so we conclude add is a protected or private method:
Object.protected_instance_methods.include?(:add)
#=> false
Object.private_instance_methods.include?(:add)
#=> true
Let's try invoking that method on an instance of Object:
Object.new.add(4)
#=> NoMethodError:
# private method `add' called for #<Object:0x007fdb6a27fa68>
That makes sense, considering that Object#add is private. We can, however invoke private methods with Object#send:
Object.new.send(:add,4)
#NoMethodError: undefined method `+' for #<Object:0x007fdb6a28e068>
As an exercise, make sure you understand the steps Ruby took that led to her raising this exception (that the instance method + is not defined on Object, or equivalently, that the instance of Object does not have a method +).
By the way, where did you define add? By that, I mean what what was the value of self when you defined it? Let's see:
self #=> main
self.class #=> Object
We see that add must be defined on the class for which its receiver is an instance. (A mouthful, yes, but it's important, so make sure you understand that).
Why is Object#add private rather than public?
Consider:
def greet
puts 'hi'
end
class A
end
A.private_instance_methods.include?(:add)
#=> true
A.new.send(:greet)
#=> 'hi'
The is because A inherits greet from Object:
A.ancestors.include?(Object) #=> true
If Object#greet were public, every built-in class and every class you define would have a public instance method greet. That would result in a great deal of misery. (Suppose you had a method great and mistyped it greet!) Even the private greet could cause trouble.)
Where should add be defined?
Since add.class => Fixnum, we define it thus:
class Fixnum
def add(other)
self + other
end
end
Fixnum.instance_methods.include?(:add) #=> true
3.add(4) #=> 7
Had I included the line puts "self#{self}" after class Fixnum it would have printed "Fixnum". Salting your code with puts statements that show the value of self often helps in understanding what's going on.
One last thing:
method(:add).owner
#=> NameError: undefined method `add' for class `Object'
Why did this not return Fixnum? Since method has no explicit receiver (i.e., no xx.method), Ruby assumes the receiver to be self, which here is:
self #=> main
so she looks for the method method in self.class => Object, and you know what she finds (or, I should say, doesn't find). Instead, we need to write:
Fixnum.instance_method(:add).owner #=> Fixnum
or
3.method(:add).owner #=> Fixnum
Here 3 can of course be replaced by any instance of Fixnum.
Note I've simplified this explanation somewhat. In searching for a method, Ruby also looks in the receiver's singleton class. This is not an issue for immediate objects (numbers, symbols, true, false and nil), however, as they do not have singleton classes:
3.singleton_class #=> TypeError: can't define singleton
By contrast, for example:
[1,2].singleton_class #=> #<Class:#<Array:0x007fbcf18c01a8>>
The plus-sign (+) in ruby can be overridden pretty much like any other method (you can look for operator-overloading):
class MyOperator
attr_accessor :text
def initialize(text)
#text = text
end
def +(operand)
"#{self.text} #{operand.text}"
end
def to_s
self.text
end
end
a = MyOperator.new "Hello"
b = MyOperator.new "World"
puts (a+b)
So there is not much magic to it. But you have to be careful if the overloading the operators make sense in your context.
class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
gives me the error:
SyntaxError: dynamic constant assignment error
Why is this considered a dynamic constant? I'm just assigning a string to it.
Your problem is that each time you run the method you are assigning a new value to the constant. This is not allowed, as it makes the constant non-constant; even though the contents of the string are the same (for the moment, anyhow), the actual string object itself is different each time the method is called. For example:
def foo
p "bar".object_id
end
foo #=> 15779172
foo #=> 15779112
Perhaps if you explained your use case—why you want to change the value of a constant in a method—we could help you with a better implementation.
Perhaps you'd rather have an instance variable on the class?
class MyClass
class << self
attr_accessor :my_constant
end
def my_method
self.class.my_constant = "blah"
end
end
p MyClass.my_constant #=> nil
MyClass.new.my_method
p MyClass.my_constant #=> "blah"
If you really want to change the value of a constant in a method, and your constant is a String or an Array, you can 'cheat' and use the #replace method to cause the object to take on a new value without actually changing the object:
class MyClass
BAR = "blah"
def cheat(new_bar)
BAR.replace new_bar
end
end
p MyClass::BAR #=> "blah"
MyClass.new.cheat "whee"
p MyClass::BAR #=> "whee"
Because constants in Ruby aren't meant to be changed, Ruby discourages you from assigning to them in parts of code which might get executed more than once, such as inside methods.
Under normal circumstances, you should define the constant inside the class itself:
class MyClass
MY_CONSTANT = "foo"
end
MyClass::MY_CONSTANT #=> "foo"
If for some reason though you really do need to define a constant inside a method (perhaps for some type of metaprogramming), you can use const_set:
class MyClass
def my_method
self.class.const_set(:MY_CONSTANT, "foo")
end
end
MyClass::MY_CONSTANT
#=> NameError: uninitialized constant MyClass::MY_CONSTANT
MyClass.new.my_method
MyClass::MY_CONSTANT #=> "foo"
Again though, const_set isn't something you should really have to resort to under normal circumstances. If you're not sure whether you really want to be assigning to constants this way, you may want to consider one of the following alternatives:
Class variables
Class variables behave like constants in many ways. They are properties on a class, and they are accessible in subclasses of the class they are defined on.
The difference is that class variables are meant to be modifiable, and can therefore be assigned to inside methods with no issue.
class MyClass
def self.my_class_variable
##my_class_variable
end
def my_method
##my_class_variable = "foo"
end
end
class SubClass < MyClass
end
MyClass.my_class_variable
#=> NameError: uninitialized class variable ##my_class_variable in MyClass
SubClass.my_class_variable
#=> NameError: uninitialized class variable ##my_class_variable in MyClass
MyClass.new.my_method
MyClass.my_class_variable #=> "foo"
SubClass.my_class_variable #=> "foo"
Class attributes
Class attributes are a sort of "instance variable on a class". They behave a bit like class variables, except that their values are not shared with subclasses.
class MyClass
class << self
attr_accessor :my_class_attribute
end
def my_method
self.class.my_class_attribute = "blah"
end
end
class SubClass < MyClass
end
MyClass.my_class_attribute #=> nil
SubClass.my_class_attribute #=> nil
MyClass.new.my_method
MyClass.my_class_attribute #=> "blah"
SubClass.my_class_attribute #=> nil
SubClass.new.my_method
SubClass.my_class_attribute #=> "blah"
Instance variables
And just for completeness I should probably mention: if you need to assign a value which can only be determined after your class has been instantiated, there's a good chance you might actually be looking for a plain old instance variable.
class MyClass
attr_accessor :instance_variable
def my_method
#instance_variable = "blah"
end
end
my_object = MyClass.new
my_object.instance_variable #=> nil
my_object.my_method
my_object.instance_variable #=> "blah"
MyClass.new.instance_variable #=> nil
In Ruby, any variable whose name starts with a capital letter is a constant and you can only assign to it once. Choose one of these alternatives:
class MyClass
MYCONSTANT = "blah"
def mymethod
MYCONSTANT
end
end
class MyClass
def mymethod
my_constant = "blah"
end
end
Constants in ruby cannot be defined inside methods. See the notes at the bottom of this page, for example
You can't name a variable with capital letters or Ruby will asume its a constant and will want it to keep it's value constant, in which case changing it's value would be an error an "dynamic constant assignment error". With lower case should be fine
class MyClass
def mymethod
myconstant = "blah"
end
end
Ruby doesn't like that you are assigning the constant inside of a method because it risks re-assignment. Several SO answers before me give the alternative of assigning it outside of a method--but in the class, which is a better place to assign it.
Many thanks to Dorian and Phrogz for reminding me about the array (and hash) method #replace, which can "replace the contents of an array or hash."
The notion that a CONSTANT's value can be changed, but with an annoying warning, is one of Ruby's few conceptual mis-steps -- these should either be fully immutable, or dump the constant idea altogether. From a coder's perspective, a constant is declarative and intentional, a signal to other that "this value is truly unchangeable once declared/assigned."
But sometimes an "obvious declaration" actually forecloses other, future useful opportunities. For example...
There are legitimate use cases where a "constant's" value might really need to be changed: for example, re-loading ARGV from a REPL-like prompt-loop, then rerunning ARGV thru more (subsequent) OptionParser.parse! calls -- voila! Gives "command line args" a whole new dynamic utility.
The practical problem is either with the presumptive assumption that "ARGV must be a constant", or in optparse's own initialize method, which hard-codes the assignment of ARGV to the instance var #default_argv for subsequent processing -- that array (ARGV) really should be a parameter, encouraging re-parse and re-use, where appropriate. Proper parameterization, with an appropriate default (say, ARGV) would avoid the need to ever change the "constant" ARGV. Just some 2¢-worth of thoughts...
In Ruby, I can define a method foo=(bar):
irb(main):001:0> def foo=(bar)
irb(main):002:1> p "foo=#{bar}"
irb(main):003:1> end
=> nil
Now I'd like to check if it has been defined,
irb(main):004:0> defined?(foo=)
SyntaxError: compile error
(irb):4: syntax error, unexpected ')'
from (irb):4
from :0
What is the proper syntax to use here? I assume there must be a way to escape "foo=" such that it is parsed and passed correctly to the defined? operator.
The problem is that the foo= method is designed to be used in assignments. You can use defined? in the following way to see what's going on:
defined?(self.foo=())
#=> nil
defined?(self.foo = "bar")
#=> nil
def foo=(bar)
end
defined?(self.foo=())
#=> "assignment"
defined?(self.foo = "bar")
#=> "assignment"
Compare that to:
def foo
end
defined?(foo)
#=> "method"
To test if the foo= method is defined, you should use respond_to? instead:
respond_to?(:foo=)
#=> false
def foo=(bar)
end
respond_to?(:foo=)
#=> true
You can check if a method exists by using the respond_to? method, and you pass it a symbol, e.g. bar.respond_to?(:foo=) to see if the object bar has a method foo=. If you want to know if instances of a class respond to a method you can use method_defined? on the class (or module), e.g. Foo.method_defined?(:bar=).
defined? isn't a method, but an operator which returns a description of the operand (or nil if it is not defined, which is why it can be used in an if statement). The operand can be any expression, i.e. a constant, a variable, an assignment, a method, a method call, etc. The reason why it doesn't work when you do defined?(foo=) is because of the parentheses, skip them and it should work more or less as expected. That being said, defined? is a pretty weird operator, and no one uses it to test for the existence of methods.