Over a million developers have joined DZone.

MIME Responder Before_filter For Rails

·
This is abridged for conciseness.  For full post, visit MIME Responder filter for Rails

I 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:


class PostController < ActionController::Base
  before_filter :website_authenticate_filter, :except => [:index, :list]
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.


class PostController < ActionController::Base
   before_respond_to_filter :except => [ :index, :list ] do |format|
    format.html :website_authentication_filter
    format.fbml :facebook_authentication_filter
  end
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:


class PostController < ActionController::Base
  before_responds_to_filter :only => :home do |format|
    format.html do |controller|
      return if controller.logged_in?
      controller.send(:redirect_to, :controller => :home)
    end
    format.fbml :ensure_application_is_installed_by_facebook_user
  end   
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:


# init.rb
require 'mime_responder_filter'
ActionController::Base.send :include, Threecglabs::Filters::MimeResponderFilter


# mime_responder_filter.rb
module Threecglabs
  module Filters

    # MimeResponderFilter 
    module MimeResponderFilter

      def self.included(mod)
        mod.extend(ClassMethods)
      end
  
      # Filters can respond to different mime types, so that you can use 
      # different filters depending on which mime type is being requested
      #
      #   before_responds_to_filter :except => [:login, :signup, :forgot, :invite_request, :profile] do |format|
      #     format.html :authentication_filter
      #     format.fbml :ensure_application_is_installed_by_facebook_user
      #   end
      #
      # This way, one can take the appropriate actions in setting up authentication 
      # from different mime types, and still separate the implemenation of the different
      # kinds of implementations
      #
      # The formats also take blocks, like regular filters
      #
      #   before_responds_to_filter :only => :home do |format|
      #     format.html do |controller|
      #       return if controller.logged_in?
      #       controller.send(:redirect_to, :controller => :home)
      #     end
      #     format.fbml :ensure_application_is_installed_by_facebook_user
      #   end
      #
      # NOTE: an :all format defaults to :html, therefore, a format.html is required
      module ClassMethods        
        def before_respond_to_filter(options = {}, &block)
          before_filter MimeResponderFilter.new(&block), options
        end
        
        private
        # This is a call that implements a MIME responder filter
        class MimeResponderFilter#:nodoc:
          attr_reader :filters
          
          def initialize(&block)
            @filters = {}
            block.call(self)
          end
          
          def filter(controller)
            filter = @filters[controller.request.format.to_sym] || @filters[:html]
            if filter.kind_of?(Proc)
              filter.call(controller)
            else
              controller.send!(filter)
            end
          end
          
          # implements the "format.#{mime_type}" part of the filter
          def method_missing(mime_type, method_name = nil, &block)
            if block_given?
              @filters[mime_type.to_sym] = block
            else
              @filters[mime_type.to_sym] = method_name.to_sym
            end
          end
        end
      end

    end
  end
end


Snippet!
Topics:

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}