iprog.com

Capistrano errors when upgrading an app to Rails 4.0

First, be sure you’re using Capistrano v2.15.4 or higher. There are some significant changes in Capistrano’s awareness of assets and how it manages precompilation.

Rails 4 changes how the asset manifest is handled. The manifest holds a list of all available assets. In Rails 3, this was generated as public/assets/manifest.yml. In Rails 4, it’s now a JSON file along with a fingerprint, eg: public/assets/manifest-9f745054f45f3f47968d59f1665c66be.json.

By itself, Rails handles this without any issue – including the upgrade from Rails 3.2 to 4.0.

However, Capistrano has some issues here which seem related to its attempt at intelligent cleanup of assets no longer being used.

If your upgrade to Rails 4 works the first time through, you’re probably fine. If there’s any kind of error, you may be left in a mixed up state that’s hard to recover from.

Two possible errors you may see from Capistrano.

1More than one asset manifest file was found in '/apps/some_app/shared/assets'.  If you are upgrading a Rails 3 application to Rails 4, follow these instructions: http://github.com/capistrano/capistrano/wiki/Upgrading-to-Rails-4#asset-pipeline
1lib/ruby/1.9.1/psych.rb:203:in `parse': (<unknown>): mapping values are not allowed in this context at line 1 column 6 (Psych::SyntaxError)

I found it easiest to login to the app server(s) and remove manifest*.{yml,json}.

Be warned, removing these files may make it difficult to rollback to a Rails 3.2 version of your app. In theory, you just need to rollback and then recompile the assets. In practice, I experienced some weird issues with bundler loading Rails 4 gems when it was supposed to be loading Rails 3.2 gems, making it impossible to regenerate the asset manifest. You may want to move those files aside instead of just removing them.

I ended up removing the current symlink along with the manifests which then allowed Rails 4 assets to finally build properly without any complaints from Capistrano:

1rm /apps/some_app/current /apps/some_app/shared/assets/manifest*.{yml,json}

You can remove current while the existing (Rails 3) app is still running. However, it’s possible assets will longer be served by your web frontend (apache, nginx, etc.) until the Rails 4 app successfully compiles assets and starts.

If you have any way of testing against a staging server (starting from the Rails 3.2 version of your app), I highly recommend it.

Also related:

0 comments

Errors when precompiling assets in Rails 4.0

Rails 4 now always loads initializers and the database configuration before precompiling assets. Depending on your deployment environment, this may cause errors.

For example, not finding database.yml:

Could not load database configuration. No such file - /apps/some_app/releases/20130703220638/config/database.yml

In Rails 3, this was solved with:

1config.assets.initialize_on_precompile = false

However, this configuration option no longer exists in Rails 4. One workaround is to pass a dummy database init string to rake assets:precompile:

1DATABASE_URL=postgresql://user:pass@127.0.0.1/dbname

Be sure to replace postgresql with your database adapter name if using something other than Postgres.

A complete example:

1bundle exec rake RAILS_ENV=production DATABASE_URL=postgresql://user:pass@127.0.0.1/dbname assets:precompile

With Capistrano’s assets handling, you can just set :asset_env:

1set :asset_env, "RAILS_GROUPS=assets DATABASE_URL=postgresql://user:pass@127.0.0.1/dbname"
3 comments

Preparing for an upgrade to Rails 4.0

There are a number of steps you can take to prepare an app for an eventual upgrade to Rails 4.0. Taking these steps now will simplify the upgrade later.

1. Upgrade everything else possible

It seems simple, but upgrade everything else possible first. This means ensuring you’re on Ruby 1.9.3 or 2.0.

Update as many other gems as possible too.

bundle outdated may be a helpful tool. Just know that it lists all potential upgrades, not just upgrades that are compatible with your existing Rails 3.2 stack. (For example, it will list the Rails 4 gems.)

2. Move to strong_parameters

Rails 4 ditches attr_accessible/attr_protected in the models and replaces them with controller-based handling. This has the benefit of letting each controller handle things individually. The new format looks something like this:

1params.require(:user).permit(:username, :password)

This new way of handling the params hash is available as a standalone gem for Rails 3.2: strong_parameters.

It’s a fairly easy change to make ahead of time. After using the new style, I happen to prefer it over the old way – you might too.

3. If you use ActiveRecord, change all scopes to lambdas.

Old style:

1scope :is_active, where(active: true)

Becomes:

1scope :is_active, ->{ where(active: true) }

Or if you’re more comfortable with the older Ruby syntax:

1scope :is_active, lambda{ where(active: true) }

Anything that was already a lambda stays as is.

4. Tidy up RegExp’s

Technically speaking, anchoring to the beginning of a string in a Regexp should use \A not ^. Likewise, anchoring to the end should use \z instead of $.

If you’re dealing with input that’s known to be at least somewhat sane, ^ and $ are fine – and visually a little cleaner.

However, ^ and $ only anchor to the beginning of a line within a string. When dealing with user-provided input, this is not good (nor sufficient).

Rails 4 now enforces better behavior for all validation strings so as to avoid careless mistakes.

So this:

1validates_format_of :alphabet, with: /^abc.*xyz$/

Becomes:

1validates_format_of :alphabet, with: /\Aabc.*xyz\z/

It may be worthwhile to search your entire code base for /^ and $/ (not just app/models/*.rb) as an incorrect RegExp could potentially have security implications for your app.

5. Update routes.rb

Rails 4 places new restrictions on the match method in routes.rb.

Search routes.rb for match. For each occurance, if it only needs to allow a single HTTP verb, just change it to get, post, put, or delete.

If it needs to allow multiple verbs, then explicitly list them using :via:

1match '/contact', via: [:get, :post]

6. Ensure your test suite passes

If you have tests, make sure they’re not broken before you even start.

If there are a few holes in your test suite, now might be a good time to plug them.

If you don’t have a test suite, ideally you’d consider writing one now. But that’s pretty unrealistic, so just prepare to spend some extra time exercising the various parts of your app when you do the Rails 4 upgrade. If you have any particularly fragile parts of your app, it might be worth considering writing a few tests for those areas.

0 comments

Moving to Markdown

In the past I’ve used Textile markup for this blog, processed by the RedCloth gem. It has done (and continues to do) a good job.

However, it seems the trend is toward Markdown. GitHub, StackExchange, and others have all embraced it. A new project I’m working on will also be using Markdown.

It seemed like it was time to move to Markdown here on the blog too.

There are a number of gems capable of processing Markdown. However, one of the most extensive appears to be redcarpet. It has baked-in support for most of GFM (GitHub-flavored Markdown), which is a bonus.

Going forward, all posts will be rendered accordingly. As a bonus, GFM’s fenced-code-blocks are much cleaner than what I had hacked in previously, so there should be fewer weird-syntax-highlighting issues. I don’t plan on going back to cleanup the old posts though.

Oh, and comments now enjoy Markdown too! Yay!

As an aside, way back I discussed syntax highlighting in rails. My current gem of choice is coderay. Most of the options are just Ruby wrappers for the Python-based pygments. That’s a viable solution, but if you want to keep it Ruby-native, then coderay is a nice alternative.

0 comments

NoSQL and transactions

One of my long running habits when dealing with databases has been to use transactions. Transactions simplify handling errors in complex, multi-record operations.

More recently, I’ve been using transactions to wrap external API calls that I also need to store data about. External APIs fail. For that matter, internal APIs fail too, so let’s simplify that: APIs fail. Sometimes for no good reason.

Accordingly, it’s necessary to be able to fail gracefully in the face of such circumstances. Failing gracefully includes not only rendering meaningful error messages, but also not leaving local data in an unexpected state.

Transactions provide a simple, clean way to resolve this issue too. And they seem all but indispensable when combining multi-record operations with APIs.

Lately I’ve been doing more work with NoSQL databases. They’re fantastic for certain use cases. But the near universal lack of transaction support is annoying at best.

So now the challenge is how to work around the overall issue and provide as robust an experience as possible within the local app.

Read more...

0 comments