rails 3: layout for namespaced routes - ruby-on-rails

I've created a number of controllers & views under an 'admin' namespace, but they are still pulling from the application layout. how can I make a layout that applies to all the views in the namespaced routes, and still use the current layout for the other pages?

I usually have a Base controller class in my namespace, and then have all controllers in that namespace inherit from it. That allows me to put common, namespace specific code in Base and all the controllers in that namespace can take advantage. For example:
class Admin::BaseController < ApplicationController
layout 'admin'
before_filter :require_admin_user
end
class Admin::WidgetsController < Admin::BaseController
# inherits the 'admin' layout and requires an admin user
end

Generally speaking, Rails will use the application layout if there isn't a layout that matches the controller. For example, if you had a PeopleController, Rails would look for layouts/people.html.erb and if it didn't find that, application.html.erb.
You can explicitly specify a specific layout if you want to override this convention.
class Admin::PeopleController
layout 'some_layout'
end
That controller will then use some_layout.html.erb rather than looking for people.html.erb and application.html.erb.
But this might be a better way if you're looking to group things: If you have a base AdminController that inherits from ApplicationController, you can have your, say, Admin::PersonController inherit from the AdminController and it will inherit the admin layout.
I don't know the specifics of your code, but you might have:
class AdminController
def show
#render a template linking to all the admin stuff
end
end
app/controllers/admin/people_controller.rb:
class Admin::PeopleController < AdminController
#your awesome restful actions in here!
end
views/layouts/admin.html.erb:
Hello from the Admin!
<%= yield %>
The one thing to realize is that Admin::PeopleController will inherit any actions that AdminController has defined (just as anything defined in ApplicationController becomes available in all sub-classes). This isn't generally a problem since you'll likely be overwriting the methods anyway, but just to be aware of it. If you don't have an AdminController, you can make one with no actions just for the purposes of the layout.

Related

Inherit from parent controller or use concerns?

I have five controllers that share common code. Is it best to let them inherit from a parent controller, or use concerns? For example:
class PostsController < ApplicationController
before_action :authenticate, :set_project
layout 'projects'
end
class CommentsController < ApplicationController
before_action :authenticate, :set_project
layout 'projects'
end
# three other controllers, etc...
I could let the controllers inherit from one controller that declares the before_actions and the layout, or I could stuff the common code into a concern.
What's the criteria from one choice or the other? Is it defined?
My rule of thumb is:
If the controllers share the same namespace in the URL (for example /projects/... or /admin/...), than I use inheritance from an Projects::BaseController or Admin::BaseController.
If they just share methods or declarations and do not share a namespace, than I use mixins.
And sometimes I prefer duplicated code. Because code in place is easier to understand than a mixin with a meaningless name. Do you have a good name for a concern that covers authentication and layout?
In most cases I use mixins for adding features and inheritance for customizing features.
e.g.:
If I need to override current_user I choose inheritance. If I got only some shared methods, then I choose mixins.

Error when removing '::Base' from ActionController

I'm writing a rails application, and in this one controller I need to specify the layout to render due to inheriting from ActionController::Base. In some of my other controllers I just have ActionController and it automatically uses the application layout. Whenever I remove the ::Base, I get the following message when accessing the page superclass must be a Class (Module given).
Why does inheriting from ActionController::Base in this controller matter, but not in the others?
To directly answer your question ActionController is not a controller class, it's a namespacing module that powers the entire controller stack. You would not interact with the ActionController module during typical Rails development. ActionController::Base is actually the class that controllers inherit from. This is why you can't inherit from ActionController.
But I think there are two controllers in play here. ActionController::Base and ApplicationController. I think you might be mistaking ApplicationController for being ActionController without the ::Base.
ActionController::Base is the main controller class where all of your Rails functionality comes from. ApplicationController is a generalized controller that you can add methods to and have all of your other Rails controllers inherit from.
You could do the following to use a different layout in one of your controllers:
class AuthenticationController < ApplicationController
layout 'authentication'
end
You can either use the AuthenticationController directly, or have new controllers inherit from AuthenticationController.
Your controllers should inherit from ApplicationController. This will allow them to automatically render the application layout. ApplicationController extends ActionController::Base.

Another section in Rails

