Each and every Ruby on Rails website typically has its own admin area. Ryan Bates (a well-known person in Rails world) says in one of his first railscasts that it is a good idea to have the same code base for admin and public area of the website. Another approach could take place, I think. And this is why:
- usually, admin area has more actions in controllers and more complex data output with variety of fields;
- public area usually has simpler output;
- having common code base for admin and public area leads to code complexity and increases the amount of access permission checks.
Thus, it is more sensible to separate admin area from public area. This way public area code will be simpler and cleaner and admin area code will do only what it's meant for. Of course, there are gems, such as activeadmin, rails_admin, etc. They allow to generate full-featured admin area. But implementation of your own custom admin area sometimes is more suitable, rather than customization of existing complex mechanism.
I'd like to tell you how to separate admin area from public area by example.
So, let's create a sample rails application:
rails new sample
You can also specify ruby version if you are using rvm
cd sample && rvm --create --ruby-version use ruby-2.0.0-p247
Let's remove turbolinks from Gemfile right away and run bundle install --without production
Turbolinks is a good idea
— shitdhhsays (@shitdhhsays) August 21, 2013
We should also remove //= require turbolinks
from app/assets/javascripts/application.js
And we will change assets structure for our needs:
app +-/assets +-/javascripts +-/admin - we will have client scripts for admin area here +-/application - we will have client scripts for public area here +-admin.js +-application.js +-/stylesheets +-/admin - we'll have stylesheets for admin area here +-/application - we'll have stylesheets for public area here +-admin.css.scss +-application.css.scss
And the contents of the main files are listed below:
# app/assets/javascripts/admin.js //= require jquery //= require jquery_ujs //= require_tree ./admin
# app/assets/javascripts/application.js //= require jquery //= require jquery_ujs //= require_tree ./application
# app/assets/stylesheets/admin.css.scss /* *= require_self *= require_tree ./admin */
# app/assets/stylesheets/application.css.scss /* *= require_self *= require_tree ./application */
We should specify some assets precomplication options to make it work in production:
# config/environments/production.rb config.assets.precompile += %w(admin.js admin.css)
Let's create admin layout now:
# app/views/layouts/admin.html.erb <!DOCTYPE html> <html> <head> <title>Sample</title> <%= stylesheet_link_tag "admin", media: "all" %> <%= javascript_include_tag "admin" %> <%= csrf_meta_tags %> </head> <body> <%= yield %> </body> </html>
You can implement it in haml if you wish. I just customized default Rails layout.
We are about to deal with controllers, views and models now and that is the most interesting part... What would we like to get in the end?
- Controllers should be in
app/controllers/admin
folder and should haveAdmin::
namespace. - Helpers should be in
app/helpers/admin
folder and also should haveAdmin::
namespace. - Resource routes in
routes.rb
should be nested withing:admin
namespace. - Views should be in subfolders of
app/views/admin
folder. - Models should not have Admin namespace and should be utilized without it everywhere.
- Redirects in controllers should point to admin pages, e.g.
redirect_to [:admin, @post]
. - Edit form should be submitted to a proper path within admin area, so we should use
form_for [:admin, @post]
. - All links should lead to admin area sections, so we should use
[:admin, @post]
,admin_posts_path
,edit_admin_post_path(@post)
andnew_admin_post_path
in views and controllers. - Tests should also take into account all those peculiarities, use
Admin
namespace and proper route paths where it is neccessary.
Standard Ruby on Rails scaffold generators can't do that. They will either create a model with namespace or will use incorrect paths and will put files to the wrong places. I found only one single article which describes the process of generating of such admin structure by hand. I wanted to share my own more simple way to prepare the structure of admin area described above. But while I was writing this post I decided to make my own generators for admin area scaffolding and I created a gem then. This is how rails-admin-scaffold gem was born. I hope it'll be useful for someone, though it is still in progress and works only with Rails 4.
This gem automates all those manual actions described above. rails-admin-scaffold gem also supports the following at the moment:
- Rails 4 only;
- test::unit tests generating;
- assets are generated in admin subfolders;
- it supports jbuilder;
- it supports haml.
I plan to implement rspec, minitest and probably formtastic/simple_form support.
It is really simple to use:
- Add
gem 'rails-admin-scaffold', 'x.x.x'
to yourGemfile
- Run
bundle install
- Now you can scaffold admin controllers, views, etc with something like:
bin/rails g admin:scaffold_controller Post title:string content:text published:boolean
It doesn't generate models, but the fields specified are used in controller.
I should note, that it makes sense to create a separate base controller class AdminController which is inherited from ApplicationController and has some authentication filters, layout, etc configured. All controllers in admin area could be easily inherited from it. Maybe I'll add this option to rails-admin-scaffold gem soon.
Rails 4 has a lot of great new features to make writing example blog applications even easier than before.
— shitdhhsays (@shitdhhsays) March 25, 2013
I'll appreciate your feedback.
Useful links:
- Icebergist blog post «RESTful admin namespaced controller using scaffolding»;
- Railscast «Where administration goes» from Ryan Bates;
- Articles at Everyday Rails
- David Eisinger's article «Rails Admin Interface Generators».