public
Description: Radically RESTful Rails.
Homepage:
Clone URL: git://github.com/benhoskings/hammock.git
name age message
file .gitignore Thu Feb 26 03:41:34 -0800 2009 Added pkg/ to .gitignore. [benhoskings]
file History.txt Mon Dec 14 02:01:59 -0800 2009 Bumped version to 0.5.5. [benhoskings]
file LICENSE Mon Aug 04 05:23:15 -0700 2008 Added BSD license. [benhoskings]
file Manifest.txt Thu Jun 04 18:56:23 -0700 2009 Bumped version to 0.3.8. [benhoskings]
file README.rdoc Tue Jun 02 07:47:13 -0700 2009 Converted README back to rdoc, for rubyforge ra... [benhoskings]
file Rakefile Mon Oct 26 18:34:05 -0700 2009 Bumped version to 0.4.1 (using jeweler now). [benhoskings]
file VERSION Mon Dec 14 02:01:59 -0800 2009 Bumped version to 0.5.5. [benhoskings]
file hammock.gemspec Mon Dec 14 02:01:59 -0800 2009 Bumped version to 0.5.5. [benhoskings]
file init.rb Thu Jun 04 18:05:08 -0700 2009 Rewrote hammock loading code to use nestable de... [benhoskings]
directory lib/ Mon Dec 14 01:58:17 -0800 2009 Attributes in #ajaxinate link_data render as ha... [benhoskings]
directory misc/ Thu Jun 04 18:05:08 -0700 2009 Rewrote hammock loading code to use nestable de... [benhoskings]
directory public/ Sun Nov 22 00:53:07 -0800 2009 Send record/form/passed attributes in non-GET a... [benhoskings]
directory tasks/ Thu Feb 26 03:39:35 -0800 2009 Added git_to_manifest task. [benhoskings]
README.rdoc

hammock

github.com/benhoskings/hammock

DESCRIPTION:

Hammock is a Rails plugin that eliminates redundant code in a very RESTful manner. It does this in lots in lots of different places, but in one manner: it encourages specification in place of implementation.

Hammock enforces RESTful resource access by abstracting actions away from the controller in favour of a clean, model-like callback system.

Hammock tackles the hard and soft sides of security at once with a scoping security system on your models. Specify who can verb what resources under what conditions once, and everything else - the actual security, link generation, index filtering - just happens.

Hammock inspects your routes and resources to generate a routing tree for each resource. Parent resources in a nested route are handled transparently at every point - record retrieval, creation, and linking.

It makes more sense when you see how it works though. There’s a screencast coming soon.

REQUIREMENTS:

    benhoskings-ambition
    benhoskings-ambitious-activerecord

These gems will install automatically as long as you’ve added the GitHub gem source:

    gem sources -a http://gems.github.com

INSTALL:

    sudo gem install hammock

in config/environment.rb:

    Rails::Initializer.run do |config|
      config.gem 'hammock'
      ...

in app/controllers/application_controller.rb:

    class ApplicationController
      include Hammock::RestfulActions
      ...

LICENSE:

Hammock is licensed under the BSD license, which can be found in full in the LICENSE file.

SYNOPSIS

At the moment, you can do this with Hammock:

    class ApplicationController < ActionController::Base
      include Hammock::RestfulActions
    end

    class BeersController < ApplicationController
    end

    class Person < ActiveRecord::Base
    end

    class Beer < ActiveRecord::Base
      belongs_to :creator, :class_name => 'Person'
      belongs_to :recipient, :class_name => 'Person'

      def self.read_scope_for account
        L{|beer| beer.creator_id == account.id || beer.recipient_id == account.id }
      end
      export_scope :read

      # TODO - Duplication, yuck. There's a proper DSL in the pipes.
      def self.index_scope_for account
        L{|beer| beer.creator_id == account.id || beer.recipient_id == account.id }
      end
      export_scope :index

      creator_scope_for :write
    end

    <% @beers.each do |beer| %>
      From <%= beer.creator.name %> to <%= beer.recipient.name %>, <%= beer.reason %>, rated <%= beer.rating %>
      <%= hamlink_to :edit, beer %>
    <% end %>

The scope methods above require just one thing — a context-free lambda that takes an ActiveRecord record as its argument, and returns true iff that record is within the scope for the specified account. Hammock uses the method (e.g. Beer.read_scope_for) to define resource and record scopes for the model:

    Beer.readable_by(account): the set of Beer records whose existence can be known by account
    Beer#readable_by?(account): returns true if the existence of this Beer instance can be known by account

You define the logic for read, index and write scopes in Beer.[read,index,write]_scope_for, and the rest just works.

These scope definitions are exploited extensively, to provide index selection, scoping for record selection, and post-selection object checks.

  • They provide the conditions that should be applied to retrieve the index of each resource.

The scope is used transperently by Hammock on /beers -> BeersController#index, and is available for use through Beer.indexable_by(account).

  • They provide a scope within which records are searched for on single-record actions.

For example, given the request /beers/5 -> BeersController#show{:id => 5}, Rails would generate the following SQL:

    SELECT * FROM "beers" WHERE (beers."id" = 5) LIMIT 1

Hammock uses the conditions specified in Beer.read_scope_for to generate (assuming an account_id of 3):

    SELECT * FROM "beers" WHERE ((beers.creator_id = 3 OR beers.recipient_id = 3) AND beers."id" = 5) LIMIT 1

Hammock uses Beer.read_scope_for on #show, and write_scope_for on #edit, #update and #destroy. These scopes can be accessed as above through Beer.readable_by(account) and Beer.writeable_by(account). This eliminates authorization checks from the action, because if the ID of a Beer is provided that the user doesn’t have access to it will fall outside the scope and will not be found in the DB at all.

  • They are used to discover credentials for already-queried ActiveRecord objects, without touching the database again.

Just as Beer.readable_by(account) returns the set of Beer records whose existence can be known by account, @beer.readable_by?(account) returns true iff @beer’s existence can be known by account. This is employed by hamlink_to.

These three uses of the scope, plus another as-yet unimplemented bit, provide the entire security model of the application.

THE MASTER PLAN

Lots of functionality is planned that will take this much further.