-1

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....

chignon
  • 19
  • 1
  • 1
  • 7
  • 2
    You have a glitch somewhere else. The code above runs perfectly in IRB. The difference between lowercase and capitalized first letter is that the latter becomes a **constant**, and the former is a **local variable**. Constants, defined outside of the class (basically, on `main` special object) have extended life circle. Please post the complete code example. – Aleksei Matiushkin Jul 07 '16 at 04:43
  • @mudasobwa I wrote countless lines of code today to diagnose this. So the above snippet is really a legit example. It was basically my test example to isolate the caps. Should I perhaps reinstall IRB? – chignon Jul 07 '16 at 04:53
  • 1
    http://ideone.com/748cnI ⇐ it works perfectly. Since you’ve admitted that in your case the code fails in C9 as well, the problem should not be in IRB (I doubt that reinstalling could help.) This codepiece works, and unless you have specified more precise circumstances we’d be unable to help. – Aleksei Matiushkin Jul 07 '16 at 05:06
  • Let me guess: after this code runs, you’ve tried to access `rex` variable? Did you call `rex.wants` from IRB after requiring the file? In the latter case it’s obvious that `rex` won’t be defined in IRB context, since it’s local variable; local to the file context. `Rex`, on the other hand, being a constant, will be still visible and accessible. – Aleksei Matiushkin Jul 07 '16 at 05:11
  • @mudasobwa i exit irb in order for any changes to take effect. FYI I reinstall IRB, moved to notepad++, hence the delay in reply (rebooted my machine). no luck. IDK what to do....so frustrating!!! – chignon Jul 07 '16 at 05:17
  • @mudasobwa I could write another codepiece if that would help (this example is legit tho....it's not a placeholder). – chignon Jul 07 '16 at 05:23
  • Please re-read my comment starting with “Let me guess” and admit that you have both `rex` assignment and `rex.wants` call in the same place (either both inside a file, or both inside an IRB console.) – Aleksei Matiushkin Jul 07 '16 at 05:28
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/116637/discussion-between-chignon-and-mudasobwa). – chignon Jul 07 '16 at 05:31

1 Answers1

1

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.


Using An Instance Variable Globally

@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.


About instance variables

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)
Community
  • 1
  • 1
Cam
  • 921
  • 7
  • 13
  • You are advising to create an instance variable on `main` object, which is already nonsence. Besides that, as soon as the whole snippet (including `rex.wants` call) is located in the file, everything works fine. Downvoted. – Aleksei Matiushkin Jul 07 '16 at 05:23
  • @mudasobwa The author's goal is to propagate an instance of Dog from an included file into the global scope for access within IRB. Declaring a global variable or unbound instance variable satisfies that, however it may offend your OO sensibilities. – Cam Jul 07 '16 at 05:35
  • @chingon, you'd have to update your `rex.rb` file instantiate the globally scoped variable, `load 'rex.rb'` back into `irb`, and then you'll have access to that variable from `irb`. – Cam Jul 07 '16 at 05:46
  • @Cam as instance variable it works (instance variables are the next topic in my tutorial as well) But I still want to understand why receiver.method fails here – chignon Jul 07 '16 at 05:47
  • If you declare a local variable `rex` from within a file `rex.rb`, that variable is **not visible outside that file**. If you, on the other hand, declare `rex = Dog.new` **from IRB** it will work perfectly well. – Cam Jul 07 '16 at 05:50
  • @cam i see what u mean. Did not think of that. FYI there;s a chat invite..since I don't want to lose privileges on SO – chignon Jul 07 '16 at 05:51
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/116640/discussion-between-chignon-and-cam). – chignon Jul 07 '16 at 05:57
  • @Cam In IRB I tried rex = Dog.new . Got "uninitialized constant error" interesting – chignon Jul 07 '16 at 06:00
  • 1
    @Cam Everything is now more or less correct, save for the leading `@legs # instance variable (for each Dog)`. This is declaring not what you expect, since it is executed on **class level during class parsing**. Class variable would be accessible through instance getter/setter, but instance variable won’t. What you defined is accessible via `A.instance_variable_get(:@legs)` and has nothing to do with instance variable, having getter and setter (besides that they share the same name.) – Aleksei Matiushkin Jul 07 '16 at 14:52
  • Thanks, @mudasobwa. Declaring the instance variable at class level does create an instance variable on the class itself. I've removed that line & appreciate the feedback. For anyone who shares my curiosity about when this behavior might be desirable, [this post](http://stackoverflow.com/questions/3802540) covers use cases for "class instance variables". (Spoiler: they serve as "inheritance-proof" class variables). – Cam Jul 07 '16 at 16:00