#148 Custom App Generators (revised)
- Download:
- source codeProject Files in Zip (1.26 KB)
- mp4Full Size H.264 Video (29.9 MB)
- m4vSmaller H.264 Video (12.9 MB)
- webmFull Size VP8 Video (13.1 MB)
- ogvFull Size Theora Video (29.2 MB)
Lately there has been a lot of talk about the default Rails stack. David Heinemeier Hansson recently wrote a blog post about some of his ideas on providing defaults in Rails. If you prefer something outside the default stack that’s perfectly fine, for example it’s easy to use RSpec instead of Test::Unit, factories instead of fixtures or HAML as a template language and making these changes takes only a few minutes. If you create a lot of Rails applications, though, doing this every time can become tedious so in this episode we’ll show some ways to customize the default Rails generator to create applications the way you prefer.
Creating a Custom Template
There are many different options that we can pass in to the rails new
command to customize it. For example we can use -d
to specify a different database, using --skip-test-unit
to skip testing-related files or --skip-sprockets
if we don’t want to use the asset pipeline. If we want to use some of these options by default we can put them into a .railsrc
file in our home directory.
-d postgresql --skip-test-unit --skip-sprockets
Now when we create a new Rails application these options will be applied automatically. This is a new feature that was introduced in Rails 3.2 and we can see a list of the options that we can use by running rails new --help
.
If these options aren’t enough we can pass in a app template or builder to further customize the generator. We’ll take a look at these next, starting with a template. A template is just a Ruby script which is run in the context of the app generator. This means that any call to self
in the script is a reference to the generator and that we have all the Rails generator actions at our disposal, including Thor actions. We can call methods like remove_file
to remove any generated files that we don’t want or create_file
to create one. We can also set up gems in this file. Calling gem
will add a gem to the application’s gemfile and we can even specify which group it should appear in. We can even run Bundler from this file or trigger generators.
remove_file "README.doc" create_file "README.md", "TODO" gem "rspec-rails", group: [:test, :development] run "bundle install" generate "rspec:install"
This is great. We can now store in this file the tedious commands we usually use to set up our Rails app. If there are commands that we only want to trigger sometimes we can use conditions. For example the yes?
method asks the user if they want certain behaviour or we can use ask
to request input from the user. We’ll use these to ask the user if they want to generate a root controller and what they want it to be called. If they do we’ll create a controller based on the name they enter and add a route to make that controller’s index action the root. We’ll also remove the static index.html
file that’s generated, although from Rails 4.0 this won’t be necessary.
if yes? "Do you want to generate a root controller?" name = ask("What should it be called?").underscore generate :controller, "#{name} index" route "root to: '#{name}\#index'" remove_file "public/index.html" end
We can also trigger Git commands in this file so if we want to initialize a new Git repository and make an initial commit we can do so with the git
method.
git :init append_file ".gitignore", "config/database.yml" run "cp config/database.yml config/example_database.yml" git add: ".", commit: "-m 'initial commit'"
Here we initialize a new git repository, add the database configuration file to .gitignore
then copy it so that we have an example config file and finally add all the file and make an initial commit.
Our script is now looking quite good so let’s try it out. To do so we create a Rails application and use the -m
option, passing in the path to the template.
$ rails new blog -m app_template.rb
This will create our Rails application as normal and, after it has finished installing the app’s gems, it will install RSpec and set it up then ask us if we want to generate a root controller. We’ll say yes to this and ask it to generate a welcome
controller.
If we move into our new application’s directory and run git log
we’ll see that the directory is a Git repository and that the initial commit has been made. When we start the server and visit the root page for our application we’ll see that the WelcomeController
is set up as the root controller.
If you’re wondering what methods you can call in an application template take a look at the RailsGuide on generators which has a useful reference.
Custom Builders
We know now that a template is a great way to add additional behaviour to a newly-generated Rails application but what if we want to replace some of the behaviour while Rails is generating the initial application structure? This can be done by using a builder. Like a template a builder is just a Ruby file, but it needs to define an AppBuilder
class and we’ll generally want this to inherit from Rails::AppBuilder
. This is the class that the app generator delegates to handle the creation of the application’s structure and the best way to see how it works is to browse its source code on Github. This is where the class that we inherit from is defined and it contains a number of methods that generate different parts of a new Rails application. We can override these builder methods to customize the way that the Rails app is generated.
For example we could override the readme
method to generate a different README file. Doing this means that we don’t have to remove the Rdoc version of the README like we did in our template. Overriding the test
method will replace the default test behaviour so we can add code here to use RSpec instead and our application won’t have a /test
directory.
class AppBuilder < Rails::AppBuilder def readme create_file "README.md", "TODO" end def test @generator.gem 'rspec-rails', group: [:test, :development] run 'bundle install' generate 'rspec:install' end end
Note that we can’t call the gem method directly as for some reason it doesn’t delegate properly to the generator. We need to use @generator.gem
here instead.
We call bundle install
in the test
method but it’s better to call this later on in the application’s generation process. It would be best to wait until the app generator calls it, but running generate 'rspec:install'
requires that we have the RSpec gem installed. It would be good if there was a callback hook that we could use to trigger this after running the bundle
command but it seems that there isn’t a good way to do this.
There are a few more things we want to do in this file but they don’t override the application’s generator methods. In this case we can use a leftovers
method which the generator will run at the end of the build. We’ll use this to ask the user if they want to generate a root controller and to create our Git repository.
def leftovers if yes? "Do you want to generate a root controller?" name = ask("What should it be called?").underscore generate :controller, "#{name} index" route "root to: '#{name}\#index'" remove_file "public/index.html" end git :init append_file ".gitignore", "config/database.yml" run "cp config/database.yml config/example_database.yml" git add: ".", commit: "-m 'initial commit'" end
Our builder is now complete and it has the same functionality as our template. The builder seems to be the better approach here as it allows is to override the default generation behaviour such as the generation of the /test
directory and the default README file. To use it we generate a new Rails application with the -b
option.
$ rails new blog -b app_builder.rb
Just like our template did this will ask us if we want to generate a root controller and we’ll end up with a new Rails application customized the way we want it.
Template Generators
If you’d rather not create a template or builder from scratch there are some tools available that can make this a lot easier. RailsWizard is one option. It’s a little out of date but it’s a great example of what can be done with app templates. We can select from a number of options from various categories and when we click “Finish” we’ll be given the Rails command that will generate the custom app for us.
The command that’s generated uses a URL for the template and we can use a URL for a template or a builder instead of a local file path.
$ rails new APP_NAME -m http://railswizard.org/92a77c755837ff78383a.rb -T
App Scrolls is a more recent tool. It takes the idea of RailsWizard and turns it into a command that can be run locally. To use it we first need to install its gem.
$ gem install appscrolls
If you’re using rbenv you’ll need to run rbenv rehash
before you can use it. Once it’s installed we’ll have access to a scrolls
command and if we run scrolls list
we should see a list of the available scrolls. These include RSpec, Guard, Capybara and many more. We can create a new application by running scrolls new
and passing in the options we want to use.
$ scrolls new blog -s rspec guard
This uses Rails templates in a similar way to how RailsWizards works but it’s more up to date with current versions of Rails.