RESTful Rails: extending scaffold_resource generator

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 first 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 profile 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 User and UsersController or AdminUser and 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 files which is tedious and error-prone.

‘There must be a better way’ we said to ourselves and indeed there is: write your own scaffold_resource generator.

What to do?

Basically, we want to generate User model and standard RESTful UserController with

script/generate scaffold_resource user some:fields...

and then RESTful AdminUserController with:

script/generate better_scaffold_resource user admin_user

The second call skips User model generation, because it was already created by the first 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.

Preparation

First, I strongly suggest that you work on a copy instead of messing with standard Rails files, 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 files 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 first success, but wait — there will be more of them.

Patching & editing

Having copied our generator, we can start patching the files. First comes scaffold_resource_generator.rb:

$ cd better_scaffold_resource/ 
$ mv scaffold_resource_generator.rb better_scaffold_resource_generator.rb

We have to rename it to fit the name of the generator. Then, fire up your favorite text editor and change the following:

Line 1: change class name to BetterScaffoldResourceGenerator

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 files: they use wrong functions for paths and urls (e.g. users_path instead of admin_users_path, new_user_url instead of new_admin_user_url) and a couple of minor problems (member variables being named @user instead of @admin_user).

We decided to fix 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 find 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 find some other tool or editor that allows find & replace in multiple files and change all the files in templates directory by hand. If you don’t know sed syntax, s/some_text/other_text/ changes some_text to other_text.

Now, we can run our generator like this:

script/generate better_scaffold_resource thing admin_things

and enjoy our third success today. All the files 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.

ToDo list

Some things left to do:

  • correcting above mentioned minor problems
  • convert it to plugin so we don’t have to manually copy the files 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.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: