I'd like my application to display different data on the frontpage depending on whether the user has been logged in or not.
def index
if current_user
# render another controllers action
else
# render another controllers action
end
end
I can achieve this by using render_component. However it has been obsolete for some time. Although I can still use it as a plugin, I'm interested if anyone has a better approach.
Just take in mind that rendering another controller's view directly is not an option.
Thanks.
Just use your index method as a public proxy to the specific view you want to render.
def index
if user?
logged_in
else
logged_out
end
end
private
def logged_in
# stuff
render :action => "logged_in"
end
def logged_out
# stuff
render :action => "logged_out"
end
If it is a relatively small subsection of data, I'd probably do that in a view helper.
Related
I want to have 2 kinds of views for the same posts in my rails application. For instance - in one where a logged in user can update and edit the post, and in the other any user can just view it and comment on it or select it.
How should I go about this? Do I need a separate class? I know I need a separate view for each, but how about the model and the controller?
1.case: your views are going to have similar content, but only the signed in users will have extra options like editing.
You should use a partial view and in your main view you should write something like this:
<% if signed_in? %>
<%= render 'edit_form' %>
<% end %>
Remember, the name of the partial should always start with a underscore, so your partial in this case would be called _edit_form.html.erb or _edit_form.html.haml, depending on what you are using.
2.case: depending on if the user is signed in or not, you want to render completely different views, then you should handle it in your controller:
def show
if signed_in?
render 'show_with_edit'
else
render 'show_without_edit`
end
end
And your files would be named show_with_edit.html.erb and show_without_edit.html.erb
Also, if your view for a signed in user was called show then you could just do this:
def show
render 'show_without_edit' unless signed_in?
end
3.case: if you want to change basically EVERYTHING depending if the user is signed in or not, you could create some custom methods and call them inside your original action like this:
def show
if singed_in?
show_signed_in
else
show_not_signed_in
end
end
private
def show_signed_in
# declaring some instance variables to use in the view..
render 'some_view'
end
def show_not_signed_in
# declaring some other instance variables to use in the view..
render 'some_other_view'
end
I'm experimenting with a few ways of doing this but just thought I'd ask the gallery.
I want the root page of my app (/) to be one of 2 things depending on whether a user is logged in or not.
If they are not logged in I want to dish up a static page with some static info and a login form.
If they are logged in I want them to be served up the main index page of my app (/invoices/).
I am using authlogic to manage user login.
Some of the ideas I've had for doing this seem unnecessarily convoluted (and this is Rails - it should be simple shouldn't it?) and I figure someone already knows a better way.
homepage_controller
def index
respond_to do |format|
if current_user
format.html { render :partial => 'view', :layout => 'nonuser' }
elsif !current_user
format.html { render :partial => 'view', :layout => 'user' }
end
end
Something like that?
I would add the following method definition to a controller to define a layout over there. If the behavior would be global then application_controller would be the place for this method definition.
layout :choose_layout
def choose_layout
if current_user
return 'user_view'
end
return 'non_user_view'
end
I think this answers your question: Protecting Content with AuthLogic
It is the cleanest way of what you are looking for, and the standard way.
I have a couple different user types (buyers, sellers, admins).
I'd like them all to have the same account_path URL, but to use a different action and view.
I'm trying something like this...
class AccountsController < ApplicationController
before_filter :render_by_user, :only => [:show]
def show
# see *_show below
end
def admin_show
...
end
def buyer_show
...
end
def client_show
...
end
end
This is how I defined render_by_user in ApplicationController...
def render_by_user
action = "#{current_user.class.to_s.downcase}_#{action_name}"
if self.respond_to?(action)
instance_variable_set("##{current_user.class.to_s.downcase}", current_user) # e.g. set #model to current_user
self.send(action)
else
flash[:error] ||= "You're not authorized to do that."
redirect_to root_path
end
end
It calls the correct *_show method in the controller. But still tries to render "show.html.erb" and doesn't look for the correct template I have in there named "admin_show.html.erb" "buyer_show.html.erb" etc.
I know I can just manually call render "admin_show" in each action but I thought there might be a cleaner way to do this all in the before filter.
Or has anyone else seen a plugin or more elegant way to break up actions & views by user type? Thanks!
Btw, I'm using Rails 3 (in case it makes a difference).
Depending on how different the view templates are, it might be beneficial to move some of this logic into the show template instead and do the switching there:
<% if current_user.is_a? Admin %>
<h1> Show Admin Stuff! </h1>
<% end %>
But to answer your question, you need to specify which template to render. This should work if you set up your controller's #action_name. You could do this in your render_by_user method instead of using a local action variable:
def render_by_user
self.action_name = "#{current_user.class.to_s.downcase}_#{self.action_name}"
if self.respond_to?(self.action_name)
instance_variable_set("##{current_user.class.to_s.downcase}", current_user) # e.g. set #model to current_user
self.send(self.action_name)
else
flash[:error] ||= "You're not authorized to do that."
redirect_to root_path
end
end
in my application, I have a "User" model. Each user can have multiple (email) addresses which are defined in the model "Address":
Class User < ActiveRecord::Base
has_many :addresses
def is_authorized(op)
# returns true or false
end
def is_owned_by(user)
# returns true or false
end
end
Class Address < ActiveRecord::Base
belongs_to :user
end
Inside the AddressController class, the currently logged in user is available in the "#user" instance variable. The controller prevents ordinary users from editing, deleting, viewing etc. addresses which don't belong to them - but he does allow an administrative user to edit those. The AddressController class can ask the AddressModel if the user currently logged in is performing normal or superuser operations.
This all works nicely and database updates are made as expected, however, I'd really like to have different HTML views depending on the mode of operation. I can only think of two ways to achieve that:
Make the mode of operation (normal/privileged) known in the AddressController class (using an instance variable, e.g. #privileged) and use an "if" statement in the view.
Use something like an "after_filter" in the address controller to render a different layout.
If it is possible to display the results of executing a single controller in two completely different layouts, depending on it's mode of operation, what is a good way to achieve that?
Thanks in advance
Stefan
You can specify which view to use to display the result of an action in the action itself. You can also specify which layout to use too. So, for example:
def my_action
if #user.is_authorised(...)
render :action => 'admin_action', :layout => 'admin'
else
render :action => 'non_admin_action', :layout => 'non_admin'
end
end
This will render either admin_action.html.erb or non_admin_action.html.erb depending on the returned value from is_authorised. The :layout option is, er, optional and refers a layout in views/layouts. There are various other options the render call which you can find in the documentation for render.
You can specify the layout of the view for that particular controller, or the whole application in the application controller by:
class SomeController < ApplicationController
layout :set_layout
def set_layout
#user.is_authorized(...) ? "privileged_layout" : "normal_layout"
end
...
end
You can try to figure it out here: http://guides.rubyonrails.org/layouts_and_rendering.html#using-render, under 2.2.12 Finding Layouts
Hope this helps =)
You can simply call the render method manually at the end of your controller action:
if #privileged
render :action => 'show_privileged'
else
render :action => 'show'
end
This will render app/views/myview/show_privileged.html.erb or app/views/myview/show.html.erb. Alternatively, you can use the :template option to give an explicit template file to the render method.
If this is the only controller in your app where you're if/else'ing all over the place that's probably fine. If you start doing this type of logic everywhere that should tell you that you're doing too much at once.
The answer you accepted (which is fine and works!) has a different layout and a different view, to me that says the controller is doing too much - I'd split this out into an admin controller.
You should put administrative actions in an an administrative namespace and restrict it there. Create a directory called admin in your controllers directory and add an _application_controller.rb_ in there:
class Admin::ApplicationController < ApplicationController
before_filter :check_authorized
private
def check_authorized?
if !logged_in? || !current_user.admin?
flash[:notice] = "You've been very bad. Go away.
redirect_to root_path
end
end
end
Now you can put controllers into this namespace and make them inherit from Admin::ApplicationController too.
Let's say I have a User model, and an Invoice model with a belongs_to :user association.
Now I'm creating a new action for my InvoicesController, and the view that will be rendered. The view will have a select-element for selecting the user that this invoice will belong to.
So I need to fetch those users somewhere; my instinct is to leave this kind of thing out of the view. I end up with this:
def new
#users = User.all
end
The form submit action is create. When the creation fails for some reason, I re-render the new action's view.
def create
invoice = Invoice.new params[:invoice]
if invoice.save
flash[:notice] = 'Invoice created!'
redirect_to :action => 'show', :id => invoice.id
else
#users = User.all
render :action => 'new'
end
end
But as you can see, in order the re-render the new action, I have to fetch the users again.
This is just an example, but consider that I have some forms with several select-elements filled from the database, or similar constructs. That turns into an awful lot of repetition.
So how should I structure my controller in this situation?
Should I simply use User.all from my view?
Should I call new from within create to do the fetching for me?
Or something else?
For this I'd use a before_filter. For example you'd do something like:
before_filter :fetch_all_users, :only => [:new, :create]
protected
def fetch_all_users
#users = User.all
end
For 90% of my controllers I use the inherited resources plugin. It cuts down the amount of controller code you need to write for CRUD controllers, which also means you can cut down on the amount of tests you need to write.
For me:
What's the rails way to load other models collections for new, edit update and create actions?
It's not a good approach for my situation. Where after ".save", I send redirect_to to an another action, if I use before_filter and ".save" returns true, the fetch_all_users is called unnecessary