I'm building a concert ticket sales application with Rails 3.0.4, working primarily with the Agile Web Development tutorial (http://pragprog.com/titles/rails3/agile-web-development-with-rails) and trying to incorporate Ryan Bate's order purchase method (http://railscasts.com/episodes/146-paypal-express-checkout). Everything works with the following in orders_controller.rb:
def create
#order = Order.new(params[:order])
#order.add_line_items_from_cart(current_cart)
#order.ip_address = request.remote_ip
respond_to do |format|
if #order.save
Notifier.order_received(#order).deliver
format.html { redirect_to(calendar_url, :notice => 'Thank you for your order.') }
format.xml { render :xml => #order, :status => :created, :location => #order }
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
else
format.html { render :action => "new" }
format.xml { render :xml => #order.errors, :status => :unprocessable_entity }
end
end
But when I add "&& #order.purchase" to the conditional clause, with the order.rb model as follows:
class Order < ActiveRecord::Base
#...
belongs_to :cart
#...
def price_in_cents
(cart.total_price*100).round
end
def purchase
response = GATEWAY.purchase(price_in_cents, credit_card, purchase_options)
cart.update_attribute(:purchased_at, Time.now) if response.success?
response.success?
end
#...
end
I receive an "undefined method `total_price' for nil:NilClass" error. I can get around this by adding
#order = current_cart.build_order(params[:order])
to the orders "create" method, but this messes up the "order_received" notification by somehow preventing the pertinent order information (in this case "#order.line_items") from rendering in the e-mail text.
The "cart" object is being set to nil somewhere along the way, but removing
Cart.destroy(session[:cart_id])
from the order "create" method does not fix the problem.
Anyone got a clue for this noob?
It doesn't look like the Cart object is ever actually specified in the belongs_to relation, you need to either do #order.cart = current_cart, or current_cart.order = Order.new, or something along those lines.
Related
I have my Ruby on Rails app up and running with 3 scaffolded models. So far so good. Now I'm coding business logic and get a syntax error and can't quite understand how to code a variable.
Details:
3 models: Document, Employee, and EmpDocument.
Business rule: Each time a new Document is added create an EmployeeDocument for each Employee
1.) Loop through Employees
2.) Cut an EmpDocument.rcd
I get an error on the .Create line, because I don't have my variables coded correctly.
I've looked through documentation without finding any rules/examples.
Document.Model
def create
#document = Document.new(document_params)
respond_to do |format|
if #document.save
format.html { redirect_to #document, notice: 'Document was successfully created.' }
format.json { render :show, status: :created, location: #document }
# create an EmpDocument record for each employee for this new document
##employees = Employee.find(:all)
Employee.all.each do |employee|
Empdocument.Create(:document_id => #document.document_id, :employee_id => employee.employee_id, :viewed => '0001-01-01')
end
else
format.html { render :new }
format.json { render json: #document.errors, status: :unprocessable_entity }
end
end
end
here's the error:
"undefined method `document_id' for #"
##employees = Employee.find(:all)
Employee.all.each do |employee|
Empdocument.Create(:document_id => #document.document_id, :employee_id => employee.employee_id, :viewed => '0001-01-01')
end
For your current requirement, I would encourage you to look into the after_create callback in ActiveModel.
What you can do then in your Document Model is
after_create :create_emp_documents
def create_emp_documents
Employeee.all.each do |employee|
self.empdocuments.create(employee_id: employee.id)
end
end
A basic overview of my app. There is currently two models. A jobs model and a clients model. Both models have a has_and_belongs_to_many relationship as I intended to allow the user to create a client entry and then assign them one or many jobs.
Here are both of my models.
Clients -
class Client < ActiveRecord::Base
has_and_belongs_to_many :job
end
Jobs -
class Job < ActiveRecord::Base
has_and_belongs_to_many :client
end
I have been doing some research and I think im right in thinking that the relationship needs a foreign key to function so have added a client_id column & a job_id column to my database.
The clients page is currently working and here is my controller for that.
class ClientsController < ApplicationController
def index
#clients = Client.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #clients }
end
end
# GET /Clients/1
# GET /Clients/1.json
def show
#clients = Client.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #clients }
end
end
# GET /Clients/new
# GET /Clients/new.json
def new
#clients = Client.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #clients }
end
end
# GET /Clients/1/edit
def edit
#clients = Client.find(params[:id])
end
def create
#clients = Client.new(params[:client])
respond_to do |format|
if #clients.save
format.html { redirect_to #clients, notice: 'Client was successfully created.' }
format.json { render json: #clients, status: :created, location: #clients }
else
format.html { render action: "new" }
format.json { render json: #clients.errors, status: :unprocessable_entity }
end
end
end
# PUT /Clients/1
# PUT /Clients/1.json
def update
#clients = Client.find(params[:id])
respond_to do |format|
if #clients.update_attributes(params[:client])
format.html { redirect_to #clients, notice: 'Client was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #clients.errors, status: :unprocessable_entity }
end
end
end
# DELETE /Clients/1
# DELETE /Clients/1.json
def destroy
#clients = Client.find(params[:id])
#clients.destroy
respond_to do |format|
format.html { redirect_to :clients , notice: 'Client was successfully removed.'}
format.json { head :no_content }
end
end
def details
#clients = Client.find_by_id(params[:id])
#jobs = Client.job
end
end
And here's what I currently have for my jobs controller.
class JobsController < ApplicationController
def index
#jobs = Job.find(:all)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #job }
end
end
def new
#jobs = Job.new
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #job }
end
end
def create
#jobs = Job.new(params[:job])
#cients = Client.find = Client.find(params[:id])
respond_to do |format|
if #jobs.save
format.html { redirect_to(#jobs,
:notice => 'Job was successfully created.') }
format.xml { render :xml => #jobs,
:status => :created, :location => #Job }
else
format.html { render :action => "new" }
format.xml { render :xml => #jobs.errors,
:status => :unprocessable_entity }
end
end
end
end
In my jobs form I was given thr following code which added a drop down with all the created clients.
<%= select("job", "client_id", Client.all.collect {|c| [ c.name, c.id ] }, {:include_blank => 'None'})%>
When I press save though. I recieve the following error.
unknown attribute: client_id
Application Trace | Framework Trace | Full Trace
app/controllers/jobs_controller.rb:22:in `new'
app/controllers/jobs_controller.rb:22:in `create'
I assume this is because I need to define a way of finding the client_id in my job creation as well as specifying one in my client creation.
This is my first rails app though so im not quite sure how.
Any help would be greatly appreciated.
Your jobs table doesn't have a client_id, nor should it. You need to create a junction table to facilitate a many-to-many relationship. It should be called clients_jobs and contain an integer client_id and job_id.
There is a lot more wrong here. Here are just the things I caught at a casual glance:
This line:
#cients = Client.find = Client.find(params[:id])
should be:
#cients = Client.find(params[:id])
Pluralization is important in Rails. A client doesn't have many "job". It has many jobs. Your models should reflect this:
class Client < ActiveRecord::Base
has_and_belongs_to_many :jobs
end
class Job < ActiveRecord::Base
has_and_belongs_to_many :clients
end
You'll need to create a junction table via a migration, which is where your foreign keys will exist:
$ rails g migration AddClientsJobsTable
In index and new, you first create #jobs = Job.new and then you render it via :xml => #job. Again, pluralization is important. You need #job = Job.new. You have the same problem in create, except you've dropped the 's' and capitalized the 'J': :location => #Job } You can't do that in programming. Case and spelling both matter.
Job.find(:all) or Client.all: Pick one. Don't mix find :all and .all.
#clients = Client.find(params[:id]). You're finding a single specific Client, not a collection of clients. Your variable should be called #client. This is not an error, but it is seriously ugly.
pluralize your jobs and clients in your associations. I.E
has_many_and_belongs_to :jobs
has_many_and_belongs_to :clients
And if you do not use the alternative to this many-to-many associations with the ActiveRecord :through method (the alternative to HMABT) You must create the join table yourself which is a table of job_id's and client_id's.
I am using:
Rails 2.3.5
Ruby 1.8.7
Windows 7 Home basic 64-bit
I'm trying to use a database I acquired using mysqldump, and create functions ADD, EDIT, and DELETE to go with it. Now, when I'm creating the edit function, and i'm using its primary key (productCode) as a parameter, i get this error:
ActiveRecord::RecordNotFound in PosController#edit
Couldn't find Product without an ID
App Trace:
C:/Ruby187/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:1567:in find_from_ids'
C:/Ruby187/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:616:infind'
C:/Users/Aldrin/Documents/Trabaho!/sites/dbSample/app/controllers/pos_controller.rb:13:in `edit'
here's my code:
def edit
#product = Product.find(params[:ProductCode])
end
def update
#product = product.find(params[:ProductCode])
if session[:user_id]
#log = "Welcome Administrator!"
#logout="logout"
else
#log = "Admin Log in"
#logout=""
end
respond_to do |format|
if #product.update_attributes(params[:product])
flash[:notice] = 'product was successfully updated.'
format.html { redirect_to(#product) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #product.errors, :status => :unprocessable_entity }
end
end
end
I don't have an :id column in my database.
If productCode is the primary key in the table then you should tell rails to use it instead of id
class Product << ActiveRecord::Base
self.primary_key = 'productCode'
end
That way standard find calls will work, and you won't need to overwrite methods like to_param as rails will already have done it for you
def edit
#product = Product.find(params[:id])
end
def update
#product = Product.find(params[:id])
..............................
end
EDIT
def to_param
"#{product_code}"
end
def edit
#product = Product.find_by_product_code(params[:id])
end
def update
#product = Product.find_by_product_code(params[:id])
..............................
end
I'm using Rails 3 for this one. I've got a collections model, a user model and an intermediate subscription model. This way a user can subscribe to multiple collections, with a particular role. However, I don't want a user to be able to subscribe to the same collection twice.
So in my Subscription model I've got something like:
validate :subscription_duplicates
def subscription_duplicates
self.errors.add_to_base "This user is already subscribed" if Subscription.where(:user_id => self.user.id, :collection_id => self.collection.id)
end
However this seems ugly. Also, it breaks when I want to do something like the following in my collection controller:
def create
#collection = Collection.new(params[:collection])
#collection.subscriptions.build(:user => current_user, :role => Subscription::ROLES['owner'])
#collection.save
respond_with(#collection)
end
When I do the build the subscription does not have an id so I get a "Called id for nil" error.
Thanks for any guidance!
use validates_uniqueness_of
validates_uniqueness_of :user_id, :scope => :collection_id
First of all, your create action should always test if the object was saved, and if not then handle that (usually by re-rendering the new/edit page and showing the errors to the user).
A standard sort of create action would look like this (for a #post in this case):
def create
#post = Post.new(params[:post])
#created = #post.save
respond_to do |format|
if #created
flash[:notice] = 'Post was successfully created.'
format.html { redirect_to #post }
format.xml { render :xml => #post, :status => :created, :location => #post }
format.js
else
format.html { render :action => :new } #or edit or wherever you got here from
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
format.js
end
end
end
Shingara's approach to avoiding duplicates should work fine for you.
This is something I've been stuck on for a while now, and I have to apologize in advance for going into so much detail for such a simple problem. I just want to make it clear what I'm trying to do here.
Scenario
So, there's a model Foo, each Foo can either be red, green, or blue. Having URLs like /reds to list all red objects, and /reds/some-red-object to show a certain object. In that "show" view, there should be next/previous links, that would essentially "find the next RedFoo in alphabetical order, and once at the last RedFoo, the next record should be the first GreenFoo, continuing in alphabetical order, and so on".
I've tried implementing this in a couple of ways and mostly ended up at a roadblock somewhere. I did get it working for the most part with single table inheritance though, having something like this:
class Foo < ActiveRecord::Base
class RedFoo < Foo
class GreenFoo < Foo
class BlueFoo < Foo
Each subclass's models and controllers are identical, just replace the model names. So the controllers look something like:
class RedFoosController < ApplicationController
def index
#foos = RedFoo.find(:all, :order => "title ASC")
respond_to do |format|
format.html { render :template => 'foos/index'}
format.xml { render :xml => #foos }
end
end
def show
#foo = RedFoo.find(params[:id])
respond_to do |format|
format.html { render :template => 'foos/show'}
format.xml { render :xml => #foo }
end
end
def new
#foo = RedFoo.new
respond_to do |format|
format.html { render :template => 'foos/new'}
format.xml { render :xml => #foo }
end
end
def edit
#foo = RedFoo.find(params[:id])
respond_to do |format|
format.html { render :template => 'foos/edit'}
end
end
def create
#foo = RedFoo.new(params[:foo])
respond_to do |format|
if #foo.save
flash[:notice] = 'Foo was successfully created.'
format.html { redirect_to(#foo) }
format.xml { render :xml => #foo, :status => :created, :location => #foo }
else
format.html { render :action => "new" }
format.xml { render :xml => #foo.errors, :status => :unprocessable_entity }
end
end
end
def update
#foo = RedFoo.find(params[:id])
respond_to do |format|
if #foo.update_attributes(params[:foo])
flash[:notice] = 'Foo was successfully updated.'
format.html { redirect_to(#foo) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #foo.errors, :status => :unprocessable_entity }
end
end
end
def destroy
#foo = RedFoo.find(params[:id])
#foo.destroy
respond_to do |format|
format.html { redirect_to(foos_url) }
format.xml { head :ok }
end
end
end
The models only contain methods for next/previous, which work fine, surprisingly.
class RedFoo < Foo
def next
if self == RedFoo.find(:all, :order => "title ASC").last
GreenFoo.find(:all, :order => "title ASC").first
else
RedFoo.find(:first, :conditions => ["title > ?", self.title], :order => "title ASC")
end
end
def previous
if self == RedFoo.find(:all, :order => "title ASC").first
BlueFoo.find(:all, :order => "title ASC").last
else
RedFoo.find(:first, :conditions => ["title < ?", self.title], :order => "title DESC")
end
end
end
Problem
For whatever reason when I try to create and edit records, none of the attributes get saved in the database. It simply adds a new record with completely empty columns, regardless of what's filled in the form. No errors get returned in the script/server output or in the log files. From the script/console however, everything works perfectly fine. I can create new records and update their attributes no problem.
It's also quite a bad code smell that I have a lot of code duplication in my controllers/models (they're using the same views as the base model, so that's fine though). But I think that's unavoidable here unless I use some meta-goodness.
Any advice or suggestions about tackling this record saving issue would be great, but the reason I posted my setup in detail is because I have a feeling I'm probably going about this whole thing the wrong way. So, I'm open to other approaches if you know of something more practical than using STI. Thanks.
Update
The parameters hash looks about right:
{"commit"=>"Create", "authenticity_token"=>"+aOA6bBSrZP2B6jsDMnKTU+DIAIkhc8fqoSicVxRJls=", "red_foo"=>{"title"=>"Hello world!"}}
But #foo.inspect returns the following RedFoo object (all nil, except for type):
#<RedFoo id: nil, title: nil, type: "RedFoo", created_at: nil, updated_at: nil>
Problem is the params
:red_foo
is the name of the params in the view, whereas you use
params[:foo]
in the controller, I think the best way would be to be use :foo, in the view by using text_field_tag rather than any (what i assume can be) form builders text_field.
You can get out of the controller smell by using a module to do the basic crud stuff, since i assume most of the new/create/edit/update/destroy stuff is the same
OR
you could map all the routes to a foo controller and use some sort of parameter either passed in from the route, or through URI analysis to get the red/green/blue foo
Please take a look at the section called "Single table inheritance" on this page and let us know if it solves your problem.
Must admit, the way I go about STI is to use set_table_name inside a model.
e.g.
class RedFoo < AR::Base
set_table_name "foos"
include FooModule
extend FooClassModule # for self methods
def next; ...; end
end
But anyway, for this situation, what does your logger say when you do a #foo.inspect just before a save, and also what is the SQL that is ran on insert/update?
Right, so #foo.inspect gives you "nil" in the log?
What I mean (if I wasn't clear enough) was:
def create
#foo = RedFoo.new(params[:foo])
logger.error "******************* foo: #{#foo.inspect} **************"
respond_to do |format|
if #foo.save
...
if you do that and tail -f your log you can easily find out what is happening to foo and compare that to the incoming params hash
Infact, that would also be some useful information to have, what is the params hash?