Rails ArgumentError in OrdersController#new validation - ruby-on-rails

Second question on here, I'd really like to solve this one myself but I just don't know where to start to debug it.
So here is my error in the browser (which occurs when I go to check out and enter my details in order/_form.html.erb)
ArgumentError in OrdersController#new
You need to supply at least one validation
Rails.root: C:/Users/Ruby/rails_practice/depot4
Application Trace | Framework Trace | Full Trace
app/models/payment_type.rb:6:in <class:PaymentType>'
app/models/payment_type.rb:1:in'
app/models/order.rb:7:in <class:Order>'
app/models/order.rb:1:in'
app/controllers/orders_controller.rb:1:in `'
And here is my def new in OrdersController:
def new
#cart = current_cart
if #cart.line_items.empty?
redirect_to store_url, :notice => "Your cart is empty"
return
end
#hide_checkout_button = true
#order = Order.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #order }
end
end
The thing is that I haven't touch def new, I've been working on def create, which is here:
def create
#order = Order.new(params[:order])
#order.add_line_items_from_cart(current_cart)
#cart = current_cart
#hide_checkout_button = true
pay_type = PaymentType.find( :conditions => ['pay_type = ?', #order.pay_type] )
#order.payment_type_id = pay_type.id
respond_to do |format|
if #order.save
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
format.html { redirect_to(store_url, :notice => 'Thank you for your order.') }
format.json { render json: #order, status: :created, location: #order }
else
format.html { render action: "new" }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
What I am trying to do there is create an order which is which belongs_to a payment_type and has_many line_items which belongs_to a cart.
Incidentally, I am also trying to hide_checkout_button with an instance variable when I am on the order page.
The Orders table has a foreign key to the PaymentTypes table and I am trying to find the correct id from this PaymentTypes table for the payment_type submitted by the user.
If I comment out these two lines:
pay_type = PaymentType.find( :conditions => ['pay_type = ?', #order.pay_type] )
#order.payment_type_id = pay_type.id
Sometimes I get a different error:
NoMethodError in OrdersController#new
undefined method `key?' for nil:NilClass
I think this is to do with incorrect caching in the browser but I'm not sure what the connection is.
I will update with the rest after I post this first
Part deux
I know that this is about validation, but I can't see what I am doing wrong... order.rb:
class Order < ActiveRecord::Base
attr_accessible :address, :email, :name, :pay_type, :payment_type_id, :cart_id,
:product_id
has_many :line_items, :dependent => :destroy
belongs_to :payment_type
PAYMENT_TYPES = PaymentType.pluck(:pay_type)
validates :name, :address, :email, :pay_type, :presence => true
validates :pay_type, :inclusion => PAYMENT_TYPES
And then you've got the other side of that belongs_to in payment_type.rb
class PaymentType < ActiveRecord::Base
attr_accessible :pay_type
has_many :orders
validates ***:pay_type,*** :uniqueness
end
I know that I am totally just confusing things but I have one fail in the functionals tests and one error that has something to do with updating an order but I don't know what yet. I am going to work on them to see if by solving them I inadvertently solve this weird error.
If anyone can give me tips on hacking and debugging in rails that would be great. I would love to be able to solve this without typing all of this in here.
I don't think the server trace gives any more information than the browser window in this case but if you need it, or anything else please ask.
UPDATE:
So my problem is that I know how to solve it with a global variable in payment_type.rb, but this means that I have one column of payment types in the Orders table and another of names and payment_type_ids in another column, which is the foreign key.
Since I have the foreign key I shouldn't need a specific column for payment_types in the Orders table. I should just be able to see the value from the PaymentType table in the Orders view.
How do you do this without a Global variable?
UPDATE deux:
Ok, so I never posted this before (from orders_form.html.erb):
26: <div class="field">
27: <%= f.label :pay_type %><br />
28: <%= f.select :pay_type, PaymentType::PAYMENT_TYPES,
29: :prompt => 'Select a payment method' %>
30: </div>
31: <div class="actions">
So I've tried to select for :pay_type in Orders but given options from :pay_type in PaymentTypes.
I can't imagine that matters does it? Seems to be where my problem lies, but can't be sure.

I think the syntax of your validate inclusion of is wrong. It should be something like:
validates :pay_type, :inclusion => { :in => PAYMENT_TYPES }

Related

How to validate foreign keys in Rails validations?

