0

I'm trying to learn ruby more in depth before I move on to rails dev, but I'm having some issues learning classes. I can't seem to understand why the following doesn't work.

#point.rb
class Point
  attr_accessor :x, :y

  def initialize(p = [0,0])
   @x = p[0]
   @y = p[1]
  end
end

#shape.rb
require_relative 'point.rb'

class Shape

  attr_accessor :points

  def initialize *the_points
    for p in the_points
      @points.append Point.new(p)
    end
  end

end

s = Shape.new([3,2])

puts s.points

When I call the function I get a no method error for NilClass, which I'm assuming is referring to @point.append.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
tshauck
  • 20,746
  • 8
  • 36
  • 36

4 Answers4

5

First, try this:

def initialize *the_points
  @points = []
  for p in the_points
    @points << Point.new(p)
  end
end

You get NilClass error because @points instance variable is Nil, and NilClass, which does not have append() method.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
rmk
  • 4,395
  • 3
  • 27
  • 32
  • Now I'm getting a different error: `shape.rb:11:in block in initialize: undefined method 'append' for []:Array (NoMethodError)` Which is really confusing to me, since I'd think that array would be fine with append. I'm on ruby 1.9.2 if that matters. – tshauck May 02 '11 at 17:31
  • 1
    @tshauck - you can use `push` or `<<` instead, there is no `append` method on Array. – McStretch May 02 '11 at 17:33
  • Hmm... This is a little surprising, but I don't see Array#append in Ruby 1.9.2. You should probably use << (e.g., @points << Point.new(p)) – rmk May 02 '11 at 17:37
  • muchas gracias @rmk and @McStretch – tshauck May 02 '11 at 17:37
1

Better than creating an array and populating it in a loop would be to initialize it like so:

class Shape
  attr_accessor :points

  def initialize *the_points
    @points = the_points.map{ |p| Point.new(p) }
  end
end
Phrogz
  • 296,393
  • 112
  • 651
  • 745
  • 1
    Point-free style FTW: `the_points.map(&Point.method(:new))`. Actually, I had this idea that classes are procedures, namely they are procedures that produce instances. And just like any object which is a procedure in Ruby, they should implement `to_proc`, so that you could write `the_points.map(&Point)`. The monkey patch for that is simple: `class Class; def to_proc; proc(&method(:new)) end end`. – Jörg W Mittag May 03 '11 at 03:35
  • @Jörg I wish you got rep for comment upvotes, because that's some crazy good thinking you're sharing here. :) – Phrogz May 03 '11 at 03:51
  • I think I posted that somewhere. At least I just found it in my StackOverflow snippets directory, but maybe it's for an answer I meant to give but never posted. – Jörg W Mittag May 03 '11 at 03:57
1

If you had warnings on (ruby -w or $VERBOSE = true), it'd warn you that @points didn't exist.

See some other debugging tips in How do I debug Ruby scripts?

Community
  • 1
  • 1
Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
0

You need to initialize @points to be a new array. It starts off as nil.

  def initialize *the_points
    @points = [];
    for p in the_points
      @points.append Point.new(p)
    end
  end
Vadim
  • 17,897
  • 4
  • 38
  • 62