2

I'm working my way through the "Well Grounded Rubyist" book and I was hoping to get some clarification on my understanding of self vs. @ for initialize.

There's a point where you create a simple Ticket class using "self." to set a venue and date. In broad strokes I understand that "self." is setting the venue and date for the current instance of Ticket, while @ is setting up an instance variable...but I'm not too sure why not use an instance variable instead?

From just playing around with it, both seem to be valid as I can access the date or venue successfully on a new instance of Ticket using either @ or self for the initialize.

class Ticket
  attr_accessor :venue, :date
  def initialize(venue, date)
    self.venue = venue
    self.date = date
  end
end

Any help to clear up my understanding would be much appreciated!

Sandeep Garg
  • 1,292
  • 1
  • 13
  • 31
D1D2D3D4D5
  • 101
  • 6

2 Answers2

3

When you use attr_accessor :venue, you're essentially doing this.

def venue
  @venue
end 

def venue=(value)
  @venue = value
end 

When you do attr_writer :venue, you're doing

def venue=(val)
  @venue = val 
end 

When you do attr_reader :venue, you're doing

def venue
  @venue 
end

Note that an @instance_variable is nil by default, or if it's uninitialized. To see this in action, type irb in your terminal and press enter. Then, type a single @, and spam a bunch of characters after it. @ausdhyf934234092348 will be nil.

When you're inside Ticket#initialize, you're acting as an instance of the object, thus self will return the current instance. self.class will return the class, and give you access to any class-level methods.

class Ticket
  VENUE_SIZES = [:small, :large]

  def self.venue_sizes
    VENUE_SIZES
  end 

  def initialize(venue, date)
    # ... 
    self.venue_sizes # NoMethodError
    self.class.venue_sizes # [:small, :large]
  end 

end

Going up the chain, if you're acting as the class and not an instance of a class, calling self.class — or self.class.class if you're acting as an instance, will return Class

attr_accessor is simply syntactical sugar; it was made because of the mundane chore of having to write those getter/setter methods over and over again. There are cooler problems to solve, like writing our own nadnerb_accessor in Ruby (though attr_accessor is implemented in C):

class Ticket
  def self.nadnerb_accessor(*names)
    names.each do |name|
      define_method(name) do
        instance_variable_get(:"@#{name}")
      end
      define_method("#{name}=") do |value|
        instance_variable_set(:"@#{name}", value)
      end
    end
  end

  nadnerb_accessor :venue, :price

  def initialize(venue, price)
    self.venue = venue
    self.price = price
  end

  def inspect
    "#<Ticket @venue=\"#{@venue}\" @price=\"#{@price}\">"
  end
end

ticket = Ticket.new("StackOverflow", "$5")
puts ticket.inspect 

Josh Brody
  • 5,153
  • 1
  • 14
  • 25
2

Pretty the same, but one is viewed as variable (@) the other as method (thanks to getter and setter). Example 4 "unveils the trick".

I hope the following examples could be self explanatory:


Example 1

class Ticket

  attr_accessor :venue

  def initialize(venue)
    self.venue = venue
  end

  def are_the_same?
    p self.venue == @venue
    p self.venue === @venue
    p self.venue.eql? @venue
    p self.venue.equal? @venue
  end

end

ticket = Ticket.new('Broadway') # this is an instance of Ticket

ticket.are_the_same?

#=> true
#=> true
#=> true
#=> true

See this great post for equals.


Example 2

class Ticket

  @@double_arroba = 'class variable' # class variable
  attr_accessor :venue

  def initialize(venue)
    self.venue = venue
  end

  def whats_self?
    p self # instance itself
  end

  def venue_self
    p self.venue # instance itself  calling its variable
  end

  def venue_arroba
    p @venue # instance variable directly
  end

  def whats_double_arroba # instance method using a class variable
    p @@double_arroba
  end

  def self.whats_double_arroba # class method using a class variable
    p @@double_arroba
  end

  def self.whats_self? # class method calling itself
    p self
  end

end

ticket = Ticket.new('Broadway') # this is an instance of Ticket

p ticket #=> #<Ticket:0x007fb3fb0f1868 @venue="Broadway">
ticket.whats_self? #<Ticket:0x007fb3fb0f1868 @venue="Broadway">
p ticket.venue #=> "Broadway" #thanks to attr_accessor
ticket.venue_self #=> "Broadway"
ticket.venue_arroba #=> "Broadway"

ticket.whats_double_arroba #=> "class variable"
Ticket.whats_double_arroba #=> "class variable"
Ticket.whats_self? #=> Ticket

Example 3

What happen if you remove attr_accessor?

class Ticket

  # attr_accessor :venue #comment out attr_accessor

  def initialize(venue)
    # self.venue = venue # venue method is not defined
    @venue = venue # so you need to use @
  end

  def venue_self
    # p self.venue # not working, self method is not available is nota available
  end

  def venue_arroba
    p @venue # instance variable directly
  end

end

ticket = Ticket.new('Broadway') # this is an instance of Ticket

p ticket #=> #<Ticket:0x007fb3fb0f1868 @venue="Broadway">
# p ticket.venue # does not work, no access
ticket.venue_self #=> nil, because commented the method code
ticket.venue_arroba #=> "Broadway"
# ticket.whats_double_arroba # not working

Example 4

The trick: getter and setter

# class with getter and setter

class Ticket

  def venue=(value) # setter (attr_reader)
    @venue = value
  end

  def venue # getter (attr_writer)
    @venue
  end

  def self_venue
    self.venue # (*) you can omit self and write just venue
  end

end

ticket = Ticket.new # ticket is the instance
ticket.venue="Brodway" # setter method .venue= in action
p ticket.venue #=> "Brodway" # getter method .venue in action
p ticket.self_venue #=> "Brodway" # calls the method .self_venue on the instance, which calls the getter method .venue (*)


# or write simply this code below, to let ruby build itself getter and setter

class Ticket

  attr_accessor :venue

end
iGian
  • 11,023
  • 3
  • 21
  • 36