The scaffold_resource generator is quite cool, but lacks one feature that my team found quite useful. Sometimes we want to generate RESTful scaffold with different names for model and controller.
Why would we want that? Well, mostly because in some cases we want to have two separate controllers for one model. The ﬁrst controller contains ordinary functionality and the second — stuff only an admin can use. We feel that this separation is helpful for maintenance.
For example, we want to have
User model class,
UsersController accessed as
/users/... with methods for signup, user proﬁle edition etc. and
AdminUsersController accessed as
/admin_users/... with administrative functionality like deleting, granting or revoking access rights etc.
Unfortunately, Rails’ standard
scaffold_resource generator only accepts model name and derives all other names from that. So it’s either
AdminUsersController. One possible solution is to generate it both ways, then delete
AdminUser and manually change
AdminUserController and its views to use
User instead. This requires editing several ﬁles which is tedious and error-prone.
‘There must be a better way’ we said to ourselves and indeed there is: write your own
What to do?
Basically, we want to generate
User model and standard RESTful
script/generate scaffold_resource user some:fields...
and then RESTful
script/generate better_scaffold_resource user admin_user
The second call skips
User model generation, because it was already created by the ﬁrst generator.
This is not as hard as it may seem. Especially when you’re not striving for perfection and can accept a ‘works-in-most-cases’ solution.
First, I strongly suggest that you work on a copy instead of messing with standard Rails ﬁles, so let’s prepare our playground. We’ll copy the
scaffold_resource generator and rename it to
better_scaffold_resource (feel free to use other name, I’m not perfectly sure if this is really better):
$ cd /usr/local/lib/ruby/gems/1.8/gems/ $ cd rails-1.2.3/lib/rails_generator/generators/components/ $ sudo cp -Rv scaffold_resource/ better_scaffold_resource/
Watch for version numbers in paths. If you use other versions of Ruby or Rails, change them appropriately. The libraries are normally owned by
root so you need
sudo to create new directory here. To avoid further
sudo‘s, I suggest changing ownership of the copied ﬁles like this:
$ sudo chown -Rv your_login:your_group better_scaffold_resource/
At this point, if you run Rails’
script/generator, you should see
better_scaffold_resource among built-in generators. I’d assume this to be our ﬁrst success, but wait — there will be more of them.
Patching & editing
Having copied our generator, we can start patching the ﬁles. First comes
$ cd better_scaffold_resource/ $ mv scaffold_resource_generator.rb better_scaffold_resource_generator.rb
We have to rename it to ﬁt the name of the generator. Then, ﬁre up your favorite text editor and change the following:
Line 1: change class name to
Line 16: remove this line and insert following three instead:
@controller_name = args.shift @controller_name ||= ActiveRecord::Base.pluralize_table_names ? @name.pluralize : @name
We can enjoy our second success now:
better_scaffold_resource basically works, i.e. generates appropriately named model, controller, tests and views. There is still one big problem with the generated ﬁles: they use wrong functions for paths and urls (e.g.
users_path instead of
new_user_url instead of
new_admin_user_url) and a couple of minor problems (member variables being named
@user instead of
We decided to ﬁx the big problem and leave the minor problems alone. Up until now, we didn’t notice any bad consequences of wrong variables’ names. I’ll post updates if I ﬁnd any other problems or bugs. Fixing the big problem requires unfortunately editing several templates in our generator. This is because templates use in some places names stripped of anything ending with underscore.
After some experimenting, tweaking the templates, generating and running tests I found that following changes are needed:
sed -i '' -e 's/singular_name %>_path/controller_file_path.singularize %>_path/' templates/* sed -i '' -e 's/plural_name %>_path/controller_file_path %>_path/' templates/* sed -i '' -e 's/file_name %>_path/controller_file_path.singularize %>_path/' templates/* sed -i '' -e 's/file_name %>_url/controller_file_path.singularize %>_url/' templates/* sed -i '' -e 's/table_name %>_url/controller_file_path %>_url/' templates/* sed -i '' -e 's/table_name %>_path/controller_file_path %>_path/' templates/*
If you don’t have
sed, you’ll have to ﬁnd some other tool or editor that allows ﬁnd & replace in multiple ﬁles and change all the ﬁles in
templates directory by hand. If you don’t know
Now, we can run our generator like this:
script/generate better_scaffold_resource thing admin_things
and enjoy our third success today. All the ﬁles should be generated correctly, all tests should run without errors (after you run rake
db:migrate, that is) and scaffolding should work in the browser.
Some things left to do:
- correcting above mentioned minor problems
- convert it to plugin so we don’t have to manually copy the ﬁles each time a new Rails version is installed
I’d be happy to hear if anyone used it and (hopefully) found useful. If you encounter any problems don’t hesitate reporting them.