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
Related
I have read some articles, and i know, that it's bad to inherit more than 2 level deep resources, but let's forget now about it.
let's imagine, i have such model:
car_brand
car_model
car_type
in route i could write something like this:
namespace :admin do
resources :car_brands do
resources :car_models do
resources :car_types
end
end
end
but i didn't find any good article, how to generate my controller's and view, with such schema,
what i need to write in controller class header, something like: class
Admin::CarBrands::CarModelsController < ApplicationController
or what? I need to clear understand this moment, each sub-model view must be in subfolder view, or how?
Admin::CarTypesController < ApplicationController in controllers/admin folder as car_types_controller.rb
Run rake routes and take a look at this line, for example:
/admin/car_brands/:car_brand_id/car_models/:car_model_id/car_types(.:format)
This is the uri pattern that will map Admin::CarTypesController#index metod. In params hash, you will find :car_brand_id and :car_model_id.
what i need to write in controller class header, something like: class
Admin::CarBrands::CarModelsController < ApplicationController
Partly yes and partly no. Yes, in that you've namespaced routes i.e. within namespace :admin and No, because nested resources do not mean namespaced Controllers as CarBrands::CarModelsController.
Since all resources are within the namespace admin, you'd generate all the controllers as follows:
rails g controller admin/car_brands
rails g controller admin/car_models
rails g controller admin/car_types
Executing each command above would place a controller class and view directory and other test specific files in their corresponding directories. Your question is more towards controllers and views so the directories of concerns are:
- app/controllers/admin/
- app/views/admin/car_brands/
- app/views/admin/car_models/
- app/views/admin/car_types/
Your controller declaration for CarBrandsController would then look like:
class Admin::CarBrandsController < ApplicationController
...
end
With these setup, it's now up to you how you want to manage each controller as a resource. If you nest your car_types within car_models then that the methods in car_models controller will also be expecting car_type_id in parameter. If you don't nest car_types resource then the resource is a standalone resource on it's own and does not have dependencies on any other resources.
So I want when I access: site.com/panel to look into /app/controller/panel/index_controller.rb
Before I start I'm new to ruby, I started a couple hours ago
So in my routes.rb I have this
namespace :panel do
root 'index#index'
resources :index
end
And I created a file called index_controller.rb in /app/controller/panel/index_controller.rb which looks like this
class IndexController < ApplicationController
def index
#foo = "Foo"
end
end
Now when I go to site.com/panel I get this: superclass mismatch for class IndexController
What I did wrong?
Also can I setup different views and layout here to use for the controllers inside /app/controller/panel/*_controller.rb
replace this
class IndexController < ApplicationController
with
class Panel::IndexController < ApplicationController
update:
to automatically generate namespaced controller you can use rails build in generator like this
rails g controller panel/users
this will generate Panel::Users < ApplicationController controller under app/controllers/panel/users_controller.rb
Since you've namespaced the index resource routes within panel, you'll need to prefix your IndexController declaration to reflect this:
# app/controllers/index_controller.rb
class Panel::IndexController < ApplicationController
Then, you can similarly reflect the namespace in your filesystem in order to get Rails to properly invoke the correct views:
/app/views/panel/index/index.html.erb
/app/views/panel/index/show.html.erb
... etc
A note: the Rails convention is that routes that are declared as resources should be named plural, as this denotes an entirely resourceful class. Thus, according to this paradigm, index should actually be indexes. However, I suspect you may mean to use a singular route, in which case the declaration would be as follows:
namespace :panel do
resource :index
end
Which creates the following singular routes (which may conform better to what you're trying to accomplish):
panel_index POST /panel/index(.:format) panel/indices#create
new_panel_index GET /panel/index/new(.:format) panel/indices#new
edit_panel_index GET /panel/index/edit(.:format) panel/indices#edit
GET /panel/index(.:format) panel/indices#show
PUT /panel/index(.:format) panel/indices#update
DELETE /panel/index(.:format) panel/indices#destroy
I'm having an error with my routes/resources and controllers.
I have the following in the routes.rb:
# routes.rb
resources :users do
resource :schedule
end
And I have a schedule_controller.rb inside controllers/users/ set up as I think it should be:
class Users::ScheduleController < ApplicationController
# Controller methods here...
end
Running a rake:routes shows
user_schedule POST /users/:user_id/schedule(.:format) schedules#create
new_user_schedule GET /users/:user_id/schedule/new(.:format) schedules#new
edit_user_schedule GET /users/:user_id/schedule/edit(.:format) schedules#edit
GET /users/:user_id/schedule(.:format) schedules#show
PUT /users/:user_id/schedule(.:format) schedules#update
However, navigating to /users/:user_id/schedule is returning the following error:
uninitialized constant SchedulesController
My only thoughts on what the problem could be are that is has something to do with nested resources or declaring a single resource and I'm going wrong somewhere.
I'm using the helper
new_user_schedule_path(current_user)
when linking to my 'new' view.
It should be SchedulesController, not Users::ScheduleController. Controllers should only be namespaced when the route is namespaced with namespace. Controller names should also always be plural.
What you're creating is a nested resource, not a namespaced one.
Is the namespacing of the SchedulesController intentional? i.e. do you really mean to do this?
class Users::SchedulesController < ApplicationController
Or are you only doing that because schedules are a "sub-thing" from users?
The reason I ask this is because typically within Rails, nested resource controllers aren't namespaced. You would only namespace a controller if you wanted to modify the controllers in a special way under a namespace. A common example of this would be having some controllers under an admin namespace, inheriting from a BaseController within that namespace that would restrict only admins from acessing those controllers.
Option 1
If you didn't intentionally namespace this controller, then you want to remove the Users:: prefix from your controller, and move it back to app/controllers/schedules_controller.rb, the helpers back to app/helpers/schedules_helper.rb and the views back to app/views/schedules. Perhaps you ran a generator which also generated a Users::Schedule model, which should also need to be renamed to Schedule and moved back to app/models/schedule.rb.
Option 2
If you did intentionally namespace this controller, then you want to do this in your routes:
namespace :users do
resources :schedules
end
Leave everything that's been generated as it should be.
In your routes.rb you need to specify the controller like this:
resources :users do
resource :schedules, controller: 'users/schedules'
end
replace resources :users to
namespace :users
Because your schedule controller is inside users folder.
class Users::ScheduleController < ApplicationController
# Controller methods here...
end
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.
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.