0

I am a Ruby beginner. I am confused by an example from Why's Poignant Guide.

I understand the following example's use (also from the Poignant Guide) of picks in initialize, because it is passed in as an argument.

class LotteryTicket

  NUMERIC_RANGE = 1..25

  attr_reader :picks, :purchased

  def initialize( *picks )
    if picks.length != 3
      raise ArgumentError, "three numbers must be picked"
    elsif picks.uniq.length != 3
      raise ArgumentError, "the three picks must be different numbers"
    elsif picks.detect { |p| not NUMERIC_RANGE === p }
      raise ArgumentError, "the three picks must be numbers between 1 and 25"
    end
    @picks = picks
    @purchased = Time.now
  end

end

But how does initialize in the following example start using picks without picks being passed in as an argument? Here, note1, note2, note3 is passed in instead. How does that ever get assigned to picks?

class AnimalLottoTicket

      # A list of valid notes.
      NOTES = [:Ab, :A, :Bb, :B, :C, :Db, :D, :Eb, :E, :F, :Gb, :G]

      # Stores the three picked notes and a purchase date.
      attr_reader :picks, :purchased

      # Creates a new ticket from three chosen notes.  The three notes
      # must be unique notes.
      def initialize( note1, note2, note3 )
        if [note1, note2, note3].uniq!
          raise ArgumentError, "the three picks must be different notes"
        elsif picks.detect { |p| not NOTES.include? p }
          raise ArgumentError, "the three picks must be notes in the chromatic scale."
        end
        @picks = picks
        @purchased = Time.now
      end
end
mrwnt10
  • 1,234
  • 1
  • 17
  • 28

2 Answers2

1

That code has an error in it. When I run it in irb I get the following:

NoMethodError: undefined method `detect' for nil:NilClass

There's a discussion here from 2005. If you put the following at the beginning of initialize you'll probably get what they were looking for:

picks = [note1, note2, note3]
if picks.uniq!
Paul Rubel
  • 26,632
  • 7
  • 60
  • 80
0

Here, picks is not a local variable. It is a method defined by attr_reader :picks, :purchased. The method calls the value of the instance variable @picks. So @picks = picks is the same as @picks = @picks, which is assigning its value to itself, which does not have any effect. I think is was written by a person not familiar with Ruby.

sawa
  • 165,429
  • 45
  • 277
  • 381
  • It was written by _why (see link in question) – Charles Caldwell Aug 08 '13 at 17:52
  • :attr_reader :picks defines the AnimalLottoTicket.picks() method for getting the @picks variable in the class and declares @ picks. picks, without an @ is a local variable and not saved between instance methods. http://stackoverflow.com/questions/5046831/why-use-rubys-attr-accessor-attr-reader-and-attr-writer – Paul Rubel Aug 08 '13 at 18:39
  • 2
    @PaulRubel No. It doesn't define `AnimalLottoTicket.picks`. It defines `AnimalLottoTicket#picks`, which accesses the instance variable `@picks`. There is no variable declaring in Ruby. If `@picks` is called without assignment, it would be initialized to `nil` at that point. Method definition of `picks` has nothing to do with it. – sawa Aug 08 '13 at 18:42
  • You're right. I was confused and mistaken. I'd remove my downvote but it won't let me without an edit. – Paul Rubel Aug 08 '13 at 18:47
  • @sawa Could you briefly explain what you mean by there not being any variable declaring in Ruby? Aren't instance variables and class variables declared variables? – mrwnt10 Aug 08 '13 at 19:16
  • @mrwnt10 There is no explicit declaration for variable like `int x ...`. – sawa Aug 08 '13 at 19:22