Rails: Card.id has no value - ruby-on-rails-3

I'm a newbie in RoR, trying to do the "Pragmatic Agile Web Development" depot application.
The application needs to keep track of all the items added to the cart by the buyer.
Here is the creation of the Cart model:
rails generate scaffold cart
And this is the application controller code:
class ApplicationController < ActionController::Base
protect_from_forgery
private
def current_cart
Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
cart = Cart.create
session[:cart_id] = cart.id
cart
end
end
My questions:
In the "scaffold" command I didn't specify any column name for this table, but still the Cart seems to have one column called "id". Is the scaffold command auto generate an "id" column?
In this rescue block, a new Cart object is created without setting any value to "Card.id". on the next line we assign this "id" value to "session[:cart_id] = cart.id". what is the value that will be stored?
Your answers will be appreciated,
Thanks

Answer of 1: Yes id is the autoincremented id column generated automatically by the scaffold generator. Each time a new entry will be created a new id will be generated.
Answer of 2: Yes a new object of Cart is created unless it can find one by the already stored cart_id in session. Hence after creating a new Cart, it saves the new cart_id in session for future uses. Like on next request when the controller will again call current_cart function, it will get a valid cart_id from session and the corresponding Cart object as well.
If you are not really aware of session or how sessions are handled in ROR, then you can follow these links.
Concept of Session
http://en.wikipedia.org/wiki/Session_(computer_science)
http://php.about.com/od/learnphp/qt/session_cookie.htm
Session In ROR
http://guides.rubyonrails.org/security.html
Let me know anything left confusing.

Related

Rails GET request model update

Say i have a GETrequest action called thankyou that redirects the user to the thankyou template after they have ordered and paid for an item. In said action, is it bad practice, since it's a GET request for me to update some attribute on the order?
e.g.
def thankyou
#order.update_attributes(:approval_required => true)
end

RAILS 3 - Transactions in controllers

I have an example Action in a Controller.
def some_action
product = Product.new
product.name = "namepro"
if product.save
client.update_attribute(:product_id,product.id)
end
end
How to add transactions for this code? I try with this example code:
def some_action
**transaction do**
product = Product.new
product.name = "namepro"
if product.save
client.update_attribute(:product_create,Time.now)
end
**end**
end
But it produces this error:
undefined method `transaction'
I read about using transactions in Controllers is a bad practice but I don't know why is the reason (http://markdaggett.com/blog/2011/12/01/transactions-in-rails/)
In the example, if product has been created and saved and the client update fail... Rails must not do nothing.
thanks.
You can use a transaction in a controller if you really want to. As you noted, it's bad practice, but if you want to do it, just call Product.transaction do instead of transaction do. transaction is a class method on ActiveRecord::Base, so you need to call it on an ActiveRecord-derived class. Any model class in your application will do (nit-picking caveat: if you are connecting to different databases for different models, that may not be true...but you're probably not doing that).
The reason this is a bad practice is that it doesn't properly separate concerns according to the MVC paradigm. Your controller shouldn't be so concerned with your data persistence implementation. A better approach would be to add a method to Product. Maybe something like this:
def save_and_update_create_time
transaction do
if save
client.update_attribute(:product_create, Time.now)
end
end
end
Then instead of calling product.save in your controller, call product.save_and_update_client_create_time. You may need to pass client to that method too; it's unclear from your code where client comes from. If it's an attribute on product, then the method above should work.
There are better, more Railsy ways to do this, too, especially if a product knows about its client without needing any controller data. Then you can just use an after_save callback, like this (add to Product class):
after_save :update_client
private
def update_client(product)
product.client.update_attribute(:product_create, Time.now)
end
Then every time a Product is saved, the field on the associated client will be updated. You'll possibly have to introduce some code to check for the existence of a client first.
The benefit to using callbacks, besides cleaner code, is that the entire callback chain runs in a single transaction along with the save; you don't need to create the transaction manually. You can read more about callbacks in the Rails documentation.

ActiveRecord Delete...?

I'm just starting to learn ActiveRecord, and I am just trying out little things to figure out how everything works. I just tried the following code on the following sqlite3 database.
Ruby:
class Balances < ActiveRecord::Base
def initialize
#balance = 50
update_attribute(:balance, #balance)
end
def withdraw amount
update_attribute(:balance, #balance-amount)
end
end
SQL:
CREATE TABLE balance(
balance 50
);
When I write:
balance = Balances.new
I Get:
NoMethodError: undefined method `delete' for nil:NilClass
from /Users/Solomon/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.3/lib/active_record/attribute_methods/write.rb:28:in `write_attribute'
from /Users/Solomon/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.3/lib/active_record/attribute_methods/dirty.rb:67:in `write_attribute'
from /Users/Solomon/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.3/lib/active_record/attribute_methods/write.rb:14:in `balance='
from /Users/Solomon/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.3/lib/active_record/persistence.rb:180:in `update_attribute'
Why is this? Am I doing something wrong?
I notice several things:
The class name should be Balance (capitalized, singular). The table name in the database will be lower case, plural. Eg, balances
Don't define an initialize method for an ActiveRecord model. Instead use after_initialize callback. A post. Also, the Rails docs.
Added Also, the file name for the model should be balance.rb (lower case, singular)
Added some more You probably don't want to change the balance of the record back to 50 every time an instance of the record is initialized. -- That's what your example is currently doing. If you want to set the opening balance of new records in the database to be 50, then use the "before_create" callback.
Remember that ActiveRecord model classes are associated with, but different from, the records in the underlying database. For example, you can create an instance of an ActiveRecord model, and then not create a matching record in the database. -- The database record will only be created when and if you call the save or create methods.

