1

I'm so confused about that.. like this

class Box 
   attr_accessor :item ,:item2  

   def initialize(item2)
      @item = []
      @item2 = item2
   end

   def add(product)
      item << product
   end

   def empty?
      item.empty?
   end

   def increment(n=1)
      item2 +=1
   end

end

cart =Box.new(123)
cart.add(1)
puts cart.empty? #false
puts cart.item   #1

in the 'add' and 'empty?' methods I use local variable 'item' right? why I can get the value from @items ??

and I try this

cart.item2 = 345
puts cart.item2  #345
puts cart.increment #'increment': undefined method `+' for nil:NilClass (NoMethodError)

now I can't get the value?

please fix my brain thx

Luke.T
  • 25
  • 3
  • 1
    `item` is the method defined with `attr_accessor`. – Sagar Pandya Jan 11 '18 at 15:32
  • because of attr_accessor , I call item method in add method and insert product? – Luke.T Jan 11 '18 at 15:40
  • 2
    Hello! It's been all said you call the accessors when you don't add the @ of the instance variable. That's why your call to increment fails, because `item2`is a return value and not a variable, so it doesn't have a `+=` method. (Can't explain exactly why it tries to call it on Nil though). For example if I replace `item2 +=1` with `item2 + 1` I have no error and it returns `346` (You can't actually increment the instance variable because `item2` returns the value, and not the variable). – Cyril Lemaire Jan 11 '18 at 15:45
  • Luke, you need to read about what `attr_accessor` is doing and how `self` works in Ruby. – Sagar Pandya Jan 11 '18 at 15:48
  • wow, thank u guys, u made it all clear – Luke.T Jan 11 '18 at 15:54
  • `cart = Box.new(123,"")` should result in `ArgumentError (wrong number of arguments (given 2, expected 1))` – moveson Jan 11 '18 at 17:58
  • oh sorry. it should be Box.new(123), it's was item3 in the class, item3 is String variable . i was trying what's different. I got the same result like item2. so i delete item3 – Luke.T Jan 12 '18 at 01:35

1 Answers1

5

First, read this answer, which is the most-upvoted Ruby post in StackOverflow history. It will help you understand attr_accessor and its cousins attr_reader and attr_writer.

Besides that, your code has many problems.

First, you should not name an Array with a singular variable name like item. Use a plural items to make its purpose clear.

Second, the name item2 is not good. For your attribute, use something descriptive like counter, and for the variable passed as an argument to initialize it, let's use something descriptive like initial_count.

Third, your increment method takes an optional argument but then ignores it. Wouldn't it be surprising if someone called box.increment(2) and the attribute was incremented by only 1? The intent of this method is to use counter += n instead of counter += 1.

Fourth, to set counter from within the class, we need to use self. So instead of counter += n, we have to do self.counter += n.

Finally, consider whether you want the attributes to be readable and writable from an outside source, or whether you want to reserve write privileges to the object itself. Because you have methods to add things to items and to increment counter, you probably want to conceal write privileges. I would use attr_reader publicly and attr_writer privately.

Incorporating these suggestions, here's the resulting code:

class Box
  attr_reader :counter, :items

  def initialize(initial_count)
    @counter = initial_count
    @items = []
  end

  def add(product)
    items << product
  end

  def empty?
    items.empty?
  end

  def increment(n = 1)
    self.counter += n
  end

  private

  attr_writer :counter, :items
end

Now you can do this, all of which makes sense, more or less:

>> cart = Box.new(123)
>> cart.increment(2)
>> cart.counter
#> 125
>> cart.add('A product')
>> cart.add('Another product')
>> cart.items
#> ["A product", "Another product"]

But if you try to set counter or items directly, you'll get an error:

>> cart.counter = 1
#> NoMethodError: private method `counter=' called for #<Box:0x007fc13e17dc50>
moveson
  • 5,103
  • 1
  • 15
  • 32
  • Overkill, but a good explanation. Though, could you chose between `@attribute` and `self.attribute` instead of mixing them? – Cyril Lemaire Jan 11 '18 at 17:16
  • @CyrilLemaire Where/how am I mixing them? I'm not using `@attribute` anywhere but in the constructor. – moveson Jan 11 '18 at 17:33
  • Well then I might just be stupid, but what's the point of changing the syntax between the constructor and the other methods? – Cyril Lemaire Jan 11 '18 at 17:35
  • @CyrilLemaire I use the constructor to initialize instance variables, and then I access them via the setter/getter methods created by `attr_reader` and `attr_writer`. "Real" OO programming, so the argument goes, is about objects sending messages to objects. So the object sets its variables once, and then those variables are viewed or changed only as a result of the messages (internal and external) sent to the object. There are differing viewpoints, as outlined [here](https://forum.upcase.com/t/using-instance-variables-vs-attribute-accessors/1788/9). But I like sending messages. – moveson Jan 11 '18 at 17:47
  • Ahhh, ok thanks! I thought `@` was just a shortcut for `self.` while `self.` only refers to methods, and `@`to attributes ! – Cyril Lemaire Jan 11 '18 at 17:58
  • @CyrilLemaire Here's another [good discussion](http://ambertunnell.github.io/blog/2014/06/29/analyzing-instance-variables-in-ruby/). Do yourself a favor and get a copy of [Practical Object-Oriented Design in Ruby](http://www.poodr.com) by Sandi Metz. – moveson Jan 11 '18 at 18:00
  • ".. the variable name item2 is not good". Is variable the correct term here? – Sagar Pandya Jan 11 '18 at 18:29
  • @SagarPandya It's both a variable (as an argument) and a method/attribute. And it's not good for either one. But I take your point. See edited answer. – moveson Jan 11 '18 at 18:33
  • thank mate. you improve my code. actually, I knew attr_accessor make variable have getter and setter. so variable has `def variable` and def `variable=` , but I misunderstood it. I thought it is local variable. you guys make me clear. thanks again – Luke.T Jan 12 '18 at 02:19