Latest from the Bamboo Blog

2

Integration testing Merb with Webrat gwyn

April 23rd, 2008

RSpec stories are a way of doing integration and acceptance testing using plaintext executable tests. You can use them in Merb as well as Rails. Here's how.

Setting up

Install edge Merb; the latest gem (0.9.2) will not work. You need merb-core, merb-more, and merb-plugins.

Merb-plugins gives you the merb_stories gem, so you don't need to install that separately.

Add this line to your app's config/environments/test.rb:

dependencies "merb_stories", "webrat"

(Note that merb_stories' README file is wrong about this, for now - and the generator will create a dependency on merb-rspec, which no longer exists. My fork fixes this.)

Now generate your story:

merb-gen story mystory

Now run your story:

rake story\[mystory\]

Yes, you must include the square brackets, and you have to escape them.

Writing stories

Now fill out your story. There are some differences to Rails' versions. The best places to look for help are in the Merb code itself:

  • spec/public/test/controller _matchers _spec.rb
  • lib/merb-core/test/helpers
  • lib/merb-core/test/matchers

To start you off, here are the steps for a simple integration test:

steps_for(:homepage) do
  When("I visit the root") do
    @mycontroller = get("/")
  end
  Then("I should see the home page") do
    @mycontroller.should respond_successfully
    @mycontroller.body.should contain("Hello") 
  end    
end

As you write your tests, don't trust Merb absolutely. Some things are wrong, don't work, or aren't meant to work [yet]. As part of debugging, look at the Merb source, and fork it and fix it if needed.

Adding Webrat

Webrat lets you write integration tests that are even closer to natural language. You can say things like:

visits '/auth/login'
fills_in 'username',:with=>"bob"
fills_in 'password',:with=>"hunter2"
clicks_button "login"
response.should be_successful
...

I've forked Webrat to add Merb support.

To get started, clone it, build the gem and install it.

You'll also need to make sure you're using the memory session store (for testing), or your sessions won't be preserved:

Merb::Config.use do |c|
  c[:session_store] = 'memory' 
end

Continuous integration

Autotest will run your specs continuously, but won't run your stories. You can fix this, but stories run very slowly, since they use the full stack. At the very least, though, you want to run your stories after you deploy, in case you have "works on dev machine, dies on production" problems.

Add this to deploy.rb:

namespace :deploy do
  task :after_deploy do
    run_remote_tests
  end
  desc "Run tests on remote server"
  task :run_remote_tests do
    run "cd #{deploy_to}/current && rake spec"
    run "cd #{deploy_to}/current && rake story[all] MERB_ENV=test"
  end 
end

This will fail if you're using Vendor Everything like us - it won't be able to find merb-core. To fix it, replace stories/stories/all.rb:

env = ENV['MERB_ENV'] || 'test'
require 'rubygems'
Gem.clear_paths
Gem.path.unshift(File.join(File.dirname(__FILE__), "..","..","gems"))

require 'merb-core'
Merb.load_dependencies(:environment => env)

require 'spec'
Merb.start_environment(:testing => true, :adapter => 'runner', :environment => env)

dir = File.dirname(__FILE__)
Dir[File.expand_path("#{dir}/**/*.rb")].uniq.each do |file|
  require file
end

You'll have the same problem with specs. Edit spec/spec_helper.rb:

Gem.clear_paths
Gem.path.unshift(File.join(File.dirname(__FILE__), "..","gems"))

Now you have natural-language integration tests that run automatically on deployment. Sweet.

7

Merb Book (Part 3) matt

April 18th, 2008

Here we are going cover the install instructions for Merb, RSpec, and DataMapper (0.9) and how to create a bare application. The next post will cover aspects of the framework and introduce the example application we will be building.

Read the rest of this entry
0

Merb and DataMapper Book (Part 2) matt

April 17th, 2008

Here is the next part of the Merb, DataMapper, RSpec book. I was planning on releasing more today but I am rewriting the examples for DataMapper 0.9. There is a example app for DataMapper 0.3 in the git repository of the book, if you can't wait to play with it.

Read the rest of this entry
5

Merb and DataMapper Book (Part 1) matt

April 9th, 2008

When I started learning Merb and DataMapper I kept a collection of notes to help me keep up with these projects. These grew to the point that they couldn't fit nicely into a single text file. With contributions from others I started to put together a small book on developing web apps with Merb and DataMapper. I have decided to release it as we go. There is also a project in GitHub if you want to check it out and contribute.

So here we go (corrections and comments welcome):

Life On The Edge With Merb, DataMapper & RSpec

Read the rest of this entry
1

Euruko 2008 matt

March 30th, 2008

We're at Euroko in Prague, with some of the guys at New Bamboo. It's been pretty good so far (crippling flu aside) and Prague is a lovely city.

I gave a talk today on Aspect Orientated Programming in Ruby using Aquarium, it quickly covered what I mentioned in my last post on AOP, and went into more detail with actual code examples this time!

The slides are on slideshare, the demo code is currently in a git repo . The demo app is Merb/Datamapper and I'm currently trying think of more examples of cool aspects to put in there, feel free to add some.

If anyone saw my talk, I'd love to hear what you thought of it so drop me a line matt@new-bamboo.co.uk or leave a comment on this post.

2

QCON London 2008 matt

March 16th, 2008

I’ve just spent the last week at QCon and I’ve just about fully recovered (it was pretty intense). Considering there was only one Ruby track I was a little worried that I wouldn’t find most of the talks interesting, however this wasn’t the case. The most interesting talks had very little to do with Ruby.

When I go to conferences, my main goal isn’t to learn lots of things it’s to be inspired and generate new ideas, consequently my “things to do in my spare time” list has grown exponentially.

The two underling themes that I detected from the conference was developers are people too and scaling is important. It was interesting to hear Java guys talk about how to make developing easier (more fun), because this is one area Ruby (in my opinion) excels over most other languages.

One of the main criticisms about the Ruby community is we don’t care about scaling, and scaling is something that the Java guys have been working on for years. So when Ola Bini says that JRuby will be the best stack/platform around, I’m starting to believe him.

Imagine having your Ruby app with objects persisting in something like Terracotta?

Or being able to use existing Java aspects on Ruby code (and visa versa).

There was talk about GemStone porting their Smalltalk VM to Ruby so we may have an ‘enterprise’ stack.

And where the hell are the Ruby Specs? I mean the tests for the language itself. There should be one central repository where YARV, Rubinius, JRuby, GemStone and every Ruby developer out there can contribute to Spec’in out the language.

They are just a handful of ideas that popped into my head. I had a great time at the conference and met many interesting people, hopefully I’ll be able to do it again next year!

The slides (videos soon) from the talks are going online so take a look. Please excuse me while I go play with Erlang.

UPDATE I’ve put some of the slides that are available on the QCon site onto slideshare tagged with QCONLONDON2008 , enjoy.

0

Nested layouts in Merb martyn

March 15th, 2008

First a simple solution using throw_content and catch_content.

This is what sublayout.html.erb looks like

<% throw_content :sublayout do %>
  <div class="header">
    ...
  </div>

  <%= catch_content :for_layout %>

  <div class="footer">
    ...
  </div>
<% end %>

<%= render catch_content(:sublayout), :layout => "application" %>

Your main layout contains <%= catch_content :for_layout %> as usual.


You might find it handy to use catch and throw content in a few more places. For example, in your sublayout you could <%= catch_content :sublayout_title %> and throw it in the view with

<% throw_content :sublayout_title %>
  Hippos
<% end %>

As a little bonus I wrote a helper so that you can use the same syntax as you're used to from the rails plugin. Just add this snippet into Merb::GlobalHelpers

def inside_layout(layout, &block)
  content = render(capture(&block), :layout => layout)
  concat(content, block.binding)
end

Then your sublayout looks like

<% inside_layout :sublayout do %>
  <div class="header">
    ...
  </div>

  <%= catch_content :for_layout %>

  <div class="footer">
    ...
  </div>
<% end %>

Here's another idea which I think would be even better: What if you could say layout ['application', 'sublayout'] in your controller and have each layout rendered inside it's parent all up the chain? Then sublayout.html.erb wouldn't be constrained to always render inside application.html.erb and you could use it in a much more flexible way. However Damien things it's a mad idea. What do you think?

1

Freeze gems in a Merb application martyn

March 14th, 2008

Frozen gems in Merb == gems installed inside the application.

That's clever because now you can use the standard tools for installing and managing the installed gems. For example, you get dependency checking and so on for free. Add -i gems to the command line options to specify the install location.

gem install merb-core -i gems

That's the simplest case (run it from the root of you merb app). It fetches the merb-core gem from the gem servers and installs it inside a gems directory.

However, in many cases that's not what you want. Really you want to freeze the version you're currently developing on. Maybe one you've installed from the merb-core git repository. That's really easy because you have the gem package files on your system already. For me they're in /Library/Ruby/Gems/1.8/cache. Get your location by running gem environment gemdir and look for a cache folder. Install the package with

gem install /Library/Ruby/Gems/1.8/cache/merb-core-0.9.1.gem -i gems

You should add /gems/doc to your .gitignore since you definitely don't want ri and rdoc files in your repository. Alternatively add --no-ri --no-rdoc to the gem install command.

The final challenge is actually getting merb started. Right now I'm relying on a pastie that google found for me (I'm sorry I have no idea who the author is). Make that executable and use it to run merb with the usual merb command line options. That's surely a bit of a hack though and I'm hoping that someone will point out a better way :) Update: Thanks Steve - I hadn't spotted the generator. Run merb-gen freezer frozen-merb and then run Merb with ./script/frozen-merb passing the usual merb command line options.

