I am an experienced PHP developer but new to RoR and trying to understand how every thing works. I know how to use flash hash i.e in my action method, I'll set
flash[:notice] = 'some message'
and in my view, i'll display that.
This mechanism is also implemented in Yii framework. I understand how it works there. The thing that I don't understand is how it actually works here in RoR. flash is just a local variable then how can I access it in my views?
flash is actually a method. It's not in your controller, but the Rails controller delegates it to the request object. So the flash method is defined in the request object, but you can access it from your controllers, and from the view.
Check the link to the ActionDispatch::Request code. The flash is actually stored inside the session when set. The next time the user requests a page, the flash is accessible for use in the views.
In your view you can just access it like this:
<%= flash[:notice] %>
Or in a aesthetically more pleasant way (this can only be done with notice and alert since they're so frequently used):
<%= flash.notice %>
See the documentation for more information.
'flash' is not a local variable. It is part of session like 'session' hash. What this implies is that session hashes (flash, session, cookies) are shared values between ActionController::Base and ActionView::Base. Therefore session hashes are accessible from controllers and from views.In order to access flash in views, just use it as you would use it in controller. Referring to your earlier code , you would print notice like this:
<% if flash[:notice] %>
<p><%= flash[:notice] %></p>
<% end %>
For further reference on this topic please checkout: guides
The flash hash is basically a variable which is populated with each controller/action request, and then is reset after the request has been performed. As Adilbiy Kanzitdinov has mentioned - it's set in the session hash :
The flash provides a way to pass temporary objects between actions.
Anything you place in the flash will be exposed to the very next
action and then cleared out. This is a great way of doing notices and
alerts, such as a create action that sets flash[:notice] = "Post
successfully created" before redirecting to a display action that can
then expose the flash to its template. Actually, that exposure is
automatically done.
You need to remember that ROR is full-stack, meaning it has a bunch of middleware it uses to create the most efficient responses for your users. This allows you to set local variables (session, params, flash are 3 I can think of)
To call from a view, you just need to reference this local variable, as below:
<%= flash[:key] %>
Related
I've got a number of security concerns about my current application and wondering if I am leaving myself open to abuse, in the following arenas.
a) .My main access control method is by maining a current_user, current_company current_project method in my application controller. These methods return object based on stored session keys established when a user logs in and cleared when they log out. I.e if I want to know something about the current user, I can call "current_user.role" or if I want see whether the account a user is trying to change belongs to him, I check whether the associated account id which is requested in the url actually belongs to that user, essentially as follows
in Account controller
def account_info
redirect_to login_path if !user.logged_in
account_id=params[:account_id]
#account = Account.find(account_id)
unless account_belongs_to_user(account_id)
redirect_to unauthorized_path
end
end
In my application controller, when a user is initially authenticated, I do something like this:
session[:current_user_id] = user.id
and clear that session key when the user logs out.
Then when account is requested, and account_belongs_to_user is called, the application controller processes it, more or less like this:
def account_belongs_to_user(account_id)
account = Account.find(account_id)
return account.user_id==session[:current_user_id]
end
So I guess my security scheme ultimately relies on whether the session data is secure and not trivially spoofable.
b) When I render pages I sometimes pass objects which have senstive data to my erb pages to generate the page text.
For example, I might pass a "company" object (ActiveRecord) to the view to generate an invoice screen. But the company object, passed as #company, has a lot of sensitive data like access keys and the like. Not really being fully aware of the the internals, if I don't specifically include something like:
<%= #company.access_token %>
on my web page, can I be confident that the attributes of #company won't somehow be passed into the browser unless I specifically ask for them to be rendered on the page?
This is obviously an issue when using rails to serve data for say, AngularJS single page applications, as everything I pass for Angular to render the page I assume is probably accessible to an evil-doer even if not on the page itself, but I'm hoping that's not the case with pages generated server side by rails.
This may be a naive question, but thanks as I just want to be certain what I am doing before start spilling secrets all over the place.
put an authentication for the token using active_record callback
https://guides.rubyonrails.org/active_record_callbacks.html
I should start of by saying that this started from helpful messages like the one you get when you set up Devise (as of v. 2.2.3):
Ensure you have flash messages in app/views/layouts/application.html.erb
For example:
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
There are at least three ways you can get information into a view from a controller. You can put things in the session, put then in the flash, or set an instance variable in the controller. I see examples where both the flash and the session are accessed with a bare variable in the view, which opens up the possibility of a collision. For example, I could have a controller like this:
class PerverseController < ApplicationController
def index
#msg = 'Message 1'
session[:msg] = 'Message 2'
# Note - this is where a real concern comes in, the previous page could
# have done anything! Though here I use flash.now to illustrate the point.
flash.now[:msg] = 'Message 3'
end
Then, if I had the following in a view:
<%= msg %>
It would seem that any of those three values might be referenced. In fact, code like the above in a view is an error for me (on Rails 3.2). You need to actually use '#' or 'flash' or 'session' explicitly.
I can't find any clear documentation on this - books and tutorials mostly reference the flash and the session as an afterthought, so I imagine it might change in future versions. But, I'd love to see a pointer to references, and a suggestion of what I should do to avoid breakage in the future while keeping my code as clear / concise as possible.
And for now, I want to make sure I integrate properly with Devise.
I'm having an issue in Rails 3 where the flash hash seems to be returning things one request too early. That is, it seems to return things upon rendering that were set in that very same request. For example, consider a controller action that does:
add_warning "Danger, will robinson."
In my ApplicationController, I have:
before_filter :set_errors
#...
def set_errors
flash[:errors] ||= []
flash[:warnings] ||= []
flash[:notices] ||= []
end
#...
def add_warning(msg)
flash[:warnings] << msg
end
And my application.html.erb layout template has
<% flash[:warnings].each do |msg| %>
<div class="warnings"><%= msg %></div>
<% end %>
Based on what I'm understanding from the Rails guide, the flash contents shouldn't be rendered in this same request unless I'm using flash.now. And, if I have a redirect_to, they SHOULD be rendered in that second request. But they don't show up at all when the redirect_to happens.
flash.now need when you can't lost params[] for example: user filling the form for new post and have mistake in title for example (action new in Posts controller), he push submit and data send to create action, we have params[:post][:title] but if it's not valid and after we redirect to new action we already haven't params[:post][:title] and we can't fill field and user lost all filled data and user will be angry =) In this case, we use render and we still need to show warning, we use flash.now.
In other way we use just flash[] new action (1)-> create action (we set flash[] var) (2)-> index action we show flash[]. Like params in 1st example we lost flash[] after 2nd step.
Sorry for my pretty bad English, especially in long text.
flash and flash.now both render, but flash.now does so immediately, while plain old flash only renders after you redirect.
http://blog.vedanova.com/2010/09/29/rails-flash-now/
Based on your main question, it sounds like you want to use flash instead of flash.now
It turns out that the reason for the issue was the set_errors method, combined with my lack of understanding of how the flash hash works.
It seems that if a new value is not assigned to a key in flash, then the value of that key will be nil on the NEXT request. This may seem obvious, but the implications are subtle because I was assigning values to keys with ||= ("or-equals") on each request.
Consider the case where I have a series of requests to the same action, which don't ever call add_warning:
On the first request, flash is an empty hash. It contains no keys, so the ||= assigns some.
On the second request, flash is a hash with three keys, each value an empty array. Now, since each key has a value, the ||= does not assign anything. Which means that on the third request, rails has cleared those values and flash is an empty hash once again.
Now, consider this case:
On the first request, flash in an empty hash. It contains no keys, so the ||= assigns some.
On the second request, flash is a hash with three keys, each value an empty array. Now, an action calls add_error. However, an array already exists at flash[:warnings]. So, a value is pushed into that array. The contents of the array have changed, but the value of flash[:warnings] has not changed - it is still a reference to the same array. Hence, two implications:
The value of flash[:warnings] (i.e., the reference to an array) has not changed during this action, so because we've modified the same object that rails assigned to flash[:warnings] at the beginning of the request, it gets rendered with the newly pushed message at render time.
The value of flash[:warnings] (i.e., the reference to an array) has not changed during this action, so the action prompted by the next request will not have a :warnings key in flash at all.
So, in conclusion, the subtle point is that assigning a new value to a key in the flash hash is what causes it to be available in the following request.
EDIT: I guess it might help to post my solution, which is to eliminate the set_error and before_filter entirely, and instead to put the ||= [] fragment in the add_error method. That way, values are assigned to the flash hash only when they should be - when an error message is added.
I have a partial that is being shared between a few different views, and a mailer template. This partial should attempt to use the user's session to store some state information if possible.
Determining if the session exists seems to be a bit of a problem. Within the partial, calling defined?(session) always seems to yield true during a mail render (is this a bug?), but attempting to access "session" in any way yields an "undefined method" exception.
As of now, I'm having my mailer use a #for_mailer instance variable to signal this partial to render differently, but this doesn't seem very elegant. Is there some simple way for the partial to figure out whether or not it's being rendered by a mailer, as opposed to being rendered in the context of a web request?
I would also create two partials for this but here is an alternative solution as well.
Assuming that it is coming from a different controller and action, you could check the params[:controller] and params[:action].
If you end up doing this more than a few times, you will probably end up with more code than just rewriting the partial. What do you want to be different between the two presentations?
I am currently using a link_to helper in View to pass parameters like title , author ,image_url and isbn back to controller
<%= link_to 'Sell this item',new_item_path(:title => title, :author => authors, :image_url=>image, :image_url_s=>image_s, :isbn=>isbn, :isbn13=>isbn13 ) %>
Controller will then assign the parameters to an object to be used by a form in View later(in new.html.erb)
def new
#item = Item.new
#item.title = params[:title]
#item.author = params[:author]
#item.image_url = params[:image_url]
#item.image_url_s = params[:image_url_s]
#item.isbn = params[:isbn]
#item.isbn13 = params[:isbn13]
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #item }
end
end
new.html.erb will then be called.
This is all working fine but the url shows all the parameters
http://localhost:3000/items/new?author=Michael+Harvey&image_url=http://ecx.images-amazon.com/images/I/51vt1uVjvLL._SL160_.jpg&image_url_s=http://ecx.images-amazon.com/images/I/51vt1uVjvLL._SL75_.jpg&isbn13=9780307272508&isbn=0307272508&title=The+Third+Rail
Is there any way I can make the parameters not show up on the URL?
Maybe you could encode the parameters and decode them in the controller to deter users who may want to modify the url? Might be overkill but...
>> author=ActiveSupport::Base64.encode64("author=jim")
=> "YXV0aG9yPWppbQ==\n"
>> ActiveSupport::Base64.decode64(author)
=> "author=jim"
A POST can be used to move the parameters out of the URL and into the request, but this is not the "correct" or best practice. HTTP standards are such that non-GET requests are meant to be used only for requests that change state on the server. This is why you get a warning when you refresh a page that was generated in response to a POST.
There is nothing wrong with having parameters in the URL. So much focus should not be made on what appears to the URL bar, let alone what's after the ?. If however you have some need (i.e. insistence of a client) to remove them, you have several options, two of which John mentions.
I'm assuming your "new" action is REST-style, in that it's generating a form that would have to be submitted to change state on the server. Therefore your options might be:
Use POST, even though it's not standard compliant. Not recommended.
Use AJAX GET. This requires javascript, and ajax handling does add requirements such as the use of a JS framework and testing.
Use GET (or POST), but capture the parameters and store them, the redirect the user back to another clean URL that displays those stored value. You could store those in the session hash, or create a database record of them. Actually you really should use POST in this case, since you are effectively changing state on the server by storing those parameters. In this case, if the user refreshes the page he is directed to, those parameters will be preserved. This effectively removes the browser warning on refresh, something I can certainly appreciate.
There are two options that I can see and both involve JavaScript:
Have the link populate hidden form fields for the parameters and then submit the form using an HTTP POST request
Have the link submit an AJAX request to the controller action (using an HTTP GET unless clicking the link changes server-side state, in which case a POST should be used)
I think I would go with the second approach.
Why not write them to the session? It looks like you might have less than 4k in data there. Just remember to wipe it.