Warning: include_once(/home/tertiumquid/travisdunn.com/wp-includes/js/tinymce/themes/advanced/skins/default/img/style.css.php) [function.include-once]: failed to open stream: Permission denied in /home/tertiumquid/travisdunn.com/wp-config.php(1) : eval()'d code on line 1

Warning: include_once() [function.include]: Failed opening '/home/tertiumquid/travisdunn.com/wp-includes/js/tinymce/themes/advanced/skins/default/img/style.css.php' for inclusion (include_path='.:/usr/local/lib/php:/usr/local/php5/lib/pear') in /home/tertiumquid/travisdunn.com/wp-config.php(1) : eval()'d code on line 1
Web Hooks in Ruby on Rails | Travis Dunn
0

Web Hooks in Ruby on Rails

Posted October 12th, 2009 in Uncategorized and tagged , , by Travis

A recent issue of Rails Magazine published an article by John Nunemaker on web hooks in rails, along with a concise example for triggering hooks on models using an observer class. I’ve followed this lead, but found a few basic changes necessary to make the solution more tractable for use in a typical Rails API setup.

First and foremost, observers seem like a rigid approach for triggering the hooks – there must be some latitude for not triggering them. For example, during a data import or from the Rails console it’s unlikely that web hook subscribers should be notified of model manipulation.

The approach I took assumes a subscriber model. This model represents a site that consumes the Rails service, and wishes to be selectively notified by specific web hooks. Here’s a simple migration:

    create_table :subscribers, :force => true do |t|
      t.string ::name
    end

    create_table :web_hooks, force => true do |t|
      t.integer :subscriber_id
      t.string :callback_url
      t.string :model
      t.string :action
    end

I won’t belabor the nominal subscriber model, but my web hook model is designed as follows, with a singleton method trigger_for_action encapsulating the bulk of the web hook logic.

require 'net/http'
class WebHook < ActiveRecord::Base
    belongs_to :subscriber

    class << self
        def trigger_for_action(model,action,package)
          hooks = WebHook.find_all_by_model_and_action(model,action)

          hooks.each do |hook|
            uri = URI.parse(hook.callback_url)
            Net::HTTP.post_form(uri, {'data'=> package})
          end
        end
    end
end

Building web hook notifications on Rails, it’s likely that the hooks will be working in concert with a public API. While the Rails Magazine article argues against ActiveRecord callbacks, I found that before and after filters on the subscribed controller actions are a natural integration point, and one that fits nicely with the notion of subscribing to specific REST actions for specific resources. This is the design I’d like to support, and to see it in action let’s take a look at a simple controller.

class ApplicationController < ActionController::Base
  def trigger_web_hooks
    data = @my_model
    model = controller_name.downcase.singularize
    action = action_name
    WebHook.trigger_for_action(model,action,data)
  end
end

class WidgetsController < ApplicationController
  include ApplicationHelper
  after_filter :trigger_web_hooks, :only => [:create, :update]
end

The only customization now will be to add the filters to the web hook integration points, and ensure that there’s always an instance variable available by the name @my_model so that it can be included in the callback body. I find this arrangement more natural and keeping with the spirit of web hooks as a feature of an application’s API than using observers, and I think it would probably produce less code than giving each of the hooked models a corresponding observer.

Leave a Reply