0

How to use git / github with capistrano martyn

March 12th, 2008

I'm assuming that you're using private key authentication for git and that you've got an ssh agent set up (if you're running Leopard then you automatically have this).

The trick is to use ssh agent forwarding, now supported in capistrano. It allows the server to pull the latest code from github using your local private key and ssh agent. To understand how this trickery works take a look at this illustrated guide.

set :ssh_options, { :forward_agent => true }

One of the great things about git is that it's really fast, even if you're cloning an entire repository. However you can go even faster by using the repository_cache option in capistrano. This essentially keeps a clone of your app on the server and then just does a git pull to fetch new changes and copies the directory across when you deploy.

set :repository_cache, "git_cache"
set :deploy_via, :remote_cache

Another recommended strategy is to specify a git branch. Otherwise you'll default to deploying HEAD which might be some crazy experiment.

set :branch, "stable"

In case you haven't used remote branches they're really easy. Say you have a local stable branch. Push this to github using

git push github stable

So the complete capistrano file should look something like

set :application, "your cool app"

# Source code
set :scm, :git
set :repository, "git@github.com:githubusername/gitrepo.git"
set :branch, "stable"
set :repository_cache, "git_cache"
set :deploy_via, :remote_cache
set :ssh_options, { :forward_agent => true }

# Deployment servers
role :app, "your server"
role :web, "your server"
role :db,  "your server", :primary => true
set :deploy_to, "/var/www/#{application}"
3

