This is abridged for conciseness. For full post, visit
MIME Responder filter for RailsI spent last week integrating
Mobtropolis with facebook. Mobtropolis doesn't require a facebook account to use it, so like other websites, it has its own authentication mechanism, something like:
1
2 class PostController < ActionController::Base
3 before_filter :website_authenticate_filter, :except => [:index, :list]
4 end
When I started using facebooker library, it already came with an authentication before_filter. That means we have two authentication filters, one native, and one for facebook. Mobtropolis users don't have to be in facebook to use it, and facebookers don't have to sign up again in mobtropolis to use it.
However, since before_filters are executed in succession, it leads to a case where the facebook authentication would be called if html was requested, and vice versa. The alternative was to take apart both authentication filters, and create a monolithic filter to handle the two different cases. Instead, I made a before_filter respond to different MIME types.
1
2 class PostController < ActionController::Base
3 before_respond_to_filter :except => [ :index, :list ] do |format|
4 format.html :website_authentication_filter
5 format.fbml :facebook_authentication_filter
6 end
7 end
That way, I didn't have to mix together the guts of each authentication filter, and it solved the problem of the wrong authentication filter being run. You can also use it like:
1
2 class PostController < ActionController::Base
3 before_responds_to_filter :only => :home do |format|
4 format.html do |controller|
5 return if controller.logged_in?
6 controller.send(:redirect_to, :controller => :home)
7 end
8 format.fbml :ensure_application_is_installed_by_facebook_user
9 end
10 end
It ended up the code for this sort of magic was fairly easy. I'm not sure if there's an easier way to do what I wanted, but I'll see if Rails core people would find it useful (or not). In the meanwhile, for those of you Rubyists that have written plugins before that want to play with it. As with the usual mumbo jumbo, it's provided as is, I'm not maintaining it, and do whatever you want with it:
1
2
3 require 'mime_responder_filter'
4 ActionController::Base.send :include, Threecglabs::Filters::MimeResponderFilter
1
2
3 module Threecglabs
4 module Filters
5
6
7 module MimeResponderFilter
8
9 def self.included(mod)
10 mod.extend(ClassMethods)
11 end
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 module ClassMethods
37 def before_respond_to_filter(options = {}, &block)
38 before_filter MimeResponderFilter.new(&block), options
39 end
40
41 private
42
43 class MimeResponderFilter
44 attr_reader :filters
45
46 def initialize(&block)
47 @filters = {}
48 block.call(self)
49 end
50
51 def filter(controller)
52 filter = @filters[controller.request.format.to_sym] || @filters[:html]
53 if filter.kind_of?(Proc)
54 filter.call(controller)
55 else
56 controller.send!(filter)
57 end
58 end
59
60
61 def method_missing(mime_type, method_name = nil, &block)
62 if block_given?
63 @filters[mime_type.to_sym] = block
64 else
65 @filters[mime_type.to_sym] = method_name.to_sym
66 end
67 end
68 end
69 end
70
71 end
72 end
73 end
Snippet!