finding id of nested attribute

I am very new to RoR so this may be very fundamental. My structure keeps getting a level deeper and I can't figure out how to find the id anymore.
First you have a Company which can have many Users. Users sign in and are authenticated and the current_user is saved in a cookie with the Session.
Since the User has one Company I can always find the Company.id through the current_user.
Next a Company has many Farms. In farms create I can get the company id from the user cookie and the farm id is new so that works, and in farm show Rails knows which farm it is supposed to show. So that level works.
Now I want to add that a Farm has many Blocks. I am adding Blocks through the associated Farm show page, but the Blocks_controller doesn't know what farm page it is on (as far as I can tell, if it can any info is appreciated).
Here is the FarmsController create that works:
def create
company_id = current_user.company_id
#company = Company.find(company_id)
#farm = #company.farms.build(params[:farm])
if #farm.save
flash[:success] = "farm created"
redirect_to root_path
else
render 'pages/home'
end
end
And this code just complains that it doesn't know what id I am talking about:
BlocksController
def create
#farm = Farm.find(params[:id])
#block = #farm.blocks.build(params[:block])
end
This is displaying on the associated Farm show page, so if there is a way to capture the id I would love to know what it is.
Thank you for your time.
The three easiest ways to get that id is to:
Pass in that farm_id using a hidden form field. When creating the link to your blocks/new form just pass in the farm_id ie use a path like new_blocks_path(:id => #farm.id) inside your blocks controller you will want to make sure that the farm_id is set on the Block model.
def new
#block = new Block
#block.farm_id = params[:farm_id]
end
Then if you are using form for the farm_id field (which should probably be of type hidden), it should contain the right id. Now change the first line in the "create" block method to
#farm = Farm.find(params[:block][:farm_id])
You can combine the process of adding the blocks and the farms using nested forms. Take a look at http://railscasts.com/episodes/196-nested-model-form-part-1 for how to do this.
You can use nested RESTful resources to make sure that within the blocks controller you always have access to the farm id. For more information about how to do this try take a look at http://railscasts.com/episodes/139-nested-resources

Rails: Difference between create and new methods in ActiveRecord?

I'm following a Rails 3.0 tutorial by lynda.com.
What's the difference between these two lines?
first_page = Page.new(:name => "First page")
first_page = Page.create(:name => "First page")
By the way, this is great tutorial; I recommend it for any other newbies like me.
Basically the new method creates an object instance and the create method additionally tries to save it to the database if it is possible.
Check the ActiveRecord::Base documentation:
create method
Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.
new method
New objects can be instantiated as either empty (pass no construction parameter) or pre-set with attributes but not yet saved (pass a hash with key names matching the associated table column names).