Github + Textmate Wiki bundle = Giki damien

March 11th, 2008

In the past year we've probably gone through about 3 different wikis. Either they don't have things like email notifications with a diff, have their own special markup language or cost what is unwieldy amount for just a wiki.

We've scrapped all of these and have now migrated to a private Github repo containing markdown files managed with the help of the Textmate Wiki Bundle. The simplest solutions are the best.

0

When using the nginx_uploadprogress_module you need to set the X-Progress-ID header in your progress polls. Safari sanitises header names, so X-Progress-ID ends up becoming X-Progress-Id in your request. Unfortunately Firefox does just the oppose renaming X-Progress-Id to X-Progress-ID (but only for this particular header). The current release of nginx_uploadprogress_module is case sensitive, so I've patched it to be case insensitive.

Nginx Upload Progress Module Safari patch

0

HTTP/AJAX debugging with Charles damien

February 29th, 2008

If you ever need to inspect an AJAX request from a browser such as Safari (which doesn't quite have a Firebug equivalent], Charles is a useful debugging proxy.

3

IrregularScience martyn

February 19th, 2008

ImageScience is great. Just look at the code! One beautiful class wraps up FreeImage with some jucy inline C. If you can install FreeImage then nothing can go wrong. Much better than a bazillion lines of RMagick (which sometimes still can't so what you need)!

Simplicity is great but unfortunately ImageScience has this infatuation with squares. I'll explain.

Image science has two low level methods which allow arbitrary cropping and resizing. They look like this:

image.with_crop(left, top, right, bottom)

image.resize(width, height)

However what you really want to do is usually a combination of cropping and resizing. ImageScience helpfully provides:

# Resizes to fit within a square
image.thumbnail(size)

# Resizes and crops to be exactly a square
image.cropped_thumbnail(size)

This is great until you need an avatar that's a fixed ... rectangle. Or an image that will fit exactly inside a ... rectangle.

In this case you need IrregularScience!

You'll get rectangular versions of thumbnail and cropped_thumbnail:

# Resizes to fit within a rectangle
image.resize_within(width, height)

# Resizes and crops to be exactly a rectangle
image.resize_exact(width, height)

I gave my methods slightly different names because I'm like that ;)

There's also the case where you want a exact height or an exact width regardless of the other dimension (ever seen facebook?). That turns out to be a really horrible hack with RMagick involving all sorts of squashing and stretching unless you go really low level. Just follow these incantations and know that no horrible hacks are going on:

image.resize_to_width(width)

image.resize_to_height(height)

To get started download irregular_science.rb and require it. Then adapt this example:

ImageScience.with_image(upload_path) do |image|
  self.thumbnails.each do |name, size|
    image.resize_exact(*size) do |img|
      img.save(attachment_path(name))
    end
  end
end

Enjoy!

P.S. This seems to be my first blog post here - so hi!

6

Micro-patterns in Ruby ismael

February 5th, 2008

Yup. Micro-patterns. I've always wanted to begin a blog post with some highly sophisticated buzzword so it might as well be this one (sadly, I can't say I'm the first to use it).

This is a tiny one. Let's say we have a Factory that gives me Adapters#1 to a series of hosted media services (Flickr, YouTube, Vimeo, PhotoBucket, etc.).

The user of my code provides a service prefix and a URL to a picture or photo page in a given service, and the factory returns an instance from which the user can fetch different media sizes through a standardized API.

flickr_pic = ServiceParser.instance( 
  'flickr', 'http://www.flickr.com/photos/new_bamboo_london/2158168775/'
)

puts flickr_pic.thumbnail  
# => http://farm3.static.flickr.com/2147/2158168775_01ae89cbfb_s.jpg

yt_video = ServiceParser.instance( 
  'youtube', 'http://www.youtube.com/watch?v=PCunD-_mOwQ' 
)

puts yt_video.thumbnail 
# => http://i.ytimg.com/vi/PCunD-_mOwQ/default.jpg

Simple enough. But it seems a bit redundant to have the user declare both the service name and the page URL. Since URL's are unique anyway, those should be enough for our smart factory to know what adapter to hand us, kindly.

We could have a set of regular expressions in the factory, one for each URL/service.

class ServiceParser
  SERVICES = {
    FlickrAdapter   => /flickr\.com/,
    YouTubeAdapter  => /youtube\.com/,
    VimeoAdapter  => /vimeo\.com/,
    PhotoBucketAdapter  => /photobucket\.com/
  }
  # Factory method
  #
  def self.instance( url )
    subclass = SERVICES.find(nil) {|klass, exp| url =~ exp}
    raise 'not implemented' if subclass.nil?
    subclass.new url
  end
end

We could. But if we did, all hell would break loose and a mob of angry Design Patterns advocates would ram our door and... reprimand us.

While they're at it, they would tell us that one of the many golden rules of Object Oriented software design is, blockquote please:

The superclass should have no knowledge of the subclasses.

But you already know that. We need to take those URL's out of the factory class and into where they belong, in each adapter class. Also, adapters should be able to register their URL's with the factory, so it knows where to look. Ruby to the rescue.

# = The superclass / factory
class ServiceParser
  # We register adapters into this class variable
  #
  @@adapters = []
  
  # class reader
  #
  def self.adapters
    @@adapters
  end

  # Factory method here, se below...
  #
end

# = An example subclass / adapter
class FlickrAdapter < ServiceParser
  def initialize( url)
  
  end

  # API methods
  #
  def thumbnail
    # do something clever here...
  end
  
  # register ourselves with the Factory
  #
  ServiceParser.adapters << [self, /flickr\.com/]
end

The fun thing about Ruby, class definitions being executable code and all, is that you can just have subclasses add themselves (and their corresponding regex.) to the factory's list of adapters in load time.

ServiceParser.adapters << [self, /flickr\.com/]

Then it is a matter of modifying the factory method to search for matching URL's in the dynamically populated list of subclasses / URL expressions.

# Refactored factory. No pun intended.
#
def self.instance( url )
  match = @@adapters.find(nil) {|klass, exp| url =~ exp}
  raise 'not implemented' if match.nil?
  match.first.new url
end

Finally, a little more elegance to make our adapter authors happy (and less prone to mess with the internals of our superclass!)

class FlickrAdapter < ServiceParser
  # Initializer, API and protected methods here...
  #
  # We register our URL with a neat class method on the superclass
  #
  register_url /flickr\.com/
end

class ServiceParser
  # Factory, abstract methods and cross-adapter utilities here...
  #
  # Class method for subclasses to register themselves.
  #
  @@adapters = [] #2 See note on class variables

  def self.register_url( exp )
    @@adapters << [self, exp]
  end

end

#1 Two more buzzwords and just the second paragraph! And there's more...

#2 Class @@variables. These variables are not inherited but shared by all classes in the inheritance tree. That means that, whenever a subclass adds to the superclass' version of @@adapters, it'll actually have the new value in all subclasses. This is not an issue in our simple example, though.

0

Code, glorious code... andy

January 22nd, 2008

…we live for it and treat it like our own child, nurturing it into maturity, but there comes a time when every child must flee the nest so…

…we’re giving it away…

…that’s right folks, I’d like to announce the introduction of code.new-bamboo.co.uk.

We hope that over time code.new-bamboo.co.uk will grow to become a great resource for open source projects and code snippets. To kick us off we have…

  • PolyPage - A jQuery plugin for adding state to wireframes.
  • JSS - a jQuery plugin for adding CSS3 selector support for your stylesheets!
  • The Index Inspector - A rails specific rake task for probing your database indexes.
  • Dogger - A fun little log helper.

I hope you’ll agree we are off to a great start but we’d love some help so if you have any ideas or suggestions then sound off in the comments for now. hopefully there will be a more formal issue tracker setup soon.

The jQuery plugins are released under the same licensing terms as jQuery itself, snippets aren’t formally licensed but feel free to use and distribute them as you please.