In my Rails app I have users who can have many projects which in turn can have many invoices.
How can I make sure that a user can only create an invoice for one of his projects and not for another user's projects?
class Invoice < ActiveRecord::Base
attr_accessible :number, :date, :project_id
validates :project_id, :presence => true,
:inclusion => { :in => ????????? }
end
Thanks for any help.
class InvoicesController < ApplicationController
def new
#invoice = current_user.invoices.build(:project_id => params[:project_id])
end
def create
#invoice = current_user.invoices.build(params[:invoice])
if #invoice.save
flash[:success] = "Invoice saved."
redirect_to edit_invoice_path(#invoice)
else
render :new
end
end
end
I think that shouldn't be on a validation. You should ensure the project the user selected is one his projects.
You could do something on your controller like:
project = current_user.projects.find params[:project_id]
#invoice = Invoice.new(project: project)
# ...
Your create action could look something like this.
def create
#invoice = current_user.invoices.build(params[:invoice])
#invoice.project = current_user.projects.find params[:invoice][:project_id]
if #invoice.save
flash[:success] = "Invoice saved."
redirect_to edit_invoice_path(#invoice)
else
render :new
end
end
project_id is "sensitive" attribute - so remove it from attr_accessible. You are right that you should not believe params from the form and you must check it.
def create
#invoice = current_user.invoices.build(params[:invoice])
# #invoice.project_id is nil now because this attr not in attr_accessible list
#invoice.project_id = params[:invoice][:project_id] if current_user.project_ids.include?(params[:invoice][:project_id])
if #invoice.save
flash[:success] = "Invoice saved."
redirect_to edit_invoice_path(#invoice)
else
render :new
end
end
If user tries to hack your app and change project_id to not owned value then method create render partial new with invalid #invoice. Do not forget to leave the validation of project_id on presence.
If you get exception Can't mass-assign protected attributes... there are several ways what to do. The simplest ways are:
1. remove line from environment configs (development, test, production)
# Raise exception on mass assignment protection for Active Record models
config.active_record.mass_assignment_sanitizer = :strict
2. Reject sensitive parameters from params before assigning.
# changes in method create
def create
project_id = params[:invoice].delete(:project_id)
#invoice = current_user.invoices.build(params[:invoice])
#invoice.project_id = project_id if current_user.project_ids.include?(project_id)
...
end
OK, luckily I managed to come up with a solution of my own this time.
I didn't make any changes to my controller ("let's keep 'em skinny"), but added a validation method to my model instead:
class Invoice < ActiveRecord::Base
attr_accessible :number, :date, :project_id
validates :project_id, :presence => true,
:numericality => { :only_integer => true },
:inclusion => { :in => proc { |record| record.available_project_ids } }
def available_project_ids
user.project_ids
end
end
I am not sure if this is good or bad coding practice. Maybe someone can shed some light on this. But for the moment it seems pretty safe to me and I haven't been able to hack it in any way so far.

Subform for parent object

So I've been holding off putting a question on here because I don't want to bother the community with stupid questions, but I'm going to ask for help now anyway.
I'm quite new to Ruby on Rails, and as you've probably read from the title, I'm having trouble with my subform. More specifically, with assigning the parent object to a client object. I'm building a system for my work in where employees can register repairs (mobile phones) and keep track of them. I'm building the client object with #repair = Repair.new, which works fine, but when I try to set the Client with #repair = Client.new, the :client_id on the repair stays null.
Here's my repair.rb: (some fields are in Dutch, please ignore that)
class Repair < ActiveRecord::Base
attr_accessible :imei, :klantnaam, :telefoon, :intake, :branch_id, :id, :client_id
attr_accessible :merk, :type, :batterij, :lader, :headset, :batterijklep, :carkit, :schade_toestel, :schade_scherm, :bon, :datum_bon, :klacht, :prijsindicatie
belongs_to :branch
belongs_to :client
accepts_nested_attributes_for :client
end
client.rb:
class Client < ActiveRecord::Base
attr_accessible :email, :firstname, :lastname, :number, :phone, :postalcode
has_many :repairs
end
repairs_controller.rb: (I've left the irrelevant methods out, I was getting tired of the 4 spaces :P)
class RepairsController < ApplicationController
# GET /repairs/new
# GET /repairs/new.json
def new
#repair = Repair.new
#repair.client = Client.new
if request.remote_ip == "xx.xx.xx.xx"
#repair.branch = Branch.where(:name => "Xxxxxxx").first
end
#repair.intake = Time.now
respond_to do |format|
format.html # new.html.erb
format.json { render json: #repair }
end
end
# POST /repairs
# POST /repairs.json
def create
#repair = Repair.new(params[:repair])
respond_to do |format|
if #repair.save
format.html { redirect_to #repair, notice: 'Repair was successfully created.' }
format.json { render json: #repair, status: :created, location: #repair }
else
format.html { render action: "new" }
format.json { render json: #repair.errors, status: :unprocessable_entity }
end
end
end
end
And this is the JSON I get from /repair/new.json:
{"batterij":null,"batterijklep":null,"bon":null,"branch_id":null,"carkit":null,"client_id":null,"created_at":null,"datum_bon":null,"headset":null,"id":null,"imei":null,"intake":"2013-02-01T23:29:10Z","klacht":null,"klantnaam":null,"lader":null,"merk":null,"pickup":null,"prijsindicatie":null,"schade_scherm":null,"schade_toestel":null,"telefoon":null,"updated_at":null}
By the way, the branch assignment works flawlessly... (It's null now because I'm not on the IP I specified in the new method)
Please help me out... :-(
Robin
Solved it!!
The code above all works flawlessly, the problem was a <% instead of <%= in my view, which made my subform not show up. Duhh.

Ruby on rails 2 level model

I need some help creating a very simple forum in a existing model.
What I want in a Game page, have a mini forum, where is possible create some topics and some comments to this topics. In the beginning I'm only implement topics.
This is the error I have:
Mysql2::Error: Column 'user_id' cannot be null: INSERT INTO `topics` (`game_id`, `question`, `user_id`) VALUES (1, 'asd', NULL)
This is my main model:
game.rb
class Game < ActiveRecord::Base
attr_accessible :name
validates :user_id, presence: true
validates :name, presence: true, length: { maximum: 50 }
belongs_to :user
has_many :topics, dependent: :destroy
end
topic.rb
class Topic < ActiveRecord::Base
validates_presence_of :question
validates_presence_of :game_id
attr_accessible :question, :user_id
validates :question, length: {maximum: 50}, allow_blank: false
belongs_to :game
belongs_to :user
end
topic_controller.rb
def create
#game = Game.find(params[:game_id])
#topic = #game.topics.create(params[:topic])
#topic.user_id = current_user.id
respond_to do |format|
if #topic.save
format.html { redirect_to #game, notice: 'Topic was successfully created.' }
else
format.html { render action: "new" }
end
end
end
game/show.html.erb
<h2>Topics</h2>
<% #game.topics.each do |topic| %>
<p>
<b>Question:</b>
<%= topic.question %>
</p>
<% end %>
<h2>Add a topic:</h2>
<%= form_for([#game, #game.topics.build]) do |f| %>
<div class="field">
<%= f.label :question %><br />
<%= f.text_field :question %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Thanks ;)
I believe the issue you're experiencing is the difference between create and new in Rails.
Using new merely initializes the model, allowing you to save/validate later; using create will perform all of those steps in one command, causing the database row to be created.
So when you attempt to perform
#game.topics.create(params[:topic])
Rails attempts to create a Topic using params[:topic] and setting the game_id to #game.id, after which it immediately attempts to validate this new Topic that it created and save it to the database.
Potential options that you can consider:
1) Use #game.topics.new(params[:topic])
2) merge {:user_id => current_user.id} in: #game.topics.create(params[:topic].merge({:user_id => current_user.id})
I'd personally suggest option 1 (i.e. using new instead), but I have seen option 2 used before.
EDIT: Another issue that it looks as though you might be experiencing: should current_user be #current_user in your code?
Side note:
generally, if create fails to create database row, it will still work (returning the initialized model instead), but in your case it looks like this won't happen due to database-level restrictions on user_id being NOT NULL, causing an uncaught error.
You may want to consider reading the Rails Guide on nested resources. I've been where you are now , take a look at this discusion.
I guess you're accessing the site without being logged in, so user_id will not be set. You should ensure that there is a logged in user for all actions that are modifying or creating a topic. A simple approach can be found in this Railscast.
I think current_user.id is not setting properly, Do inspect these issue are almost all the other issues, ruby debugger is the beset way
in your GEM file add
GEM file
gem 'debugger'
run bundle install
then in your controller
def create
#game = Game.find(params[:game_id])
#topic = #game.topics.create(params[:topic])
#topic.user_id = current_user.id
debugger
respond_to do |format|
if #topic.save
format.html { redirect_to #game, notice: 'Topic was successfully created.' }
else
format.html { render action: "new" }
end
end
end
and this will stop you in the the debugger line, then from the console you could see if the values are set or not. check this for more

Agile Web Development w/ Rails (4th e) Cart Issue

I'm working through Agile Web Dev w/ Rails book (4th ed) and I'm totally stuck...
I'm running Rails 3.2.3 on Mac OSX.
Im on task D-3: Adding a Button.... it started with the test:functionals not working at the end of the chapter... it gave me an error saying:
Can't Mass assign protected attributes: product
I followed the advice given here: http://forums.pragprog.com/forums/148/topics/10565
and changed my line of code in the Line_Items_Controller to
#line_item = #cart.line_items.build
#line_item.product = product
Here is what my current Line_Items_Controller create method looks like:
# POST /line_items
def create
#cart = current_cart
product = Product.find(params[:product_id])
#line_item = #cart.line_items.build
#line_item.product = product
respond_to do |format|
if #line_item.save
format.html { redirect_to(#line_item.cart,
:notice => 'Line item was successfully created.') }
format.xml { render :xml => #line_item,
:status => :created, :location => #line_item }
else
format.html { render :action => "new" }
format.xml { render :xml => #line_item.errors,
:status => :unprocessable_entity }
end
end
end
Now I am getting this weird message:
NoMethodError in LineItemsController#create undefined method `product=' for <LineItem:0x000001024f7fb0>
Here is my LineItem model
class LineItem < ActiveRecord::Base
attr_accessible :cart_id, :product_id, :product
end
Im not really sure what to do at this point, since I'm a total Rails (& Ruby) newb.
Can anyone point me in the right direction?
Changing the original line of code
#line_item = #cart.line_items.build(product: product) to #line_item = #cart.line_items.build(:product_id => product.id) in line_items_controller.rb solved this problem for me.
In my case there is :product_id in LineItem attr_accessible:
attr_accessible :cart_id, :product_id
So i changed :product => product in build's attributes to :product_id => product.id and it works.
#line_item = #cart.line_items.build(:product_id => product.id)
if you want the example from the book to work exactly the way they typed it up, go to models/line_item.rb and add attr_accessible to look like this;
attr_accessible :cart_id, :product_id, :product
n'joy.
Your line item model doesn't have an ActiveRecord association for Product. I'm not sure what you're building but I assume you'll want:
#lineitem
belongs_to :produce
#product
has_many :line_items
You'll also need to add product_id to your line items table.

Rails Validation with ActiveModel

I've been going through the documentation for getting ActiveRecord validation working with ActiveModel. For some reason I am not seeing any validation results returned.
I have a set of models which instead of interfacing with ActiveRecord are interfacing through a custom API that will be sitting behind Rails.
The Model:
class ApiObject < ApiConnector
include ActiveModel::Validations
attr_accessor :fieldName
validates :fieldName, :presence => true
def save
#save method implementation
end
end
The Controller:
def create
#apiObject = ApiObject.new(params[:api_object])
respond_to do |format|
if #apiObject.save
format.html { redirect_to(#apiObject, :notice => 'User was successfully created.') }
format.xml { render :xml => #apiObject, :status => :created, :location => #apiObject }
else
format.html { render :action => "new" }
format.xml { render :xml => #apiObject.errors, :status => :unprocessable_entity }
end
end
end
The Form:
<%= form_for :api_object, :url => '/apiobjectcontroller/' do |f| %>
<%= f.label :fieldName, 'Field Name' %>
<%= f.text_field :fieldName %>
<%= f.submit 'Create'%>
<% end %>
I am following the code laid out here: Rails ActiveModel Validation
The method is correctly returning to the form because #apiObject.save is returning as false, but no validation response is coming back. I've checked the markup and the usual rails validation results are not returned. What am I missing?
I have similar code that works, but I have an initialize method in my classes. Perhaps your model should be:
class ApiObject < ApiConnector
include ActiveModel::Validations
validates :fieldName, :presence => true
attr_accessor :fieldName
def initialize(fieldName)
#first_name = fieldName
end
def save
return false unless valid?
# save method implementation to go here
# ...
true # if save successful, otherwise, false
end
end
If the above works, and you end up having a lot of attributes to assign in your initializer, then you could use this old trick:
def initialize(attributes = {})
attributes.each do |name, value|
instance_variable_set "##{name}", value
end
end
EDIT: Added a call to valid? in save implementation, so that errors collection will be filled out.
This should fully answer:
http://asciicasts.com/episodes/211-validations-in-rails-3
In a nutshell: create your form with an instance variable + add the necessary code to display your errors.

Resources