Shoulda Sporked That

July 28th, 2009

Nick Rutherford

One of the feelings I got from Rails Underground (best not to ask about the others!) is that while RSpec, Cucumber and family have laid tracks in the Rails toolkit and community, many still use TestUnit.

Alternatives are good in some senses, but make me feel like I'm missing out and worry about chasing the wrong rabbit. Seeing things bridged can be nice, and here's an example of that. This one is extra-nice because it also patched what was, to my eyes, a crack in the plaster.

Shoulda for RSpec

I've got some rspec view and controller macros I am considering pulling out into a plugin, but decided to look around for alternatives first. Remarkable is noted on some wiki or other, but I couldn't make much sense of it. rspec_on_rails_macros is a sensible looking alternative, but the origin has been out of development for some time and the forks are somewhat confusing. Looking around did have a positive consequence though -- they pointed out that Shoulda now has rspec support!

That may sound confusing, since Shoulda is another way of writing declarative unit tests (with a similar looking syntax to rspec), but the bonus is a raft of new matchers. So far only the model macros are available but more are promised to come.

Instant gratification:

-    it "should not be valid without a meta description" do
-      Service.make_unsaved(:meta_description => nil).should_not be_valid
-      Service.make_unsaved(:meta_description => '').should_not be_valid
-    end
-    
-    it "should not be valid without meta keywords" do
-      Service.make_unsaved(:meta_keywords => nil).should_not be_valid
-      Service.make_unsaved(:meta_keywords => '').should_not be_valid
-    end
-    
+    it { should validate_presence_of(:name) }
+    it { should validate_presence_of(:teaser) }

I'd Spork That

Next up is Spork, a must-have if you find your specs are running slowly.

You get another executable to add to your workbench, spork, this is a distributed ruby server which uses forking (unix only) to cut down start-up times. Pretty cool I think, get set up to a common starting point then clone it per run.

I find this config works for me, without having to restart the server when I change Machinist blueprints or helper files

require 'rubygems'; require 'spork'

Spork.prefork do
  ENV["RAILS_ENV"] ||= 'test'
  require File.dirname(__FILE__) + "/../config/environment"
  require 'spec/autorun'
  require 'spec/rails'
  require 'shoulda'
  require 'webrat'

  Dir[File.dirname(__FILE__) + "/spec_helpers/**/*.rb"].each do |file| 
    require file 
  end

  # Requires supporting files with custom matchers and macros, etc,
  # in ./support/ and its subdirectories.
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}

  Spec::Runner.configure do |config|
    config.use_transactional_fixtures = true
    config.use_instantiated_fixtures  = false
    config.fixture_path = RAILS_ROOT + '/spec/fixtures/'

    config.include Webrat::Matchers, :type => :views

    #machinist setup
    config.before(:all)    { Sham.reset(:before_all)  }
    config.before(:each)   { Sham.reset(:before_each) }
    #end machinist setup
  end
end

Spork.each_run do
  # This code will be run each time you run your specs.
  require File.expand_path(File.dirname(__FILE__) + "/blueprints")

  Spec::Runner.configure do |config|
    config.include ViewHelpers, :type => :views
    config.include ControllerHelpers, :type => :controller
    config.extend ControllerMacros, :type => :controller
  end
end

I went the route of adding --drb to the spec.opts file. This works with textmate and autospec. If you forget to run spork it'll run locally without the drb.