I saw a lot of discussions about creating another section in Rails 3 but not a complete guide.
I would like to create another section for example
/admin/...
All my previous controllers inherits from
ApplicationController
and use
layout/application.html.erb
So now I want every controller that is places in the newly created /admin/... directory to inherit form a different BaseController and use a different layout than the application.html.erb. If that is possible can you provide a guide about which files has to be created in /admin/... which for layout and what I have to place in the route files??
Thanks in advance.
Create the admin directory under your controllers and then have an 'admin' controller (so they inherit the set layout - also useful for forcing authentication etc), eg
class Admin::AdminController < ApplicationController
layout 'admin/admin'
end
then have your other controllers in the admin directory extend off the admin controller eg
class Admin::CategoriesController < Admin::AdminController
def index
...
end
end
You'll need to create an admin folder under your layouts too and the admin.html.erb (or whatever templating engine you're using, layout can obviously be named whatever you like). Views also for the other admin controller methods will need to live under their respective admin folder, eg app/views/admin/categories/index.html.erb (second admin is the name of the controller
You'll also need to add the routes in your routes.rb - assuming Rails 3
namespace :admin do
root :to => 'admin#index' #default page when accessing /admin
resources :categories #whatever resources you want
...
end
you could add a base_controller.rb in your /admin/ and let your other controllers in /admin/ inherit from Admin::BaseController. Just include a < ApplicationController in your /admin/base_controller.rb.
Now specify the layout in your /admin/base_controller.rb.
For routing you only need to add references available to the generic public. Add a namespace for it:
namespace :admin do
resouces :xyz
end

Menu displayed on all pages, code is replicated in all controllers

I have a collection model. I succesfully created a _collection.html.erb that i call with <%= render #collections%> in my application layout.
My problem is that in ALL my controlers method I must add #collections = Collection.all
I found it very very ugly,it will make my collection scope a pain to change, and I'm sure that I am missing a rails magic something that would be way nicer.
Is there a way to have a part of the layout generated by model data without having a identical piece of code in AAAALLLLLL the controllers?
Notice that your controllers all inherit from ApplicationController. Use this to your advantage. Add a before_filter to ApplicationController that loads your collections.
#cam was right. Any rails project has an ApplicationController. Your controllers all start with MyController < ApplicationController, right? If so, that means you can create a before_filter in your ApplicationController, which will be inherited by all your controllers. To do so :
/app/controllers/application_controller.rb
before_filter :load_collection
def load_collection
#collections = Collection.all
end
From now on, you can use #collections from all your controllers (as long as they inherited from ApplicationController)

Should frontend and backend be handled by different controllers?

In my previous learning projects I always used a single controller, but now I wonder if that is good practice or even always possible.
In all RESTful Rails tutorials the controllers have a show, an edit and an index view. If an authorized user is logged on, the edit view becomes available and the index view shows additional data manipulation controls, like a delete button or a link to the edit view.
Now I have a Rails application which falls exactly into this pattern, but the index view is not reusable:
The normal user sees a flashy index page with lots of pictures, complex layout, no Javascript requirement, ...
The Admin user index has a completly different minimalistic design, jQuery table and lots of additional data, ...
Now I'm not sure how to handle this case. I can think of the following:
Single controller, single view: The view is split into two large blocks/partials using an if statement.
Single controller, two views: index and index_admin.
Two different controllers: BookController and BookAdminController
None of these solutions seems perfect, but for now I'm inclined to use the 3rd option.
What's the preferred way to do this?
I asked myself this question almost every time when I get a new project. I usually choose one of the two solutions:
1). Single Controller, Single View
I almost never choose this solution now a days unless the project is really simple, and only one or two types of users. If you get multiple user types it is better to use solution # 2. Although this solution may be appealing because you think you save yourself some time by writing less code, but in the end, your controller and view will grow in complexity. Not to mention all the edge cases you have to consider. This usually means bugs.
My company once had to rescue a failed project, it had 3 user types. (admin, business, and member). They used solution #1. The code was in a horrible condition, ( and that's why we were asked to rescue this project) We were jokingly saying it is not MVC, it was MMM. (Model-Model-Model) This is because business logic was not properly extracted and put into models, but spread in controllers and views as well.
2). Multiple Controller, Multiple Views
I use this solution more and more these days. I usually namespace the controllers with user types. For example:
In "app/controllers"
class BookController < ApplicationController
end
and in "app/controllers/admin"
class Admin::BookController < Admin::BaseController
end
I only need to consider regular users when I fill in BookController, and only need to consider admin users when I fill in Admin::BookController
I'm not sure if there are better ways, but this is what I learned from a dozen projects I've done so far...
What I do in such a situation changed quite a bit lately. The current approach is as follows:
I separate controllers based on access requirements. This gives me a clear
mental model and a very easy way to check for access control (and test it).
I even go as far as to separate 'your own access' to models into separate
controller. I also usually keep the controller's name just put it into a
separate namespace.
This approach is also makes it very easy to use standard restuful controller
implementations like InheritedResources.
Note that you can reuse many of the views if same functionality is required for
different kinds of access.
So I'd have something like this:
### lets start with routes
# this is basically guest access level. you can only list it and see details
map.resources :books, :only => [:index, :show]
namespace :my do |my|
# this will require at least login.
# index and show will be basically same as at the guest level. we can reuse the views
my.resources :books
end
namespace :admin do |admin|
# this will require admin login
admin.resources :books
end
# now the controllers
# this might be just enough of a controller code :). the rest goes into model.
class BooksController < InheritedResources::Base
actions :index, :show
end
module My
class BooksController < InheritedResources::Base
before_filter :require_user
protected
def begin_of_association_chain
# this will force resources to be found on current_user.books.
# so if you go to /my/books/123 and book 123 is not your book you will get 404
current_user
end
end
end
module Admin
class BooksController < InheritedResources::Base
before_filter :require_admin
# this controller is essentially unrestricted. you can get/edit/update any book
# and you can have separate view template for index too
end
end
## Views
# well, this is the part that I like the least in this approach, but
# I think the good outweight the bad.
# I see 2 ways to reuse the views w/o writing lots of ugly customization code for the InheritedResources
# 1) render 'parent' views inside templates. i.e. like:
# my/books/index.html.haml:
!= render :file => "/books/index"
# or just link the templates on the filesystem level with symlinks.
# (or if all of them are the same you can link the directory)
Use two current if there are two modules 1] Admin 2] User
Say
class BookUserController < ApplicationController
#Here layout is used will be of layouts/application.html.erb
#Here all the methods which user can will be present
end
class BookAdminController < ApplicationController
layout 'admin' #here we set the layout is layouts/admin.html.erb
end
IF Only one page you want to show admin you can use single controller and two methods
class BookUserController < ApplicationController
layout 'admin', :only=>['index_admin']
def index_admin
end
def index
end
end
OR
class BookUserController < ApplicationController
def index_admin
render :action=>'index_admin', :layout => false
end
def index
end
end
When I need a clearly separated administration area, I tend to go for a solution with two controllers for one resource. An Admin::BooksController in the admin/ sub directory for the admin interface with all actions and a public BooksController that only has index and show methods depending on needs.

Resources