Subscribe

Recent Articles

Rake Task to Upload Assets to S3 for Cloudfront

In an application serving static assets from Cloudfront, I’m using Fog and the following Rake task to upload precompiled assets and remove stale ones.

The task depends on assets:clean and assets:precompile, so each time it runs public/assets is cleaned out and the assets are recompiled. The task then calculates the etag (MD5 checksum) of each file, compares it to the etag of the file on S3, and, if it’s different, copies the asset up. If the etags are the same, it skips the file. Finally, after uploading everything, the task runs through the contents of the asset bucket, and removes any files that didn’t also exist in public/assets on the local machine. This assumes the bucket in question is only being used to serve assets for the current application. Do not use this task as is if you are using the bucket to serve additional content!

As a sanity check, the task aborts before making any changes if public/assets is empty.

This task also takes advantage of Rake’s command line arguments feature to let you run the task in “no-op” mode. In this mode, assets are still removed locally and precompiled, but changes to S3 are reported but not actually carried out. To run it in no-op mode, append [noop] (really, anything in brackets) to the task name on invocation:

$ rake assets:upload[noop]    # runs in no-op mode
$ rake assets:upload          # runs in dangerous mode

Rails Source Annotations and RSpec

By default Rails’ source annotation rake tasks (notes and its more specific children notes:todo, notes:fixme, etc.) only search the app, config, lib, scripts and test directories of your application. I use RSpec, and on short notice, dumping this file into lib/tasks of my application was the best I could come up with to add spec to the annotation search path.

(I know some people consider littering your code with notes to your future self [or future replacement] to investigate and fix things is a smell that could indicate you’re lazy and/or bad at prioritizing. They may well be right, but until I retrain myself, this helps.)

RSpec + Spork Ignoring Filters

I’m posting this as a reminder to myself and as Google fodder to raise awareness of this discussion.

Most of the Rails projects I have in active development use RSpec for testing. I also use Spork to preload the Rails environment, allowing the tests to run more quickly. When I’m actively working on a specfic example, particularly relatively slow-running request specs, I’ll often use the :focus tag to filter out the specs I don’t need to run. I have the following set up in my RSpec configure block:

# spec/spec_helper.rb
RSpec.configure do |config|
  config.treat_symbols_as_metadata_keys_with_true_values = true
  config.filter_run focus: true
  config.run_all_when_everything_filtered = true
end

Then I tag the spec I’m working on with :focus like so:

# spec/requests/some_feature_spec.rb
describe 'SomeFeature' do
  it 'successfully does awesome stuff', :focus do
    # test awesome behavior
  end
end

I then go to work implementing the feature, periodically checking the window running RSpec to observe my progress towards getting the feature working as described.

At some point recently — apparently after upgrading to RSpec 2.8 — I noticed the :focus tag being ignored. When I’d save my changes, instead of the one focused example being run, the entire spec file was being run. On a slow-running request spec, this could be annoying, especially if I wanted to scroll through the log/test.log file to debug exactly what was happening in the database as the log output was cluttered with unrelated examples.

After spending some time composing suitable Google-fu to find reports of similar problems I ran across #166 on Spork’s Github issue tracker. The problem seems to rest in RSpec 2.8 somewhere, and the fix (or, at the very least, workaround) is relatively simple: add --tag focus to the .rspec file at the root of your project.

(As an added reminder, don’t forget to set run_all_when_everything_filtered to true in your RSpec.configure block to ensure all your specs are eligible for running when nothing is tagged with :focus.)