0
votes

I create class in Ruby without any attr_* method like attr_reader, or attr_writer or attr_accessor. So something like this

class DocumentIdentifier
 def initialize( folder, name )
  @folder = folder
  @name = name
 end
end

I mean it can be instantiated like this right

di = DocumentIdentifier.new(folder, 'image')

But i read some codes in some ruby books they also add attr_* to the instance variable like this

class DocumentIdentifier
 attr_reader :folder, :name
 def initialize( folder, name )
  @folder = folder
  @name = name
 end
end

My question is, should we use attr_* in every class definition and why is the reason?

2
This is a general OOP question: When we have a class with some instance variables - under what condition should we provide getter and setter methods? This is not specific to Ruby, and this question has been discussed for instance here or here.user1934428
Does this answer your question? Explain to me what is a setter and getteruser1934428
yeah maybe it is general oop question but i also wanna if there is some ruby thingy answer, but it is a general oop question, so yeah you are right @user1934428dedypuji

2 Answers

1
votes

attr_reader will create methods that will return your instance variables with matching names.

class Foo
  attr_reader :bar

  def initialize(bar)
    @bar = bar
  end
end

This means that instances of Foo will both internally and externally have the method bar, that will respond with the value of the instance variable @bar:

foo = Foo.new(1)
foo.bar
=> 1

It's better to depend on behavior (a method) than on data (direct instance variable).

If you access the data through the methods generated by attr_reader you're ensuring you'll make your code slightly more "future proof" by encapsulating behavior in the same place, so it's always best to access all your instance variables through them.

Another interesting aspect is that you can limit your interface to match your needs. There's no need to expose bar to the outside world if you only use internally for instance, so:

class Foo
  def initialize(bar)
    @bar = bar
  end

  def zoo
    bar
  end


  private

  attr_reader :bar
end

Will behave like:

foo = Foo.new(1)
foo.zoo
=> 1

But:

foo.bar
NoMethodError (private method `bar' called for #<Foo:0x00005581544b83d8 @bar=1>)

Please let me know if that makes sense, otherwise I'll update the answer with more details.

0
votes

attr_reader is a "getter method". In other words, it allows us to declare that a data attribute will be publicly read-able.

When you save a data attribute on an instance of a class, that data can't be viewed from outside of the class by default. With attr_reader set, we can access that attribute from anywhere.

Here's an example using the "name" field from your question.

# without "attr_reader"
class DocumentIdentifier
   def initialize(folder, name)
      @folder = folder
      @name = name
  end
end

first_example = DocumentIdentifier.new(nil, 'test')

first_example.name

This will give an error:

Traceback (most recent call last):
        4: from /Users/ibell/.rvm/rubies/ruby-2.6.5/bin/irb:23:in `<main>'
        3: from /Users/ibell/.rvm/rubies/ruby-2.6.5/bin/irb:23:in `load'
        2: from /Users/ibell/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
        1: from (irb):9
NoMethodError (undefined method `name' for #<DocumentIdentifier:0x00007fe0160956e8 @folder=2, @name="l">)

Now, if you add the attr_accessor, the same code will work.

class DocumentIdentifier
  attr_reader :name
  def initialize(folder, name)
    @folder = folder
    @name = name
  end
end

second_example = DocumentIdentifier.new(nil, 'test')

second_example.name

This will give you the string "test" back.

Just as we need to read data, we might also want to write it. attr_writer works similarly to attr_reader, but it allows us to "set" data.

class DocumentIdentifier
  attr_writer :name
  def initialize(folder, name)
    @folder = folder
    @name = name
  end
end

second_example = DocumentIdentifier.new(nil, 'test')

second_example.name = 'we can change this text'

second_example.name

This will give you back: 'we can change this text'

Lastly, attr_accessor is a shortcut for when you want attr_reader and attr_writer on the same attribute.