Using self. Can you newbify this for me?

For some reason I am tripping up on the use of self and new.

class LineItem < ActiveRecord::Base
  belongs_to :order
  belongs_to :product

  def self.from_cart_item(cart_item)
    li = self.new
    li.product = cart_item.product
    li.quantity = cart_item.quantity
    li.total_price = cart_item.price
    li
  end
end

I don’t know for sure because I haven’t encountered a situation where I’d need to do things like this… But I’ll give it a shot - I’m open to corrections…

class LineItem < ActiveRecord::Base

belongs_to :order
belongs_to :product

  def self.from_cart_item(cart_item)
      li = new
      li.product = cart_item.product
      li.quantity = cart_item.quantity
      li.total_price = cart_item.price
      return li
 end

end

You’d simply use the new method without the self because self is used to refer to the current instance of the object. Also, you should put return li at the end; you don’t have to, but it just makes the code easier to read.

new is used to create a new object.

A new object is often created by using the class name followed by .new
e.g People.new, Items.new etc.

However in the example you gave you are creating a new object from
within the class, i.e. you are creating a LineItem object from within
the LineItem class and so instead of saying LineItem.new you use the
self keyword and it creates the object.

dev wrote:

For some reason I am tripping up on the use of self and new.

class LineItem < ActiveRecord::Base
  belongs_to :order
  belongs_to :product

  def self.from_cart_item(cart_item)
    li = self.new
    li.product = cart_item.product
    li.quantity = cart_item.quantity
    li.total_price = cart_item.price
    li
  end
end

This all looks correct. What problem do you think you're having? Which
tests are failing?

More importantly, why do you think you need to do this in the first
place? Although the method seems correct, it also seems like a strange
thing for your app to need.

Best,

dev wrote:

For some reason I am tripping up on the use of self and new.

class LineItem < ActiveRecord::Base
  belongs_to :order
  belongs_to :product

  def self.from_cart_item(cart_item)
    li = self.new
    li.product = cart_item.product
    li.quantity = cart_item.quantity
    li.total_price = cart_item.price
    li
  end
end

This pattern looks very much like the NSCoding protocol pattern that I'm
familiar with from the Cocoa frameworks. Compare what you have with this
Objective-C example:

- (id)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];
    mapName = [[coder decodeObjectForKey:@"MVMapName"] retain];
    legendView = [[coder decodeObjectForKey:@"MVLegend"] retain];
    auxiliaryView = [coder decodeObjectForKey:@"MVAuxView"];
    magnification = [coder decodeFloatForKey:@"MVMagnification"];
    return self;
}

This implementation is slightly more abstract, and implemented as an
instance method rather than a class method, but it serves essentially
the same purpose.

To use this method you would do this:
id myObject = [[MyObject alloc] initWithCoder:coder];

Notice that Obj-C instance methods begin with a minus sign (-) and class
methods begin with a plus sign (+). Since this is an instance method the
instance must be allocated before being initialized. This alloc-init
process is roughly equivalent to Ruby's "new" method.

One way to declare a class method in Ruby is to begin it with "self."
Which is what you are doing in your code (self.from_cart_item). In order
to use class methods you send messages to the class rather than an
instance of the class:

my_line_item = LineItem.from_cart_item # LineItem class is the receiver
of message from_cart_item

While you already have some good answers from Joshua, Floyd, and
Marnen, I'll add a bit to what they said from a Rails idiom perspective.
(Or ActiveRecord idiom if you prefer.)

class LineItem < ActiveRecord::Base
   belongs_to :order
   belongs_to :product

   def self.from_cart_item(cart_item)
     new(:product => cart_item.product,
         :quantity => cart_item.quantity,
         :total_price => cart_item.price)
   end
end

I think I recognize that example, so I suspect that you're working through the
Agile Wed Development with Rails. Like others have said, you could have put
self.new since self is the default receiver of a method and from within a
class method (i.e., defined on the LineItem class itself rather than on an
instance of that class). This is just a specialized form of instance creation
that knows how to create a new LineItem that has the properties from a
CartItem (which is presumably never in the database and thus not an
ActiveRecord model itself).

There are other ways of doing this same initialization, for example, with a block passed to new:

class LineItem < ActiveRecord::Base
   belongs_to :order
   belongs_to :product

   def self.from_cart_item(cart_item)
     new do |li|
       li.product = cart_item.product
       li.quantity = cart_item.quantity
       li.total_price = cart_item.price
     end
   end
end

You can think about LineItem.from_cart_item as a kind of cast or constructor if you're coming from C or Java (but please let it me just a fleeting thought that never appears once you "get it" from a Ruby perspective ;-).

-Rob

Rob Biedenharn
http://agileconsultingllc.com
  Rob@AgileConsultingLLC.com
http://gaslightsoftware.com
  rab@GaslightSoftware.com

Joshua Martin wrote:

Also, you should put return li at the end; you
don't have to, but it just makes the code easier to read.

Oddly most ruby style guides I've seen disagree with this. They recommend using return only if one is returning from the middle of the function.

I understand their position, but don’t you think it’s kinda weird to just leave it like that… No operations or anything… Ruby is all about readability, and to just type the name of the object without performing any operations on it is not really natural. It’s like, what the heck is it doing just sitting there like that? Why even waste the time typing it at all if it doesn’t do anything?

Question: Doesn’t Ruby automatically return the object you were last operating on? So, we could do whatever we want to the object we created, and just let it return the object automagically?

not quite.

it returns the result of the last operation.

Actually the value of the last expression.

Which is why ending a method with a variable name doesn't do nothing,
as Joshua seems to think, it provides the result of the method.

And it IS standard ruby practice.

Sorry; of course, expressions can be comprised of several operations :slight_smile: