Many Ruby gems are packaged with a Rails generator that generates a configuration file. Keeping these gem configurations up-to-date can be much easier if you take advantage of these generators during the upgrade process.
Installing a gem that has configuration
One example is simple_form, a gem that makes it easy to create and maintain forms. Another is devise, a popular gem for handling user session management. (Both of these gems are maintained by the excellent developers at Plataformatec.)
The simple_form README says to run this command during installation:
The file config/initializers/simple_form.rb looks like this:
# Use this setup block to configure all options available in SimpleForm.
SimpleForm.setup do |config|
# Components used by the form builder to generate a complete input. You can remove
# any of them, change the order, or even add your own components to the stack.
# config.components = [ :placeholder, :label_input, :hint, :error ]
# Default tag used on hints.
# config.hint_tag = :span
# CSS class to add to all hint tags.
# config.hint_class = :hint
# CSS class used on errors.
# config.error_class = :error
and so on...
Also notice that you get a nice ERB template that extends the built-in Rails scaffold generator to automatically hook newly generated forms up to simple_form. And you get a default set of internationalization strings in config/locales.
Upgrading a gem that has configuration
The problem is that when you upgrade the simple_form gem from 1.5 to 2.0, the files that you have generated are stale. They might represent options that are no longer in the gem. Also, you might be missing out on new configurable features.
And the defaults might have changed, so any commented out lines showing the defaults will now be wrong. This recalls the programmer adage, “a comment is a lie waiting to happen.”
Well, there is an easy solution, of course. Just re-run the generator!
Assuming you’re using a source control system like Git, you can safely re-run the generator without breaking any of your code.
$ rails generate simple_form:install
SimpleForm 2 supports Twitter bootstrap. In case you want to generate bootstrap configuration, please re-run this generator passing --bootstrap as option.
Overwrite /Users/grant/code/blog_posts/rerun_generators/config/initializers/simple_form.rb? (enter "h" for help) [Ynaqdh]
Oops, looks like we have a conflict! Not to worry. I always just say yes to everything and move on. We will address the conflicts later.
Afterwards, all three of the generated files show up as having changes.
$ git status
# On branch master
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
# modified: config/initializers/simple_form.rb
# modified: config/locales/simple_form.en.yml
# modified: lib/templates/erb/scaffold/_form.html.erb
no changes added to commit (use "git add" and/or "git commit -a")
I can use command-line tools like git diff or GUIs like GitX to figure out what changed, and more important, make an educated decision about which changes I want to keep and which ones I want to change back to the old version.
Now let’s look at config/initializers/simple_form.rb.Check out this diff; it’s got quite a lot of changes!
Changes to the name of a setting
Let’s focus on one small change:
- # When false, do not use translations for labels, hints or placeholders.
- # config.translate = true
+ # When false, do not use translations for labels.
+ # config.translate_labels = true
This configuration option changed! I imagine that using the old version would give a deprecation warning when your application boots. Most people would simple change translate to translate_labels and move on, but in our version, we pick up the change to the comment as well, helping out future developers (and our future selves) to figure out what’s going on more quickly.
Changes to settings that are not on by default
Here’s another interesting example. Note that the comment didn’t change. It turns out that the simple_form authors are doing something subtle here.
# You can define the class to use on all labels. Default is nil.
- # config.label_class = nil
+ config.label_class = 'control-label'
They really want new users that follow the installation instructions to set a config.label_class. This would make it easier for those users to style the form labels generated by simple_form while not affecting other labels that they might be manually generating. But the authors also don’t want to force that decision on other gem users who upgrade blindly and don’t even realize the configuration file exists.
So they have set a reasonable backwards-compatible default for the past, and have pushed out a good new opinionated default for new users. This is a balanced and thoughtful approach. And now that we are sitting in front of this diff output, we get to make a conscious decision about which direction to take.
For my final example, I’ll ask you to spend some more time looking over the diff, which appears below.
Notice that several settings were bundled together and moved into a config.wrappers block. This change is pretty drastic, and I imagine there is some sort of backward-compatibility for users who use the old settings.
But by embracing the new block style, we learn that we can create additional groups of settings. The :default argument to config.wrappers implies that we can create additional named settings groups and mix and match where we use them in the application.
So by re-running the generator, we find ourselves making a lot of interesting choices about our application right at the time that we upgrade a gem. This is great because we still have full context about why we chose to upgrade. Woe to the developer that is working on a bug several months from now who doesn’t understand why a setting is set in some strange old way.