Why do undeclared Ruby local, instance, class, & global variables have different behavior? - ruby

Some undeclared variables are nil, some throw an error. How come?
$ irb
1.9.3p0 :001 > asdf # local
NameError: undefined local variable or method `asdf' for main:Object
from (irb):1
from /Users/saizai/.rvm/rubies/ruby-1.9.3-p0/bin/irb:16:in `<main>'
1.9.3p0 :002 >#asdf # instance
=> nil
1.9.3p0 :003 >##asdf # class
NameError: uninitialized class variable ##asdf in Object
from (irb):3
from /Users/saizai/.rvm/rubies/ruby-1.9.3-p0/bin/irb:16:in `<main>'
1.9.3p0 :004 > $asdf # global
=> nil

Class variables must always be assigned or else they will return a NameError when you attempt to use them. I do not currently have the details as to why this is.
Instance and Global variables will return nil even if they are not assigned. However, they will raise a warning if you run the script with the -w flag.
I do, however, have the answer in regards to the local variables. The reason local variables act like this comes in the fact that they do not have any punctuation in front of them. This means the variable could be either a variable or a method call (since Ruby does not require () after a method call with no parameters).
something # could be a variable named 'something' or a method called 'something()'
If there is no value assigned to something variable then the Ruby interpreter assumes it is a method invocation. If there is no method by that name then it raises NameError. That is why you will get this message:
NameError: undefined local variable or method 'something' for main:Object
from (irb):1
from path/to/Ruby/bin/irb:12 in '<main>'
So, it is important for the Ruby interpreter to treat local variables in this manner just in case it is actually a method you are referring to.
As an interesting side note:
There is one quirk—a variable comes into existence when the Ruby
interpreter sees an assignment expression for that variable. This is
the case even if that assignment is not actually executed. A variable
that exists but has not been assigned a value is given the default
value nil.
Which means that:
if false
z = "Something"
end
z.nil? #=> true
never_assigned.nil? #=> NameError
The above quote is from The Ruby Programming Language by David Flanagan and Yukihiro Matsumoto section 4.2

Related

Ruby: how to determine an object type in irb and binding.pry?

I have just begun adding binding.pry after my objects in order to start determining what they evaluate to. However, now I want to know what type of object the output is.
How can I do this in irb? How can I do this in binding.pry?
EDIT:
Here is what I have tried to determine the type of object H. I know it is a hash, but sometimes it is less obvious in the console:
irb(main):001:0> H = Hash["a" => 100, "b" => 200]
=> {"a"=>100, "b"=>200}
irb(main):002:0> H
=> {"a"=>100, "b"=>200}
irb(main):003:0> type(H)
NoMethodError: undefined method `type' for main:Object
from (irb):3
from /Users/macbook/.rbenv/versions/2.3.0/bin/irb:11:in `<main>'
irb(main):004:0> object.class(H)
NameError: undefined local variable or method `object' for main:Object
Did you mean? object_id
from (irb):4
from /Users/macbook/.rbenv/versions/2.3.0/bin/irb:11:in `<main>'
irb(main):005:0> object.is_a?(H)
NameError: undefined local variable or method `object' for main:Object
Did you mean? object_id
from (irb):5
from /Users/macbook/.rbenv/versions/2.3.0/bin/irb:11:in `<main>'
You can get the class with
h = {a: 100, b: 200}
h.class
# Hash
You can also check if something is of a particular class
h.is_a? Hash
# true
h.is_a? String
# false
The Ruby Language has no concept of "type". Or, to be more precise: in Ruby, types only exist latently in the mind of the programmer, they are not manifest in the program. Ergo, there is no way to get an object's type from the program, you can only get it from the programmer.
Sometimes, types are written down in the documentation or in comments. Some types are basically community folklore, not written down anywhere but passed down from generation to generation.
You can ask an object about its class, you can ask it about its methods, you can ask it whether it responds to a specific message, but you can not ask it about its type.
Note that older versions of Ruby had a type but that method was removed because it was misleading: it didn't actually return the type (I explained above why that is impossible), it returned the class, which is something completely different.

Set a Ruby variable and never be able to change it again? [duplicate]

This question already has answers here:
Closest Ruby representation of a 'private static final' and 'public static final' class variable in Java?
(4 answers)
Closed 8 years ago.
Giving a Java example:
class MyClass {
private final int variable;
MyClass(int variable) {
this.variable = variable;
}
}
Is there something like final I can do in Ruby? I looked up freeze but am not sure if it's the right approach. I don't need the variable to be assigned while creating a new instance of my class, I need to set it in one of the methods and once it's set I want to freeze it.
They are called constants. A constant in Ruby is defined by a UPPER_CASE name.
VARIABLE = "foo"
It is worth to mention that, technically, in Ruby there is no way to prevent a variable to be changed. In fact, if you try to re-assign a value to a constant you will get a warning, not an error.
➜ ~ irb
2.1.5 :001 > VARIABLE = "foo"
=> "foo"
2.1.5 :002 > VARIABLE = "bar"
(irb):2: warning: already initialized constant VARIABLE
(irb):1: warning: previous definition of VARIABLE was here
=> "bar"
It's also worth to note that using constants will warn you if you try to replace the value of the constant, but not if you change the constant value in place.
2.1.5 :001 > VARIABLE = "foo"
=> "foo"
2.1.5 :002 > VARIABLE.upcase!
=> "FOO"
2.1.5 :003 > VARIABLE
=> "FOO"
In order to prevent changes to the value referenced by the constant, you can freeze the value once assigned.
2.1.5 :001 > VARIABLE = "foo".freeze
=> "foo"
2.1.5 :002 > VARIABLE.upcase!
RuntimeError: can't modify frozen String
from (irb):2:in `upcase!'
from (irb):2
from /Users/weppos/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>'
2.1.5 :003 > VARIABLE
=> "foo"
Here's an example inside a class.
class MyClass
MY_CONSTANT = "foo"
end
MyClass::MY_CONSTANT
# => "foo"
The Ruby equivalent of your code looks like this:
class MyClass
def initialize(variable)
#variable = variable.freeze
end
end
Generally this is frowned upon as this method doesn't have ownership of the object variable represents, so the caller might be in for a rude surprise when their object is suddenly frozen.
Note that this prevents manipulating the #variable object, but doesn't prevent repeated assignment to that property. There's nothing that can block that behaviour.
You can alway make a copy if applicable and freeze that.
In general practice you don't normally do this, but instead take a disciplined approach about not manipulating objects your class doesn't own, or making copies of them if you do need to make changes.
So, in summary, there's no tradition of locking things down like this in Ruby and very little support in the language for that sort of behaviour.
The closest thing might be a constant. A Ruby constant is like a variable, except that its value is supposed to remain constant for the duration of the program. The Ruby interpreter does not actually enforce the constancy of constants, but it does issue a warning if a program changes the value of a constant.

