XHR Redirects

All my ‘Ajax’ requests, use simple xhr when they want to load html, and not .js ,which imo is a misuse since requesting .js should yield a json response.

The first thing i always do is define a xhr layout, which usually is nil. Who ever thought that any xhr request should ever respond with stylesheets and javascript?

layout proc {|controller| controller.request.xhr? ? nil :  'application' }

Working this way i came to some limitations of the xhr approach:

  • cannot easily request from the browser
  • cannot redirect

Redirect an xhr ?

When a user requests something in a Lightbox, it is render using xhr. When the requested resource is not accessible by this user, he should see a login screen. This can be done with a normal redirect, which the JS Framework(at least jQuery does) uses to render the login page inside the lightbox.

Sounds good? But does not work…

Redirects will have the default layout, since they are no longer xhr request, which messes up the lightbox.

Xhr redirects

With this xhr redirect plugin the problem should go away.

_xhr=1

The plugin uses an added parameter _xhr to signal if a request is xhr or not, so now one can also request a page with _xhr=1 and see the actual xhr response.

Separate Rights Management from Controllers

Update: new controller methods inspired by Nicolás Sanguinetti

All to often Controllers verify if a certain user can say “create or edit a resource”, which at first seems to be a natural place for this kind of logic, but alas it is not!

When building your views you need access to the same knowledge, if the user can edit this movie, show the edit link.

Is it mine?

At first i solved this with a simple is_mine?(obj) on the user, and let the user decide if he can edit a record.

def is_mine?(obj)
  case obj
    when Movie then obj.id == id
    when Participation then obj.movie.id == id
   .....
end

Can i read/write it ?

But this still left too much logic for my helpers and controllers, so i added another bit: read/write, where a new User symbolizes no user, so user.can_write?(obj) returns false if it is a new_record.

  def can_read?(obj)
    raise "new objects cannot be read" if obj.new_record? #new_records can only be created, never viewed...
    case obj
      when Address,Order,OrderItem #not readable by others/public
        is_mine?(obj) #i can read an order/address if it is mine
      else not new_record?#only a logged in user can read something...
    end
  end

  def can_write?(obj)
    ...
  end

Controller-logic be gone!

Now in my controller i use can_read for show, can_write for edit/update/create/new/destroy, which i inherit on all controllers.

before_filter :can_read, :only => [:show]
before_filter :can_write, :only => [:destroy,:update,:edit,:new,:create]

def can_write
  access_denied unless can_write?(requested_object)
end

def can_write?(object)
  user = current_user || User.new
  user.can_write?(object)
end
helper_method :can_write?

The magic starts in requested_object: Create a new object of the current controller class, or find it by id. This can be a bit tricky with nested resources, but can be as simple as (see below) with make_resourceful.

  def requested_object
    case params[:action]
      when 'new','create' then build_object
      else current_object
    end
  end

Clean Tests

Testing, once cumbersome “get :new, response.should”-madness now looks like this, which is orders of magnitude faster, easier to write and more fun to read.

  it "reads/writes movies" do
    #changeable by owner
    can_read_and_write(@owner,@movie)
    can_read_not_write(@other,@movie)
    can_write(@owner,Movie.new)
    cannot_read_and_write(@no_user,@movie)
    cannot_write(@no_user,Movie.new)
  end
...
  def can_read(user,item)
    user.can_read?(item).should be_true
  end
...

I Will Paginate Acts As Searchable

UPDATE
acts_as_searchable is no longer maintained, please switch over to the new and healthy world of search_do bug-free, clear structure and paginating!

Just relesed another small plugin, to help integrate acts_as_searchable and will_paginate

Movie.paginate_by_fulltext_search(
  params[:q],
  :page=>params[:page],:per_page=>10
)

script/plugin install http://small-plugins.googlecode.com/svn/trunk/will_paginate_acts_as_searchable/

Inspired by rails20.jp

Generic Fixture Selection

I have done a lot of generic test writing in the last months, and one thing that always resurfaced was:

item = send(@obj.class.to_s.underscore.pluralize,:one)
item = send(@item_name.to_s.pluralize.:one)
...

What does it do ? It load the fixture :one for this objects class, since i always keep a fixture :one and :two around to make generic tests less painful. But all this send madness was unreadable, so i factored out some fixture calling.

#spec/spec_helper.rb OR test/test_helper.rb

#helper for generic fixture calling
#fixture 'user','one' = users('one')
#fixture @user,'one' = users('one')
#fixture User,'one' = users('one')
def fixture(singular,name)
  klas = input_to_class(singular)
  fixture = find_fixture(klas,name)
  raise "fixtures for #{klas} not loaded!" unless fixture
  fixture
end

def find_fixture(klas,name)
  table = klas.to_s.underscore.pluralize
  return send(table,name) if respond_to? table
  false
end

def input_to_class(val)
  return val if val.kind_of? Class
  return val.class if val.kind_of? ActiveRecord::Base
  return val.to_s.classify.constantize
end

Include all Helpers for Testing

When your helpers use each other, or your view tests use many, it gets frustrating to include them by hand…
This will load all helpers from app/helpers dir and include them!
Happy, stress-free testing!

def include_helpers
  Dir.glob("#{RAILS_ROOT}/app/helpers/*_helper.rb").each do |file|
    helper = File.basename(file).sub('.rb','').camelcase.constantize
    include helper
  end
end

Your Controller/View/Helper test would look like this:

describe MoviesHelper do
  include_helpers
  ...
end

Getting all other helpers(e.g. from helpers/admin is more complicated, but surely possible…)