If you want something more (or actually) distributed for your tests take a look at testjour, or even some of the cloud computing services (which I've forgotten the names of!).

Nick Rutherford

Authlogic is a handy plugin for shifting your user authentication, big or small, into an external dependency. Having it in gem (or submodule/plugin) form rather than generated code means as updates become available they are easily installed, since the codebase is separated from your own.

On a current green-field project I've switched from a home-rolled simple admin authentication solution to Authlogic. This took some fiddling as I don't get on well with Authlogic's style of documentation, but I expect that will improve with time and blogs.

I'm hesitant to give out security advice, but found the following useful. I kept hold of as many of my specs and features as was reasonable with the authlogic switch, while the hashing was now out of my hands there were still important criteria for how I wanted usernames, passwords and so on to behave at each MVC layer. In particular I don't want the attempted password to be sent back to the user as a field value when they fail to log in. While SSL offsets this somewhat I would rather have that data submitted once and not sent back. Also I don't see a use for the unencrypted password other than during validation and hashing at update or login.

A very simple fix for the view concern, found on slideshare.net is to set the view template value to an empty string.

<%= f.password_field :password, :value => "" %>

That's fine, but it's hardly in the MVC spirit. The model shouldn't be letting anything have the raw password. For example if you start adding more access formats you'd need to worry about it there. I think it's best to cut it off at source, with something like the following ActiveRecord hook in the model which acts_as_authentic

def after_save
  @password = nil
  @password_confirmation = nil
end

There is probably a better way to do this, but between that and the view value blanking my specs and features pass. If you're doing a lot before saving or leaving an unsaved model floating around bear this in mind.

Thoughts, comments & criticisms welcome.

Nick Rutherford

While adding Textile to a resource following the outside-in BDD process described in the RSpec Book I found myself wanting to put a should_receive expectation on ActionView::Helpers::TextHelper#textilize

This left me somewhat confused, as I wasn't sure what to stub, and the book doesn't cover this. Google also drew a blank.

sticking a <%= inspect %> in the view and looking at the page source revealed an instance of ActionView::Base, which makes sense but was somewhat elusive. It also showed a @controller reference, which itself contains a @template reference. A consequence of this is that in your view spec you can get at the template with @controller.template

It turns out that within a view spec #template provides the relevant object. Thanks to Tim Riley for pointing this out.

For example:

it "should textalize the teaser and extra_content" do
  @service = mock_model(Service, :null_object => true)
  @service.stub!(:teaser).and_return 'a teaser'

  template.stub!(:textilize).and_return "textalised teaser"
  template.should_receive(:textilize).with @service.teaser

  render
  response.should contain("textalised teaser")
end

This kind of stubbing is useful as it checks that the view is calling the textilize method, part of the api, correctly, not whether RedCloth (or some other texitle gem) is doing its thing. Stubbing like this will, if nothing else, speed up the spec execution. Testing with input/output examples for the textilized data could go in the view spec, but api testing at that level seems out of place to me. Of course it should be tested at some level, e.g. with Cucumber. YMMV.

Font-Face Heats Up

July 20th, 2009

Ray Drainville

I spent last week shivering away with the Swine Flu. It wasn’t fun; but I knew something was coming & accordingly shut down jobs until I felt better, notifying the clients of the issue. This is planning: you do it because you know what’s coming.

Whilst convalescing, I read a fascinating discussion over on Jeffrey Zeldman’s blog. In this article, Zeldman publicised the fact that David Berlow of the Fount Bureau was proposing a new permissions table to OpenType. The idea is to be able to embed fonts into websites via @font-face whilst protecting the foundries from piracy. A permissions table would stop the font from, say, being downloaded & used elsewhere. Currently, only Microsoft’s EOT format allows for any protection from misusing the technology; it’s been around since 1997 (and it feels like it). Safari (for some time), Firefox (as of 3.5) & Opera (as of 10) support standard, naked type formats: Firefox 3.5 has just been released & Opera 10 will be released any day now. One may assume this is why there’s a sudden flurry of activity from foundries about the subject.

The name “David Berlow” may be familiar to you: he was interviewed in A List Apart back in April, where he started making this permissions idea & I wrote about that interview, and some of the reaction to it. Mark Pilgrim smacked it down pretty thoroughly, and with good cause: Mr Berlow’s suggestion would require that every computer on earth be altered. Not to mention virtually every font as well.

This isn’t the only problem, however. In the comments to Zeldman’s article I pointed out that it’s far too late for foundries to make such proposals: all modern browsers now support @font-face. To expand upon what I wrote there, the time for making these proposals should have been made back at least in 1998, when the @font-face was definitely part of the W3C’s CSS2 specification. And remember, if 1998 is the date of the recommendation, you can be goddamn sure that they were talking about it for years beforehand.

I apparently irked Berlow. He became quite defensive that until recently, foundries didn’t know how browser vendors would deal with fonts; and moreover, like other industries, he just wants to protect his IP. At first I thought that, in my feverish state, I had been a dick, but looking back, he simply didn’t get what I was saying. No matter the mechanism by which a browser deals with a font, @font-face has been with us for over a fucking decade. Foundries have had plenty of time to do something about it.

The best scenario for some sort of “webfont”, protected format would be to strongarm all the browser vendors into supporting it; suppress all the browsers out there that now support naked fonts; update every browser with webfont-“enabled” (one might say DRM-crippled) versions; and then hope for the best. Good luck with that. Let me reiterate what I’ve said before: this horse has long since bolted. If the foundries have pursued actions, they’ve been very slow and, worse, ineffective.

And as for Berlow’s concern about protecting his IP: well, they’ve had at least a decade to think about how to do this. A less charitable man than myself might think they were hoping this whole “fonts on the web” thing would just go away. Instead, they should have planned for this: full @font-face support was coming, and they knew it.

So bring on TypeKit. Where of course you’ll rent & not simply pay for the fonts you use. I have sympathy when people want to protect their IP, but Jesus H. Christ in a chicken basket, they’ll do anything to stop use from being straightforward.

Thinking

July 17th, 2009

Ray Drainville

That the recent brouhaha over the death of XHTML2 is totally overdone. After all, the spec was so fucking obtuse & abstract that no browser vendor was going to implement it.

I mean, the authors wanted to get rid of the IMG tag, fer chrissakes!

Republican Talking Point

July 6th, 2009

Ray Drainville

So Obama makes a speech at the University of Cairo (an excellent speech, in fact). You can always control what you say, but you can’t control who attends to your saying it:

Palestinian militants from the Popular Resistance Committee watch the televised speech of US President Barack Obama in Gaza City, Thursday, June 4, 2009.

Click on that link. You’ll not be sorry.

Summer Socials

July 1st, 2009

Nick Rutherford

Well Uni is over now for me (ceremony pending) and life out of the city is a lot quieter.

I've decided to keep busy with a few events coming up this year, two rails specific and one on more general web design topics:

I'm attending Rails Underground!

Rails Camp UK:
Rails Camp 2 UK

FOWD Tour - Leeds:

Take a look, there may be something you fancy.

Liquid error: undefined method `current_page' for #