4

I've been looking at learning a new dynamic scripting language for web development and after agonising over Python and Ruby, as I really liked both, I've decided to pick Ruby (It pretty much came down to a coin toss and the fact there are more RoR jobs in the UK than Python/Django). My question is about scope in Ruby. Do I have to declare a class attribute inside a method to be able to access it from other methods?

For example, I cannot do

class Notes
  @notes = ["Pick up some milk"]

  def print_notes
    puts @notes
  end
end

It seems I have to declare attributes I want to use in the constructor? This example works:

class Notes
  def initialize
    @notes = ["Pick up some milk"]
  end

  def print_notes
    puts @notes
  end
end

Is this right? I've noticed prefixing example one with @@ instead of @ works but to my understanding if the class has a subclass (say, Memo) then any changes to attributes prefixed with @@ in Notes would change the value in Memo?

Sorry if this is a repeat question, just a lost noobie :)

1 Answer 1

4

When you declare the @notes within the class but not in the constructor or any of the instance methods, then you are making @notes an instance variable of the instance of the class itself. Every class exists as an instance of Class too.

class Notes
  @notes = ["Pick up some milk"]

      def print_notes
        puts @notes
      end
  end
# => nil
Notes.instance_variable_get(:"@notes")
# => ["Pick up some milk"]

So the answer is yes, you do need to declare the instance variable within the constructor or some other instance method. I think you'd prefer to do this:

class Notes
  def notes
    @notes ||= []
  end

  def print_notes
    puts @notes
  end
end

note = Notes.new
note.notes << "Pick up some milk"
note.notes
# => ["Pick up some milk"]

In addition:

Just avoid class variables e.g. @@notes. Use class instance variables instead (which is what you unwittingly did).

Do this:

class Notes
  def self.notes
    @notes ||= []
  end
end

not this:

class Notes
  def notes
    @@notes ||= []
  end
end

when you want a class variable. The latter will cause you problems down the road. (But I think this is something for a different conversation.)

Sign up to request clarification or add additional context in comments.

3 Comments

Would this (the last code snippet you wrote) be the same as using attr_reader, attr_writer and attr_accessor? If so, is that the preferred way to do things?
@Iain The last snippet before the additional info? Yes and no. Yes, in that it's the same as attr_reader. No, because it defines a default value other than nil, which I've done because it's an array, otherwise the first time you set it you'd have to use note.notes = ["whatever"]. Because the default value is an array, then it in practice works very similarly as attr_accessor, because you're able to manipulate the value (the array) - although you cannot replace the array using =.
@Iain Since you can already program, then I'd suggest reading "Metaprogramming in Ruby" for an extensive discussion on scope and how it affects things, but there are lots of resources on the internet and other books (maybe others will suggest some).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.