Archive for the ‘programming’ category

Ensuring that Heroku does not install gems within the Bundler :development group

January 7th, 2012

Tonight, i ran into an issue with Heroku, where is was failing when installing a gem that i have within my :development bundler group:

group :development, :test, :cucumber do

gem ‘ruby-debug19′

end

As the clear solution was to prevent heroku installing gems that it didn’t need, i found this handy heroku command:

heroku config:add BUNDLE_WITHOUT=”development:test:cucumber”

Running this has told heroku to ignore gems that it doesn’t need, meaning the deploy worked fine. Further details: http://devcenter.heroku.com/articles/bundler

This resolves the heroku error:

Installing linecache19 (0.5.12) with native extensions /usr/ruby1.9.2/lib/ruby/1.9.1/rubygems/installer.rb:483:in `rescue in block in build_extensions’: ERROR: Failed to build gem native extension. (Gem::Installer::ExtensionBuildError)

Testing Paperclip generated expiring S3 urls with RSpec, Cucumber and Timecop

December 21st, 2011

The need

I have a Rails app that is using Paperclip to generate expiring urls for files stored in S3. The urls are set to expire after 1 minute. As much as i trust Paperclip and Amazon, I need tests that prove that these generated urls do in fact expire on time, and that visitors to those files after they’ve expired are prevented from accessing the file.

Disclaimer

This has been a bit of a rush, so no doubt i’ll refactor and tidy the code and this post laster today / in the week.

The solution

I’ve used RSpec and Cucumber to check expiring urls that the system generates to ensure they expire successfully. RSpec simply checks that a generated url includes the Expires parameter and it’s value is set exactly to 60 seconds from now. Cucumber goes further than this by uploading files and checking if they are accessible before and after expiration.

RSpec to simply test that the expiration time generated for a link is correctly set to 1 minute

This test simply asks the model containing the attachment (in this case an “Asset” model), how many seconds from now remain before the attachment expires.

Spec

describe Asset do

it “should return an attachment link that expires within 1 minute” do

asset = Factory.build(:asset)
asset.seconds_until_attachment_expires.should == 60

end

end

This depends on a few new methods in the Asset model class, which take care of extracting the Expires param from the expiring url, and comparing to Time.now.

Asset Model Class

First, we create an instance helper method that returns the number of seconds an object’s url has left before it expires

def seconds_until_attachment_expires

Asset.seconds_until_attachment_expires(expiring_attachment_url)

end

I decided to pass the responsibility of calculating this number to a class method. I did this because the Cucumber tests need to request the same calculation for urls that were generated in the past. If they interacted with an instance of the Asset class, by default it would return a new url each time it was asked. So, rather than clutter up the instance method with a decision about whether to issue a new url or return an existing one, i simply passed the responsibility to the class. That seems to work for now, although I might refactor it later.

Next, we create the class level method that calculates time left until expiration. This accepts a url, meaning we can test urls generated now or in the past

def self.seconds_until_attachment_expires(url)

seconds = attachment_expiration_in_seconds_from_epoch(url) – Time.now.strftime(”%s”).to_i
seconds.round

end

This method simply strips the time from the generated url (via the attachment_expiration_in_seconds_from_epoch method) and rounds the value.

def self.attachment_expiration_in_seconds_from_epoch(url)

url.split(”&”).second.split(”=”).last.to_i

end

Clearly, this is tightly coupled to the format of the generated url string, so a cleaner way should be sought. However, for now, this method is only used in the tests and it does work, so it’ll do for the moment.

Finally, to ensure that Rspec, Cucumber and the app all interact with a url generated exactly 60 seconds from now, we create a model instance method that generates the link. All requests for the link call this method.

def expiring_attachment_url

attachment.expiring_url(60)

end

Cucumber to test actual file access via the browser

Cucumber takes things 1 step further. It interacts with all the same methods that we created on the Asset model, but also goes off and uploads attachments and then tries to access them before and after they’ve expired. We use Timecop to create expired urls, and a Cucumber before hook to ensure all scenarios run from the current time by default.

Scenarios

@selenium
Scenario: Viewing an active attachment on an object

Given some object has been created and a plain text file attached
When I visit the object’s attachment url
Then I should see the contents of the uploaded attachment
And I should not see “Request has expired”

@selenium
Scenario: Viewing an expired attachment on an object

Given some object has been created and a plain text file attached
When I visit the object’s attachment url after it has expired
Then I should not see the contents of the uploaded attachment
And I should see “Request has expired”

features/support/hooks.rb

Before do

Timecop.return

end

NB: For the sake of completeness (even though we’re not calling Timecop from our Rspec specs), to be completely satisfied that Timecop isn’t affecting our specs in any unexpected way, we add the same to spec_helper.rb too:

spec/spec_helper.rb

config.before do

Timecop.return

end

config.before do
Timecop.return
end

steps

This is where Timecop offers a wonderfully simple way of generating expired urls.

And /^I visit the question’s attachment url after it has expired$/ do

#First, go back in time 2 minutes and generate the expiring url, and make sure it’s set to expire in 1 minute
Timecop.freeze(Time.now – 2.to_i.minutes) do

@url = current_object.asset.expiring_attachment_url
Asset.attachment_expires_in(@url).should == 60

end

#Next, return to the current time and make sure the previously generated expiring url has now been expired for 1 minute
Timecop.return
Asset.attachment_expires_in(@url).should == -60

#Finally, go visit the expired url
visit @url

end

When run, cucumber correctly reports that expired urls result in the user seeing the message “Request has expired”, and non-expired urls correctly provide access to the uploaded file.

Summary

Although a rough and ready solution, and most likely needing refactoring, it does provide us with a way to test expiration of uploads to S3.

I hope you found this useful.

Getting up and running with Git and Rails on EC2

November 6th, 2011

Here’s a short list of things to do to get Git and Rails running on EC2:

Install Git

sudo yum install -y git
  • sudo yum install -y git

Install Rails

  • sudo yum install -y rubygems ruby-devel gcc libxml2 libxml2-devel libxslt libxslt-devel mysql mysql-devel
  • sudo gem update –system
  • sudo gem install rails

Quick notes on creating a local git branch and pushing it into the wild

December 6th, 2010
  1. Checkout the branch you want to switch from
    git checkout branch_to_branch_from
  2. Create the local branch
    git branch branch_name
  3. Switch to the branch
    git checkout branch_name
  4. Push to Remote
    git push origin branch_name

Tiny post – how to use .htaccess to redirect visitors

March 22nd, 2010
redirect 301 /help_decide/index.html http://showcase.health2works.com/help_decide/Homepage.html

Damn, it’s been ages since i’ve posted. This is a tiny one that is simple yet very useful.

The Situation

I have a Napkee site that consists of a bunch of html files linked together. This was naturally created from a set of Balsamiq screens. Within these Balsamiq screens was a screen called ‘Homepage’ (not index, because that would have been machine friendly but user unfriendly). So, on Napkee export, no index.html file had been created, which is just messy when sending out links to the Napkee screens because i didn’t want to include Homepage.html in the link.

The Need

I wanted to redirect all requests for http://www.website.com/napkee_mockup to http://www.website.com/napkee_mockup/Homepage.html

The Options

  • [Bad] An error page: Not an option so i’ll say no more about it.
  • [Bad] A Meta Refresh, where the following code is added to the Meta tags within the file Head. Again this was not an option as it gives the redirect task to the end user’s browser.

<META HTTP-EQUIV=”refresh” content=”0;URL=http://www.new.com/new.htm”>
<META NAME=”ROBOTS” CONTENT=”NOINDEX, NOFOLLOW”>

  • [Bad] JavaScript Refresh: Too ugly to even paste the code in here, so i won’t bother. This is again bad because you’re forcing the user to not only do the redirecting for you, but you’re also demanding that they use JavaScript to do it. Not very friendly.
  • [Good] .htaccess redirect: This is the only correct way to do it.

The Solution

cd [www]/napkee_mockup

vim .htaccess

add the following:

redirect 301 /napkee_mockup/index.html http://website.com/napkee_mockup/Homepage.html

The key is to ensure that the FROM url is relative to the web application root (so you have to include /napkee_mockup/) and you shouldn’t include the domain details (http://website.com)

I hope this helps a little.

There’s more good reading at http://www.tamingthebeast.net/articles3/spiders-301-redirect.htm