correct way to pass errors - ruby-on-rails

I have a form in a view of my project and
Im doing
redirect_to root_path(#locale), alert: #client.errors
and getting the errors in my view iterating with
flash[:alert].full_messages.each
but when there are a lot of errors rails launch the error
ActionDispatch::Cookies::CookieOverflow
what is the correct way to pass a lot of error descriptions in RoR?

Normally, the alert is a set length string - not something that can grow this long.
render :new, alert: "There has been an error"
When you render you can look for the errors in an instance object - in this case #client.errors.
The rails way is something like this in the view...
<% if #client.errors %>
<ul>
<% #client.errors.full_messages.each do |m| %>
<li><%= m %></li>
<% end %>
</ul>
<% end %>
Forgive my erb - I've been writing haml too long.
The main thing to take away from this is to render instead of redirect. Usually the page that's rendered is the same one that had the form. So, in the create method, you render new on error. In the update method, you render edit.

Personally, I think that the most correct way to pass error is to use a redirect_to only if you pass the validation, and a render if you don't (a simple test on the value of #object.update_attributes(hash)).
This way, you can directly access your object errors from inside your view (through #object.errors) without loading anything in the user cookies.

Related

What's the difference between the two ways of message rendering in Rails?

I copied an existing rails repo, but I found two ways of rendering the message to View files in different web pages.
This is the first one, which can render the "notice" or "alert" in the controller to the web page.
<% flash.each do |type, msg| %>
<div>
<%= msg %>
</div>
<% end %>
This is the second one, and I have no occasion to trigger it.
<% if #task.errors.present? %>
<ol>
<% #task.errors.each do |error, message| %>
<li>WARNING: <%= "#{error.capitalize} #{message}" %>
</li>
<% end %>
</ol>
<% end %>
I wonder what is the essential difference between them? Can they replace each other?
I wonder what is the essential difference between them?
These are two completely different concepts.
The first is iterating through the flash. This is basically just a simple cookie based store used to pass primatives such as strings, hashes or arrays to the next request. The flash is typically used when displaying a message to the user after redirecting.
For example:
def deny_access
flash[:alert] = "You must be signed in."
redirect_to new_session_path
end
You can also set flash messages for the current request by using flash.now. The flash is purged from old messages after every request. The flash is typcially used as a kind of system wide announcement system and displayed as a banner on the top of the page.
The second is iterating (badly) through the instance of ActiveModel::Errors attached to a specific instance of a model. This is a collection of validation errors that gets filled when .valid? is called (usually indirectly) on a model instance.
You use this when displaying the validation errors of a model in the current request cycle. Typically on the top of a form or inline by the corresponding input.
Since this data is just stored in memory in an instance variable it does not persist across redirects.
Can they replace each other?
No - not really. Both are used to provide user feedback but are very different tools to solve different jobs. Doesn't really stop some noobs from trying though.
See:
https://guides.rubyonrails.org/action_controller_overview.html#the-flash
https://guides.rubyonrails.org/active_record_validations.html

How to Handle Rails/Mongoid with no doc

When calling all posts for a user Posts.find(creator: current_user:_id), and the user hasn't made any...rails spits a "NoMethodError" for it...
What I want to do is have a pretty output for the user of "Why, no. You haven't posted anything, you lazy slob." instead of this scary error.
What's the best way to handle things like this?
You need to use where instead of find. By design, find method expect to actually find an existing thing you're looking for. Consult docs about querying here.
Also, you can try and use try method. Basically it's equal to the following:
object.try(:something_scary)
# is equal to
object && object.something_scary
This is how I handle nil entities. If you want to show some kind of message to user (about being slobby) you make a check inside of your template and render different partials. Example:
<% if #posts.present? %>
<%= render 'posts' %>
<% else %>
<%= render 'no_posts' %>
<% end %>
Then you can put your message inside of that no_posts partial.

RoR undefined method 'to_datetime' depending on order of helper methods

I am experiencing a very odd problem; I get this error
undefined method `to_datetime' for nil:NilClass"
when render 'modal' is before render 'post' in show.html.erb, but not if it is after:
<div class="page-header">
<h2>Indlæg af <%= #user.first_name %></h2>
</div>
<% if current_user == #user %>
<%= render 'modal' %>
<% end %>
<%= render 'post' %>
I have attached the views in the gist underneath:
Gist: https://gist.github.com/czepluch/8166841
It makes no sense to me that this error occurs depending on the order the renders are placed in. So I would really like to know why the order of the rendering of the helpers matter?
Please add code to the question so it is saved with the answers here. I suspect it has to do with the fact that your form has this:
simple_form_for([#user, #user.posts.build]
That means a new post is built that has no attributes, which could cause a nil error later on. reverse the order, and this isn't built until after the other code has run. The only thing I can see at first glance that is related to datetime would be:
time_ago_in_words(p.created_at)
If created_at is nil, because of an empty post, that could generate an error.
Play with the form declaration, you may be able to do
simple_form_for([#user, :post])
or something like that to get a form for a new post without actually attaching an empty object to the #user object.

How to bubble up exception in rails

New to rails and running into some issues around best practice error handling.
I have the following code in helper in the /lib folder which is used to apply a coupon to some pricing.
As you can see from code below i'm trying a few different ways to return any errors. Specifically, model.errors.add and flash[:error].
The code below is supposed to return a new price once a valid coupon has been applied. What i'm not sure about is what to return in the case of an error? I can't return false as I am below because this method should really be returning a numeric value, not a boolean. What is the best method to bubble up an error to the view from the lib/helper and stop execution?
def calc_coupon transaction_price, discount_code, current_tenant
coupon = Coupon.where(code: discount_code, tenant_id: current_tenant.id).first
if coupon.nil?
current_tenant.errors.add :base, "No such coupon exists"
return false
else
if coupon.maxed? || coupon.expired?
flash[:error] = "This coupon is no longer valid."
return false
else
transaction_price = coupon.calculate(transaction_price)
end
end
return transaction_price
end
I think you are on the right track but you are struggling because you are mixing view logic with business logic.
Forget the helper class and add the functionality to your model.
By adding the validations to the model rather than a helper class and adding the errors to base your controller action will be able to handle the errors when it tries to save/create the record in the normal way, thus "bubbling up the errors"
e.g.
class MyModel < ActiveRecord ...
validate :validate_coupon
protected
def validate_coupon
# add your validations here - call your boolean check which adds errors to the model
# if validations are ok then calculate your transaction price here
end
end
If you have your checks as public methods on your model then they can be called from anywhere, i.e. from within a view
so a public transaction_price method on your model that first checks for errors and sets the price could be used in a view like so
<%= #some_object.transaction_price %>
As I stated earlier because the validations are added in the model then your controller action just needs to check the value of the call to save and flash the errors as normal if it returns false so no need to do anything custom in your controller actions at all. Just the default scaffolded behaviour.
There are a mountain of validation options available to you. Have a look at
http://guides.rubyonrails.org/active_record_validations_callbacks.html
Hope that makes sense.
UPDATE IN RESPONSE TO COMMENT
1) You have that pretty much spot on. It is merely a suggestion that you could calculate the transaction price as part of the validation. If that doesn't suit your requirement then that's fine. I was just trying to give you the best fit for your question. The thing is that you should have two seperate instance methods then you can call them from wherever you wish which makes it a lot simpler for experimentation and moving stuff around. Go with your suggested solution It's good :)
2) Have a close look at a scaffolded controller and views. You will see how the saves/updates fail (They will fail automatically if you add errors as part of your validation process) using an if statement and you will see how the _form partial displays the errors
This is an example of an form that deals with engines in an app I am working on.
<%= form_for(#engine) do |f| %>
<% if #engine.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#engine.errors.count, "error") %> prohibited this engine from being saved:</h2>
<ul>
<% #engine.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Basically it is checking for errors and looping through them all if any exist to display them.
To handle flash messages you should utilise the applications main layout
The following code will display flash messages inside a div called "info" which you can style however you want
<% unless flash.empty? %>
<div class="info">
<%- flash.each do |name, msg| -%>
<%= content_tag :div, msg, :id => "flash_#{name}" %>
<%- end -%>
</div>
<% end %>
By placing that before the main yield in your application.html.erb in your views/layouts folder you can be sure that whatever page is rendered, flash messages will always be shown to the user
Of course if you are displaying flash messages then maybe you don;t need to display the errors. Sometimes both is the right thing to do and sometimes just one or the other is fine. That's up to you to decide on a case by case basis.
The main thing is that the receiving action for the HTTP POST or HTTP PUT (create or update action) handles the failure to save with or without a flash as you see fit
e.g.
A create action
def create
#engine = Engine.new(params[:engine])
respond_to do |format|
if #engine.save
format.html { redirect_to #engine, notice: 'Engine was successfully created.' }
format.json { render json: #engine, status: :created, location: #engine }
else
flash.now[:notice] = "Failed to save!" # flash.now because we are rendering. flash[:notice] if using a redirect
format.html { render action: "new" }
format.json { render json: #engine.errors, status: :unprocessable_entity }
end
end
end
Hope that clears things up
UPDATE 2
Forgot to mention that flash is just a hash and uses the session cookie so you can set any key you like
e.g.
flash.now[:fred]
This would enable you to style and handle a specific fred notification in a specific view if you wanted a different styling from that supplied by your application layout by adding the code to handle the :fred key (or name of your choice) in a specific view

Rails 3 / Controller / Flash hash

I want to be able to pass multiple messages to the flash hash, inside of my controller, and have them display nicely together, e.g., in a bulleted list. The way I've devised to do this is to create a helper function in my Application Controller, which formats an array into a bulleted list, which I then pass to, in my case, flash[:success]. This is clearly not the Rails Way because, i.a., my bulleted list gets encoded. That is, instead of getting:
Message 1
Message 2
I get:
<ul><li>Message 1</li><li>Message 2</li></ul>
I'm sure I could figure out a way to raw() the output, but isn't there a simple way to get something like this working? Perhaps there's an option to pass to flash[]? Something else?
I used render_to_string and a partial instead of a helper to achieve something similar.
# app/controller/dogs_controller.rb
def create
#dog = Dog.new(params[:dog])
#messages=[]
if #dog.save
#messages << "one"
#messages << "two"
flash[:notice] = render_to_string( :partial => "bulleted_flash")
redirect_to(dogs_path)
else
render :action => 'new
end
end
Then I format the array of flash messages in an HTML list
# app/views/dogs/_bulleted_flash.html.erb
<ol>
<% #messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ol>
Which produces the following HTML
# http://0.0.0.0:3000/dogs
<body>
<div id="flash_notice">
<ul>
<li>one</li>
<li>two</li>
</ul>
</div>
...
</body>
If you need to continue using a helper then I think you need to append the html_safe method to your string to prevent it from being encoded (which rails 3 does by default). Here is a question showing how to use html_safe in a similar fashion
If you are using Rails3, try the raw method.
raw(my_html_string)
And it won't escape the html. Oh, sorry, I just read your last sentence. Check out this information, "Rails 3 flash message problems", it looks like it may be what you are looking for:
http://www.ruby-forum.com/topic/215108
Usually I would ask for more information about your views and layouts in this situation, because scaffolding doesn't display flash[:success] by default.
The way I solve this is to totally redo my flash messages usually, by making the flash[:whatever] an array every time, and in my layout handling that array instead of just the notice. Remember that flash is just a Hash, you're just setting values.
However if you just want to do this with the setup you have now (helper putting the HTML inside the flash[:success]), you can change the way that the flash messages are displayed in your layout file. By default they are just use <%= flash[:success] %>, which automatically escapes HTML. To make it not do that for the flash messages, change it to <%=raw flash[:success] %>

Resources