Ruby assignment vs expression modifier precedence [duplicate]

This question already has answers here:
I don't understand ruby local scope
(5 answers)
Closed 8 years ago.
I see the following behavior in both MRI 2.0 and jruby 1.7.16.1:
irb(main):001:0> a
NameError: undefined local variable or method `a' for main:Object
from (irb):1
from /usr/bin/irb:12:in `<main>'
irb(main):002:0> a = 2 unless true
=> nil
irb(main):003:0> a
=> nil
irb(main):004:0>
I expected a to remain undefined because = has higher precedence than unless. What am I missing?
a = 2 unless true is evaluated like this:
unless true
a = 2
end
precedence doesn't come into play because its a different scope.
a #=>NameError: undefined local variable or method `a' for main:Object
The parser can not decide if this is a local variable OR METHOD, as the error says.
a = 2 unless true
Here the parser is able to recognize this is meant to be a variable, and it is defined (not initialized). It will be initialized if the statement is executed. Uninitialized variables evaluate to nil.

erb gives me - undefined local variable or method for main:Object (NameError)

erb gives me undefined local variable or method for main:Object (NameError) unless the variable used in erb template is a global variable.
Is that correct? on ruby 1.8.7 (2010-01-10 patchlevel 249) [i486-linux]
Below is code that works. If I remove $ from the variable name ($db, $db_root, $db_root_password) I get the error.
$db = get_single_argument("database name")
$db_root = get_single_argument("database root user name")
$db_root_passwd = get_single_argument("database root user password")
mysql_commands = get_conf_file("installer_mysql.erb")
puts mysql_commands.result #gives me the error
and get_conf_file procedure
def get_conf_file(file)
return_array = Array.new
if (File.exists?(file))
return_array = ERB.new File.read(file)
end
return_array
end
Ruby has a concept called a binding, which you might think of as the local variables, value of self, block etc. that a piece of code might have. You might also think of a binding as the code's context.
Erb's result method takes an optional second method which is the binding with which to evaluate the code you give it, so you can do stuff like
x = 1
ERB.new('x=<%= x %>').result(binding) #=> "x=1"
You're not passing in the binding of the caller, and you should be:
puts mysql_commands.result(binding)
The binding contains all the variable references in the current scope.

Ruby equivalent operators at "OrElse" and "AndAlso" of Vb.net

There are operators in Ruby similar to "OrElse"and "AndAlso" in VB.NET?
For example in Ruby NoMethodError exception is raised when active_record is nil:
if active_record.nil? || active_record.errors.count == 0
...
end
In VB.net i can do:
If active_record Is Nothing OrElse active_record.errors.count = 0
...
End
That does not generate an exception because it is only checked the first expression
In this case there will be no exception raised (because only the first term in || will be evaluated). However you might be interested in reading about Object#try from ActiveSupport, which can be helpful when dealing with objects that can be nil.
in ruby, there is a big difference between something that is nil and something that is undefined. Considering the following, from IRB:
ruby-1.9.2-p0 :002 > active_record
NameError: undefined local variable or method `active_record' for main:Object
from (irb):2
from /Users/jed/.rvm/rubies/ruby-1.9.2-p0/bin/irb:16:in `<main>'
ruby-1.9.2-p0 :003 > active_record = nil
=> nil
ruby-1.9.2-p0 :004 > active_record.class
=> NilClass
ruby-1.9.2-p0 :006 > active_record.nil?
=> true
So, an object that is nil is an instance of NilClass and therefore responds to the message nil? will return true, but without declaring the variable (as in your code) Ruby doesn't know what you are calling.
A couple of options here:
Ruby's || operator is a strict operator, whereas the or keyword is less strict, so I don't know where the vb operation compares to these two or flow options.
you could use a neat little gem callled 'andand'
require 'andand'
active_record.andand.errors.count == 0
but, generally when you are dealing with this situation in rails, you would use another means to determine the situation above, consider:
#post = Post.new(:my_key => "my value") #=> an ActiveRecord object
if #post.valid?
# do something meaningful
else
puts #post.errors.full_messages.to_sentence
end
and if you mean to assign something based on if it possibly undefined, you would want to use memoization:
#post ||= Post.new
which will declare the object if undefined or use the existing object
Ruby || is short circuit evaluation operator, so it should evaluate only first condition, therefore your if should not raise any exception.
I assume active_record.nil? returns boolean true.

Resources