So I ran into an interesting dilemma regarding object/receiver cases (upper/lower). Convention states lowercase but something seems to go awry. But, it my case, my code only works when the instance begins with a cap? It's driving me nuts!
I'm am using Start Command Prompt with Ruby. IRB 2.3, and also C9 (problem persisted in the IDE as well)
An example:
#rex.rb
class Dog
def wants(something)
puts "Rex wants to #{something}."
end
end
#rex instance is lowercase, per convention and the book's fidelity
rex = Dog.new
rex.wants("play")
This runs in my machine (via ruby rex.rb) and on irb (via require "rex") as well. It runs. But, then in irb:
rex.wants("x") yields a NameError basically saying rex object is undefined.
To remedy this I have to alter the code:
#rex.rb
class Dog
def wants(something)
puts "Rex wants to #{something}."
end
end
#Rex starts with an uppercase R now, not lowercase r - which it should be
Rex = Dog.new
Rex.wants("play")
I run this similarly on my machine (ruby rex.rb) and on irb (require "rex"). Then, in irb,
Rex.wants("x")
it runs (=> nil) perfectly.
My question: why do I am having to capitalize (the first letter of) my object/receiver? Convention is that they begin with lowercase.
Maybe the book I'm using (Head First Into Rudy) tackles this later on? IDK...the book hasn't touched IRB. I just wanted to experiment. Hopefully others with benefit...google didn't really help me today ;(.
The code runs...but the instance should be lowercase, shouldn't it? What am I missing here?
Is it possible that my machine is screwed up. The codepiece works on ideone when others test it. But it fails for me on my machine and c9? I'm at a loss....
Your trouble here is that any local variable defined in an included file will be out of scope within IRB
. By defining a constant, you were able to get around this. Declaring an explicit global variable $rex
or instance variable @rex
are alternatives:
@rex = Dog.new
@rex.wants("play")
@rex will now be accessible through IRB...
@rex.wants "x"
# Rex wants to x.
# => nil
@John Hyland has a suggestion for declaring a constant within a module which you might also look into.
@mudasobwa's accusation of heresy for using an instance variable globally is not without merit. Declaring @rex
outside of the context of a class and importing the containing file into IRB will add that instance variable to the main object. This violates Object Oriented design principles, may be confusing, and global variables should generally be avoided.
That said, a globally scoped instance variable may be appropriate if viewed as a "fish out of water" for experimentation purposes. I prefer it to declaring a constant we intend to mutate. Instance variables may also more closely resemble resemble code you'll eventually deploy; often in rails
or sinatra
applications, instance variables are declared within the scope of a route handler (e.g. #get
) that a view
can access.
Classically, instance variables should pertain to the class they live within. Here's how that looks:
# dog.rb
class Dog
@@population = 0 # class variable (shared across all Dogs)
def initialize legs=4
@legs = legs # instance variable (for each Dog)
@@population += 1
end
def amputate
@legs-=1 if @legs > 0
@legs
end
# setters and getters (we could DRY this up with attr_accessor...)
def legs= x
@legs = x
end
def legs
@legs
end
def self.count
@@population
end
end
Our class describes a model of what a Dog looks like in our program. Every dog we create will have its own instance variable @legs
and share a class variable @@population
with all Dogs. Calling Dog.new
will invoke #initialize
, which sets @legs to 4, by default, and adds 1 to the total population. From irb, we could test this out:
# irb terminal
rex = Dog.new.amputate
fido = Dog.new
puts "rex has %i legs, fido has %i legs" % [rex.legs, fido.legs]
# => rex has 3 legs, fido has 4 legs
puts "We have %i total dog(s)" % Dog.count
# => We have 2 total dog(s)