spanner edited this page Sep 13, 2010 · 34 revisions
Clone this wiki locally

This is a fork of the multi_site extension. The main addition is a scoped finder that can be applied to any model by calling is_site_scoped, but there are a few other minor changes and the site detection mechanism is shifted to ApplicationController so that a current_site accessor can be made available alongside current_user,

There are several other forks of multisite that enforce some fixed but probably popular scoping patterns. I needed something a bit more flexible and robust, and I feel more comfortable with a low-level solution that hooks into ActiveRecord rather than sitting on top of radiant. The main purpose of this version is to underpin other extensions that need to be site-scoped, several of which will appear here shortly. It also makes possible a very thin scoped_admin extension.

The additions are all quite new and untried but everything is specced properly and the tests pass.

class Forum < ActiveRecord::Base

  # and you'll need a site_id column
  # but that's it

The effect is to completely hide any instance of that class that doesn’t belong to the current site. It’s as though they don’t exist. There are three main interventions, all quite simple:

  • a site association is declared and validated
  • an alias_method_chain applies with_scope to all Forum finds and calculations such that only those belonging to the current site are visible. To change the rules for that scope – eg to allow for shared forums or snippets – change the return value of Forum.scope_condition
  • a before_validation filter sets forum.site ||= current_site

(update: if you pass :shareable => true to is_site_scoped then the before_validation step is omitted and the site association is not validated for presence. This also changes the scoping rules so that items with no site are admitted: the effect is that anything with a site is scoped and anything without a site is shared)

Where current_site is a class method that returns Page.current_site, which has been set in the usual way by the controller’s before_filter :set_current_site (which I’ve moved but not changed).

(In fact the main alias is not :find but :find_every, through which I think all find operations travel, including find_by_ids and all the the attribute-based finders.)

As is usual, if Model.find(...) fails we raise a ActiveRecord::RecordNotFound but Model.find_by_name(...) finds nothing it just returns nil. If a site can’t be found during any of these operations we raise a MultiSite::SiteNotFound exception. I probably want to catch that and redirect to admin/sites (without triggering another one) but I haven’t really thought that part through yet.