diary at Telent Netowrks

Objections on Rails#

Thu, 05 Jan 2012 18:01:28 +0000

It seems to be pick-on-rails week/month/quarter again. I'm not sure that 3 months and two projects really should qualify me to have an opinion on it, but this is the internet, so here we go anyway.

Recent influences which have provoked these thoughts:

I'm not going to lay into the V and C of Rails here: to be honest, although they seem rather unpretty (my controller's instance variables appear magically in the view? ew) they're perfectly up to the task as long as you don't try to do the heavy lifting in either of those layers. Which is a bad idea anyway. And actually, after writing an entire site including the layouts in Erector (if you don't know Erector, think Markaby), there is a certain pleasure in being able to write the mostly static HTML bits in ... HTML.

No, instead of generalizing that MVC is the problem, I an going to confine myself to the ActiveRecord arena and generalize that M, specifically M built on ORM, is the problem. The two problems.

Here is the first problem. Object-orientated modelling is about defining the responsibilities (or behaviours, if you prefer) of the objects in your system. Object-relational modelling is about specifying their attributes. Except in the trivially simple cases ("an Elf is responsible for knowing what its name is") the two are not the same: you define an Elf with a method which tells him to don his pointy shoes, not with direct access to his feet so you can do it yourself. So that's the first problem: the objects you end up with when you design with ActiveRecord have accessors where their instance variables would be in a sensible universe.

(Compounding this they also have all the AR methods like #find wich in no way correspond to domain requirements, but really I think that's a secondary issue: forcing you to inherit baggage from your ancestors is one thing, but this pattern actively encourages you to create more of it yourself. This is the kind of thing that drives Philip Larkin to poetry)

Here is the second problem. We're wimping out on relations. For the benefit of readers who equate RDMBS with SQL with punishment visited on us by our forebears in 1960s mainframe data processing departments, I'm going to expound briefly on the nature of relational algebra: why it's cool, and what the "object-relational impedance mismatch" really is. It's not about the difference between String and VARCHAR

Digression: a brief guide to relational algebra

A relation is a collection of tuples. A tuple is a collection of named attributes.

(You can map these to SQL database terminology if you put your tuples in a grid with one column per attribute name. Then attribute=column, row=tuple, and relation=table. Approximately, at least)

An operation takes relation(s) as arguments and returns a relation as result. Operators are things like

Here's an example to illustrate for SQL folk: when you write

select a,b,c from foo f join bar b on b.foo_id=f.id where a>1
this is mathematically a cross join of foo with bar, followed by a selection of the rows where b.foo_id=f.id, followed by a projection down to attributes a,b,c, followed by a selection of rows where a>1.

Now here's the important bit:

the tuple isn't in itself a representation of some real-world object: it's an assertion that some object with the given attributes exists.

Why is this important? It makes a difference when we look at operations that throw away data. If Santa has a relation with rows representing two elves with the same name but different shoe sizes, and he projects this relation to remove shoe_size, he doesn't say "oh shit, we can't differentiate those two elves any more, how do we know which is which?", because he doesn't have records of two elves and has never had records of two elves - he has two assertions that at least one elf of that name exists. There might be one or two or n different elves with that name and we've thrown away the information that previously let us deduce there were at least two of them, but we haven't broken our database - we've just deleted data from it. Relational systems fundamentally don't and can't have object identity, because they don't contain objects. They record facts about objects that have an existence of their own. If you delete some of those facts your database is not screwed. You might be screwed, if you needed to know those facts, but your convention that a relation row uniquely identifies a real-world object is your convention, not the database's rule.

(Aside: the relational algebra says we can't have two identical rows: SQL says we can. I say it makes no difference either way because both rows represent the same truth and you have to violate the abstraction using internal row identifiers to differentiate between them)

Back in the room

The reason I've spent this expended all those words explaining the relational model instead of just saying "ActiveRecord has poor support for sticking arbitrary bits of SQL into the code" is to impress on you that it's a beautiful, valuable, and legitimate way to look at the data. And that by imposing the requirement that the resulting relation has to be turned back into an object, we limit ourselves. Consider

We wave #select, #map and #inject around on our in-memory Ruby arrays like a Timelord looking for something to use his sonic screwdriver on. When it comes to doing the same thing for our persistent data: performing set operations on collections instead of iterating over them like some kind of VB programmer, why do we get a sense of shame from "going behind" the object layer and "dropping into" SQL? It's not an efficiency hack, we're using the relational model how it was intended.

And although we can do this in Rails (in fairness, it gets a lot easier now we have Arel and Sequel), I think we need a little bit of infrastructure support (for example, conventions for putting relations into views, or for adding presenters/decorators to them) to legitimise it.

Wrapping up

Summary: (1) our ORM-derived objects expose their internal state, and this is bad. (2) we don't have good conventions for looking at our state except by bundling up small parcels of it and creating objects from them, and this is limiting us because sometimes we want to see a summary composed of parts of several objects. Summary of the summary: (1) exposing state is bad; (2) we can't see all the state in the combinations we'd like.

Yes, I realise the apparent contradiction here, and no, I'm not sure how it resolves. I think there's a distinction to be drawn between the parts of the sytem that allow mutation according to business requirements, and the "reporting" parts that just let us view information in different ways. I also think we're putting behaviour in the wrong places, but that's a topic for Part Three

If you have read all the way to the end, Part Two, "Objective in Rails", will be a run through of my current progress (with code! honest!) for coping with with the first problem and parts of the second. Part Three will be a probably-quite-handwavey look at DCI and how it might provide a way of looking at things which makes both problems go away.

Micro setup for minitest in rails#

Sat, 21 Jan 2012 18:27:16 +0000

I think this is the bare minimum setup for being able to write Minitest::Spec tests in a Rails 3.1 app, and certainly a lot simpler than all that faffage with minitest-rails and stuff

    config.generators do |g|
      g.test_framework nil
    end

inside YourApp::Application in config/application.rb

This is all pretty vanilla - it doesn't do spork or any of the faster-testing-through-not-loading-the-framework stuff, but with those three simple steps you can run rake test:units just as you would with the default Test::Unit stuff. test:foo for other values of foo appears also to work, but I don't have any integration tests in this project yet so don't take my word for it.

Double Trouble

I can see no way in minitest to create a partial mock: viz. a real object with some but not all methods mocked. In fact I can see no documented way in minitest to do any kind of mocking at all. As the new fashionable Double Ruby (a.k.a. rr ) library scores highly on both these counts, I decided to use that too.

This is a simple matter of adding "rr" to the Gemfile, then amending test/test_helper.rb to include the lines

require 'rr'
and
class MiniTest::Unit::TestCase
  include RR::Adapters::MiniTest
end

Making reload! work in Pry with Rails 3.2#

Mon, 23 Jan 2012 14:08:41 +0000

As of Pry 0.9.7.4 (the current version according to Bundler at the time I write this), the setup instructions for replacing irb with Pry when you run rails console no longer work fully in Rails 3.2. Specifically, the Rails team have changed the way they create irb commands like reload!: where in earlier version they were added to Object, now they are added to IRB::ExtendCommandBundle to avoid polluting the global namespace. Here's the github pull request where the change is described.

(The cynic here will say "Rails? Namespace pollution? Lost cause, mate", but hey, let's not be down on attempts to make it better)

IRB already knows how to look in IRB::ExtendCommandBundle; Pry doesn't, so what will happen if you have installed Pry in the usually recommended way by assigning IRB=Pry is you'll get an error that Pry::ExtendCommandBundle doesn't exist.

(I've seen 'fixes' for this bug that assign Pry::ExtendCommandBundle=Pry. This will make rail console start, but it still doesn't make the commands accessible. Less than entirely useful then)

So, let's make it so. Here's the relevant bit of my config: feel free to use as inspiration, but don't blame me if copy/paste doesn't work

First, in config/environments/development.rb

  silence_warnings do
    begin
      require 'pry'
      IRB = Pry
      module Pry::RailsCommands ;end
      IRB::ExtendCommandBundle = Pry::RailsCommands
    rescue LoadError
    end
  end

and then in $HOME/.pryrc we create commands for the methods in that module

if Kernel.const_defined?("Rails") then
  require File.join(Rails.root,"config","environment")
  require 'rails/console/app'
  require 'rails/console/helpers'
  Pry::RailsCommands.instance_methods.each do |name| 
    Pry::Commands.command name.to_s do 
      Class.new.extend(Pry::RailsCommands).send(name)
    end
  end
end

If you are using a newer version of Pry than me - well, first off, they may have fixed this already and if so you can ignore this whole post. But if they haven't, and if Pry::Commands.command is giving you trouble, note that the unreleased Pry 0.9.8 is set to include a new way of defining custom commands and you may need to rewrite this using the new Pry::Commands.block_command construct instead.

HTH

backbone.js 1 0 jQuery#

Sun, 29 Jan 2012 22:04:06 +0000

I've spent a few hours over the last couple of days figuring out how to use backbone.js, and so far I'm impressed by it: it solves a real problem elegantly and doesn't seem to have an entire religion bolted onto the side of it.

5 minute summary: it introduces models (and collections of them) and views to client-side javascript, and connects them with a publish/subscribe event notifier system so that when you make changes to a model all the views of it update without your having to remember to do anything to them.

A Model is an object that knows how to update itself from a Rails-y "REST" server (scare quotes, because as we all know these days REST isn't what you think it is ), and publishes its attributes using the methods set and get.

	var m=find_me_a_model();
	var selected= (m.has('selected')) ? m.get('selected') : false;
	m.set({selected:  !selected});

Calling set will, if the value has changed, trigger a changed event handler to be called in all objects which have bound to it. These objects are usually Views.

A View is an object with a render method and an el attribute, and in which calling the former creates a piece of DOM tree in the latter, which you can then attach to your document somewhere

MyApp.Views.ThingView=Backbone.View.extend({
    initialize: function() {
	this.model.bind("all",this.render,this);
	this.render();
    },
    // ... this is not working code - I missed out some important bits ...
    events: {
	"click li" : "doselect",
    },
    doselect: function(e) { ... },
    render: function() {
	var ul=$(this.el);
	ul.html(somehtmlfor(this.model));
        return this;
    }
})

jQuery(document).ready(function() { var myView=new MyApp.Views.ThingView(); $('#some_element').append(myView.render().el); });

Collections are provided too. They come with a large number of iteration functions (map, filter, reduce, all that stuff) which makes them really rather useful, and you can build Views of them in much the same way as you can build views of models.

(To complete the completion, there's also a Router, which is an interface for monkeying around with the URL so you can build bookmarkable client-side apps. But I haven't had to use that yet)

Anyway. As you see in the example above, the view can also take a hash of events which is registered with jQuery using its delegate method. In this case we're asking to have do_select called whenever a click event is received on any li element inside it. Great!

Not so great when it unexpectedly doesn't work, though. Specifically, jQuery drag/drop events don't work with jQuery's delegate method, and there's nothing in the documentation on either page to stop you wasting an afternoon finding this out. Way to go. For more details on just how much hysterical raisins mess is involved with jQuery event handlers, see the pages for on and live - look upon these works, ye mighty, and despair.

backbone.js is here There's a Ruby gem for using it with Rails: add rails-backbone to your Gemfile, and you get a handy set of generators which write Coffeescript for you. (A brief inspection of the result says that this is a good thing because there's no way on earth I'd want to write that stuff myself. But I concede, significant whitespace is a valid personal preference, just not one of mine.)

ANN Twitling: a Twitter link digest tool#

Wed, 01 Feb 2012 12:54:57 +0000

Problem: I can't keep up with the Internet

I often check Twitter on my phone. When I see tweets with links in them I tend to skip over them intending to return later when I'm on a computer with a full-size screen, and then forget about them either because I find something else to look at or I can't be bothered with scrolling all the way down again. And looking through old tweets is nearly as bad on the full-size twitter web site as it is in a mobile client.

Proposed solution: I need a computer program to read the Internet for me

Thus, Twitling: a small script consisting of Ruby and Sinatra and OmniAuth and the Twitter gem and Typhoeus to grab links in parallel, the function of which is to read one's timeline and display the resolved URL, the title and an excerpt from the text of each link that was posted. Source code is on Github.

I haven't really used it myself yet in anger: the first thing I notice while testing it is that there are a whole lot more links in my feed than I thought there were, and the original plan to produce a 24 hour digest might become very unwieldy.

Possible further development ideas include

Try not to break it, please.