Rails model passes validation but view still renders errors? - ruby-on-rails

Oddly enough, my model passes validation just fine, and acts as expected, however I still have the error rendered to the view.
# Controller
def up
#vote = Vote.create :vote => true, :voter => current_user, :voteable => Recipe.find(params[:id])
respond_to do |format|
format.js { render :json => {:model => 'vote', :success => #vote.valid?, :errors => #vote.errors }}
end
#vote.errors.clear # <= doesn't seem to help
end
The model I wrote has a custom validation:
class Vote < ActiveRecord::Base
# ... associations etc.
validate :voter_voting_too_frequently?
private
def voter_voting_too_frequently?
last_vote_cast_by_voter = Vote.find_last_by_voter_id self.voter
unless last_vote_cast_by_voter.nil? || last_vote_cast_by_voter.created_at < 5.seconds.ago
errors.add_to_base("You can only vote every 5 seconds.")
end
end
end
And lastly, the response that is rendered to the view: (returned as js no doubt, but would be the same if it were in a <div>)
{"errors":[["base","You can only vote every 5 seconds."]],"model":"vote","success":false}
And even though it was successful, this is continuously returned.
Ideas on how to debug this?

The issue was totally bizarre, and was related to render => :json. Strangely the :success key couldn’t be first in the hash, when the respond_to block renders json.
This was the only change that needed to be changed:
format.js { render :json => {:model => 'vote', :errors => #vote.errors.on_base, :success => #vote.errors.on_base.nil?} }
Also I'm logging a ticket with the rails team.

It doesn't look like it is passing the validation since the success status is false.
If you take a look at you custom validation it seems that the "unless" keyword has caused some confusion.
Try:
if !last_vote_cast_by_voter.nil? && last_vote_cast_by_voter.created_at < 5.seconds.ago
errors.add_to_base ...

I think your unless statement is wrong. Personally I am not used to them, so I always rewrite to if statements
if last_vote_cast_by_voter.nil? == false && last_vote_cast_by_voter.created_at > 5.seconds.ago
Looking at this line, I would suspect that last_vote_cast_by_voter.created_at should be lower than 5 seconds, therefore I suppose your unless statement should be changed into
unless last_vote_cast_by_voter.nil? || last_vote_cast_by_voter.created_at > 5.seconds.ago
Adding #vote.errors.clear indeed does not help, since the view is rendered at the point already...
If this still is not working try to write a test where you cast some votes. The time between two votes should be 1 second and 10 seconds for example.
Check if Vote.find_last_by_voter_id is working properly.
If all these test are working and rendering your view is not, then something strange is going on in your view and you should post some more information about your view, I guess.

Related

Rails: Conditional Validation & Views

What I'm thinking right now is...
I have a library full of books (entries). Each book has many checkouts (embedded document).
What I think I want to do is, upon checkout, make a new "checkout" as an embedded document. Upon checkin, I want to edit the checkout and add a "date_checked_out" field...
The issue is, my current model/controller makes a new entry each time there is a checkin or checkout...so it's doubly redundant...
What's the best way to go about this? Need more detail?
Checkout Controller:
def new
#entry = Entry.find(params[:entry_id])
#checkout = #entry.checkout.new
respond_to do |format|
format.html {render :layout => false}
end
end
def create
#entry = Entry.find(params[:entry_id])
#entry.update_attributes(:checked_out => "Out")
#checkout = #entry.checkout.create!(params[:checkout])
redirect_to "/", :notice => "Book Checked Out!"
end
class Checkout
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::MultiParameterAttributes
field :checkout_date, :type => Time
field :checkout_date_due, :type => Time
field :book_in, :type => Time, :default => Time.now
field :book_out, :type => Time, :default => Time.now
embedded_in :entries, :inverse_of => :entries
end
It makes sense the checkout would have a start and stop date. Do you need to make a checkout when a checkin occurs? You may be able to change this to an 'update' instead of a 'create' on the checkout controller - enter a checked_in_at on update.
Specifically - you'd want to be able to accept a PUT on the checkout controller - this could either be generic (allowing you to update the checkout in many ways) or specific, make a route that cleans up this for you:
resources :checkouts do
put :checkin, :on => :member
end
in checkouts_controller
def checkin
#checkout = Checkout.find(params[:id]
#checkout.update_attribute(:checked_in_at, Time.now)
# handle issues, redirect, etc.
end
Keeping it pure REST, add an update action to your Checkout controller.
Also, post your entry model. I'm assuming from your code that an entry has_one checkout, and a checkout belongs to an entry.
Something like:
*Edit because it appears OP wants to see how this works while checking for a conditional
... original boilerplate code ommitted
def update
#entry = Entry.find(params[:entry_id])
# if the book is checked out
if #entry.checked_out == "out"
# then update it
#entry.update_attributes(:checked_out => "whatever" # though I'd seriously consider changing checked out to a boolean - if it's either out or in, true or false makes sense. Ignore this advice if there are more than two states
#checkout = #entry.checkout
respond_to do |format|
if #checkout.update_attributes(:checked_out => "newValue")
...
else
.. handle errors
end
end
else
#the book does not have the correct status
respond_to do |format|
format.html { redirect_to some_action_path, :notice => "Entry is not out, so we cannot update its status." }
format.json { render json: #entry.errors, status: :unprocessible_entry }
end
end
end
Also, if you want to make the code a bit more explicit, you might consider taking swards advice and creating a few named endpoints like
def checkout
end
def checkin
end
I think that makes sense, in that someone else reading the code can very easily know exactly what that controller action is doing, as opposed to create and update.

Rails ActiveRecord relations - avoiding writing .blank? checks

This is more a style question than anything.
When writing queries, I always find myself checking if the result of the query is blank, and it seems - I dunno, overly verbose or wrong in some way.
EX.
def some_action
#product = Product.where(:name => params[:name]).first
end
if there is no product with the name = params[:name], I get a nil value that breaks things.
I've taken to then writing something like this
def some_action
product = Product.where(:name -> params[:name])
#product = product if !product.blank?
end
Is there a more succinct way of handling nil and blank values? This becomes more of a headache when things rely on other relationships
EX.
def some_action
#order = Order.where(:id => params[:id]).first
# if order doesn't exist, I get a nil value, and I'll get an error in my app
if !#order.nil?
#products_on_sale = #order.products.where(:on_sale => true).all
end
end
Basically, is there something I haven;t yet learned that makes dealing with nil, blank and potentially view breaking instance variables more efficient?
Thanks
If its just style related, I'd look at Rails' Object#try method or perhaps consider something like andand.
Using your example, try:
def some_action
#order = Order.where(:id => params[:id]).first
#products_on_sale = #order.try(:where, {:onsale => true}).try(:all)
end
or using andand:
def some_action
#order = Order.where(:id => params[:id]).first
#products_on_sale = #order.andand.where(:onsale => true).andand.all
end
Well even if you go around "nil breaking things" in your controller, you'll still have that issue in your views. It is much easier to have one if statement in your controller and redirect view to "not found" page rather than having several ifs in your views.
Alternatively you could add this
protected
def rescue_not_found
render :template => 'application/not_found', :status => :not_found
end
to your application_controller. See more here: https://ariejan.net/2011/10/14/rails-3-customized-exception-handling

Rails inherited resources usage

I'm using Inherited Resources for my Rails 2.3 web service app.
It's a great library which is part of Rails 3.
I'm trying to figure out the best practice for outputting the result.
class Api::ItemsController < InheritedResources::Base
respond_to :xml, :json
def create
#error = nil
#error = not_authorized if !#user
#error = not_enough_data("item") if params[:item].nil?
#item = Item.new(params[:item])
#item.user_id = #user.id
if !#item.save
#error = validation_error(#item.errors)
end
if !#error.nil?
respond_with(#error)
else
respond_with(#swarm)
end
end
end
It works well when the request is successful. However, when there's any error, I get a "Template is missing" error. #error is basically a hash of message and status, e.g. {:message => "Not authorized", :status => 401}. It seems respond_with only calls to_xml or to_json with the particular model the controller is associated with.
What is an elegant way to handle this?
I want to avoid creating a template file for each action and each format (create.xml.erb and create.json.erb in this case)
Basically I want:
/create.json [POST] => {"name": "my name", "id":1} # when successful
/create.json [POST] => {"message" => "Not authorized", "status" => 401} # when not authorized
Thanks in advance.
Few things before we start:
First off. This is Ruby. You know there's an unless command. You can stop doing if !
Also, you don't have to do the double negative of if !*.nil? – Do if *.present?
You do not need to initiate a variable by making it nil. Unless you are setting it in a before_chain, which you would just be overwriting it in future calls anyway.
What you will want to do is use the render :json method. Check the API but it looks something like this:
render :json => { :success => true, :user => #user.to_json(:only => [:name]) }
authorization should be implemented as callback (before_filter), and rest of code should be removed and used as inherited. Only output should be parametrized.Too many custom code here...

How to create a full Audit log in Rails for every table?

We recently began a compliance push at our company and are required to keep a full history of changes to our data which is currently managed in a Rails application. We've been given the OK to simply push something descriptive for every action to a log file, which is a fairly unobtrusive way to go.
My inclination is to do something like this in ApplicationController:
around_filter :set_logger_username
def set_logger_username
Thread.current["username"] = current_user.login || "guest"
yield
Thread.current["username"] = nil
end
Then create an observer that looks something like this:
class AuditObserver < ActiveRecord::Observer
observe ... #all models that need to be observed
def after_create(auditable)
AUDIT_LOG.info "[#{username}][ADD][#{auditable.class.name}][#{auditable.id}]:#{auditable.inspect}"
end
def before_update(auditable)
AUDIT_LOG.info "[#{username}][MOD][#{auditable.class.name}][#{auditable.id}]:#{auditable.changed.inspect}"
end
def before_destroy(auditable)
AUDIT_LOG.info "[#{username}][DEL][#{auditable.class.name}][#{auditable.id}]:#{auditable.inspect}"
end
def username
(Thread.current['username'] || "UNKNOWN").ljust(30)
end
end
and in general this works great, but it fails when using the "magic" <association>_ids method that is tacked to has_many :through => associations.
For instance:
# model
class MyModel
has_many :runway_models, :dependent => :destroy
has_many :runways, :through => :runway_models
end
#controller
class MyModelController < ApplicationController
# ...
# params => {:my_model => {:runways_ids => ['1', '2', '3', '5', '8']}}
def update
respond_to do |format|
if #my_model.update_attributes(params[:my_model])
flash[:notice] = 'My Model was successfully updated.'
format.html { redirect_to(#my_model) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #my_model.errors, :status => :unprocessable_entity }
end
end
end
# ...
end
This will end up triggering the after_create when new Runway records are associated, but will not trigger the before_destroy when a RunwayModel is deleted.
My question is...
Is there a way to make it work so that it will observe those changes (and/or potentially other deletes)?
Is there a better solution that is still relatively unobtrusive?
I had a similar requirement on a recent project. I ended using the acts_as_audited gem, and it worked great for us.
In my application controller I have line like the following
audit RunWay,RunWayModel,OtherModelName
and it takes care of all the magic, it also keeps a log of all the changes that were made and who made them-- its pretty slick.
Hope it helps
Use the Vestal versions plugin for this:
Refer to this screen cast for more details. Look at the similar question answered here recently.
Vestal versions plugin is the most active plugin and it only stores delta. The delta belonging to different models are stored in one table.
class User < ActiveRecord::Base
versioned
end
# following lines of code is from the readme
>> u = User.create(:first_name => "Steve", :last_name => "Richert")
=> #<User first_name: "Steve", last_name: "Richert">
>> u.version
=> 1
>> u.update_attribute(:first_name, "Stephen")
=> true
>> u.name
=> "Stephen Richert"
>> u.version
=> 2
>> u.revert_to(10.seconds.ago)
=> 1
>> u.name
=> "Steve Richert"
>> u.version
=> 1
>> u.save
=> true
>> u.version
=> 3
Added this monkey-patch to our lib/core_extensions.rb
ActiveRecord::Associations::HasManyThroughAssociation.class_eval do
def delete_records(records)
klass = #reflection.through_reflection.klass
records.each do |associate|
klass.destroy_all(construct_join_attributes(associate))
end
end
end
It is a performance hit(!), but satisfies the requirement and considering the fact that this destroy_all doesn't get called often, it works for our needs--though I am going to check out acts_as_versioned and acts_as_audited
You could also use something like acts_as_versioned http://github.com/technoweenie/acts_as_versioned
It versions your table records and creates a copy every time something changes (like in a wiki for instance)
This would be easier to audit (show diffs in an interface etc) than a log file

When creating an object in Ruby on Rails, which method of saving do you prefer, and why?

When writing the "create" method for an object in a Ruby on Rails app, I have used two methods. I would like to use one method for the sake of cleaner and more consistent code. I will list the two methods below. Does anyone know if one is better than the other? If so, why?
Method 1:
def create1
# is this unsecure? should we grab user_id from the session
params[:venue]['user_id'] = params[:user_id]
begin
venue = Venue.create(params[:venue])
#user_venues = #user.venues
render :partial => 'venue_select_box', :success => true, :status => :ok
rescue ActiveRecord::RecordInvalid
render :text => 'Put errors in here', :success => false, :status => :unprocessable_entity
end
end
Method 2:
def create2
# is this unsecure? should we grab user_id from the session
params[:venue]['user_id'] = params[:user_id]
venue = Venue.new(params[:venue])
if venue.save
#user_venues = #user.venues
render :partial => 'venue_select_box', :success => true, :status => :ok
else
render :text => 'Put errors in here', :success => false, :status => :unprocessable_entity
end
end
class VenuesController < ApplicationController
def create
#venue = #user.venues.create!(params[:venue])
render :partial => 'venue_select_box', :success => true, :status => :ok
end
rescue_from ActiveRecord::RecordInvalid do
render :text => 'Put errors in here', :success => false, :status => :unprocessable_entity
end
end
Using #user.venues in this way ensure that the user ID will always be set appropriately. In addition, ActiveRecord will protect the :user_id field from assignment during the course of the #create! call. Hence, attacks from the outside will not be able to modify :user_id.
In your tests, you may verify that doing a POST to :create raises an ActiveRecord::RecordInvalid exception.
I'm of the school of thought that exceptions shouldn't be used for routine conditions, so I'd say the second is better.
It depends. If you expect all of the create statements to work, use the former, because the failure to create-and-save is exceptional, and may possibly be a condition from which the program can't readily recover. Also, if you use relational integrity (foreign_key_migrations by RedHill Consulting), this will throw exceptions on foreign key violations, so you probably want to catch them whenever creating or updating.
The second is workable, and good if the query not succeeding is something you expect as part of the day-to-day operation of that particular action.
Also, your code comment about session being insecure -- the session is the place to put the user_id. As long as you're checking to verify that the user has been authenticated before doing anything else, you'll be okay.
I totally agree with Don's comment. But I would even go one step further with the user_id part and set it as a before filter on the model.

Resources