Model Methods
As with ordinary ruby classes, you can add methods to your product class. Before we do that, let's add a way to track how many items of each product are left in our inventory. Do that below and in your app.
Instance Method
Once you've added a quantity column, you can set the quantity of your products. Next, let's create a method purchase
for purchasing a specific item.
class Product < ActiveRecord::Base
#...
def purchase
end
end
Now we want to fill in the above purchase
method so it decreases quantity by one.
We could use the standard update_attributes
method:
update_attributes(quantity: quantity - 1)
Rails also provides a useful method decrement
to make this more concise:
def purchase
decrement(:quantity)
end
Trying it out
Now that you have a purchase method, start your console and set the quantity of some products. For example:
prod = Product.first # this returns the first product
prod.quantity = 3
prod.save
Now, try out the purchase method:
prod.purchase
output:
(0.3ms) begin transaction
SQL (0.5ms) UPDATE "products" SET "quantity" = ?, "updated_at" = ? WHERE "products"."id" = ? [["quantity", 2]....
(2.1ms) commit transaction
=> true
Now prod's quantity is only 2, which you see by entering prod.quantity
.
You can purchase it a few more times, and it will go down to 0:
prod.purchase
prod.purchase
What happens if you purchase it again?
prod.purchase
prod.quantity
=> -1
Oops. We don't want to allow a negative quantity. Let's adjust the purchase method to prevent that:
def purchase
if quantity > 0 # line 2
decrement(:quantity) # line 3
return true
end
end
Now if you reload your console, you'll find that the quantity doesn't change when you try purchasing a product that's already at 0.
:symbols and variables
Question: In the purchase
method above, we referred to quantity
on its own in line 2, but we used the symbol :quantity
in line 3. What's going on? And how could we refer to quantity
if no variable like that had been set earlier in the code?
Answer: Rails automatically provides methods to get and set the columns of a model. So if you have a column in your model, you can use methods to get and set its values.
When you enter the following in your console:
prod = Product.first
prod.quantity
prod.quantity
calls the quantity 'getter' method and returns the value of quantity
of prod
. The same thing happens in the method above when it says if quantity > 0
. The getter method is called to get the value of quantity of your current product object. In fact, to make your code clearer, you could change it to:
if self.quantity > 0
self
here refers to the current object, and you can see it's calling quantity
just like prod.quantity
in the command line.
In the next line of code, we use a symbol:
decrement(:quantity)
Why? In this case it wouldn't make sense to say quantity
or self.quantity
, since that would just return a number value without telling decrement
what to change. The Rails decrement method takes in a symbol of the column/field you want to change, so that's what you need to pass it. Symbols are used as identifiers throughout Rails.
If you're not referring to an existing variable or calling a method, you'll probably want to use a symbol in Rails to identify a specific column.
Question: What about the following line of code?
update_attributes(quantity: quantity - 1)
Answer: In this case, Rails is using another shortcut. The method takes in a hash that consists of a symbol and the new value to set it to. You can rewrite it like this to be clearer:
update_attributes(:quantity => quantity - 1)
And here's a more explicit version of the entire method:
def purchase
if self.quantity > 0
self.update_attributes({:quantity => self.quantity - 1})
return true
end
end
Ruby has many ways to write the same thing, which can initially be a little confusing when you're reading code written in a different way!
Class methods
The purchase
method was called on specific product instances of the Product class. You can also create class methods that are called on the entire Product class.
Add the following method to your product class:
def self.available
end
Now let's get all the products whose quantity is not 0. You can do this in Rails 4 with the following code:
def self.available
Product.where.not(quantity: 0)
end
Finally, let's create one more class method to retrieve the most recently created product. Use .order
to order items in rails, and pass in the field you want to order them by: Product.order(:created_at)
. To get the last item in a collection, use .last
. Putting it all together, we have:
def self.newest
Product.order(:created_at).last
end
Challenges
Adding more class methods
- Create a class method
oldest
that returns the product with the oldestcreated_at
time. You can copy and adjust your method for getting the newest item. - (optional) Create a class methods
sold_out
that returns all the products with a quantity of 0. See the Rails Guide to Queries for help on the code to use.
Challenge
Let's create a migration file AddQuanityToProducts
to add a quantity column to products. Enter the code below to create the migration.
Please sign in or sign up to submit answers.
Alternatively, you can try out Learneroo before signing up.