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.
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.
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.
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:
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 ;-).
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?