An extremely common practice for Rails applications is to provide keyed access through subdomains (i.e. http://someaccount.awesomeapp.com/). However, there has never been a real unified convention for handling this functionality. DHH’s Account Location works for some circumstances but is more tailored for a Basecamp domain model (i.e. the app is on a separate domain from all other functionality, so you can always expect a subdomain) than the more common usage of one domain only.

SubdomainFu aims to provide a simple, generic toolset for dealing with subdomains in Rails applications. Rather than tie the functionality to something specific like an account, SubdomainFu simply provides a foundation upon which any subdomain-keyed system can easily be built.

Usage Fu

SubdomainFu works by riding on top of the URL Rewriting engine provided with Rails. This way you can use it anywhere you normally generate URLs: through url_for, in named routes, and in resources-based routes. There’s a small amount of configuration that is needed to get you running (though the defaults should work for most).

To set it up, you can modify any of these settings (the defaults are shown):

# in environment.rb

# These are the sizes of the domain (i.e. 0 for localhost, 1 for something.com)
# for each of your environments
SubdomainFu.tld_sizes = { :development => 0,
                          :test => 0,
                          :production => 1 }

# These are the subdomains that will be equivalent to no subdomain
SubdomainFu.mirrors = ["www"]

# This is the "preferred mirror" if you would rather show this subdomain
# in the URL than no subdomain at all.
SubdomainFu.preferred_mirror = "www"

Now when you’re in your application, you will have access to two useful features: a current_subdomain method and the URL Rewriting helpers. The current_subdomain method will give you the current subdomain or return nil if there is no subdomain or the current subdomain is a mirror:

# http://some_subdomain.myapp.com/
current_subdomain # => "some_subdomain" 

# http://www.myapp.com/ or http://myapp.com/
current_subdomain # => nil

# http://some.subdomain.myapp.com
current_subdomain # => "some.subdomain"

The URL rewriting features of SubdomainFu come through a :subdomain option passed to any URL generating method. Here are some examples (in these examples, the current page is considered to be ‘http://intridea.com/’):

url_for(:controller => "my_controller", 
  :action => "my_action", 
  :subdomain => "awesome") # => http://awesome.intridea.com/my_controller/my_action

users_url(:subdomain => false)  # => http://intridea.com/users

# The full URL will be generated if the subdomain is not the same as the
# current subdomain, regardless of whether _path or _url is used.
users_path(:subdomain => "fun") # => http://fun.intridea.com/users
users_path(:subdomain => false) # => /users

While this is just a simple set of tools, it can allow the easy creation of powerful subdomain-using tools. Note that the easiest way to locally test multiple subdomains on your app is to edit /etc/hosts and add subdomains like so:

127.0.0.1    localhost subdomain1.localhost subdomain2.localhost www.localhost

Adding an entry for each subdomain you want to use locally. Then you need to flush your local DNS cache to make sure your changes are picked up:

sudo dscacheutil -flushcache

Installation

SubdomainFu is available both as a traditional plugin and as a GemPlugin for Rails 2.1 and later. For a traditional plugin, install like so:

script/plugin install git://github.com/mbleigh/subdomain-fu.git

For a GemPlugin, add this dependency to your environment.rb:

config.gem 'mbleigh-subdomain-fu', :source => "http://gems.github.com/", :lib => "subdomain-fu"

Implementing A Simple Account Key System

Let’s take this functionality and implement a simple account-key system based off of the subdomain. We’ll start with some controller code (assuming that we have an Account model with a ‘subdomain’ field):

class ApplicationController < ActionController::Base
  protected

  # Will either fetch the current account or return nil if none is found
  def current_account
    @account ||= Account.find_by_subdomain(current_subdomain)
  end
  # Make this method visible to views as well
  helper_method :current_account

  # This is a before_filter we'll use in other controllers
  def account_required
    unless current_account
      flash[:error] = "Could not find the account '#{current_subdomain}'" 
      redirect_to :controller => "site", :action => "home", :subdomain => false
    end
  end
end

That’s really all we need for a basic setup, now let’s say we have a ProjectsController that you must specify an account to access:

class ProjectsController < ApplicationController
  # Redirect users away if no subdomain is specified
  before_filter :account_required
end

There’s lots more you can do with the plugin, but this is a simple use case that everyone can relate to.

Resources and Plans

A feature that I hoped would make it to the first release of SubdomainFu but is now a planned feature is subdomain-aware routing so that you can add conditional subdomain routes to your routes.rb file. Keep an eye out for more on that in the future.

In the meantime, the project will live at its home on Acts As Community for intermittent updates, is available on GitHub as always, and bugs/feature requests may be passed on through the Lighthouse.

The new Gem Dependencies of Rails 2.1 give developers an easier-than-ever ability to keep track of and maintain the various library dependencies inherent with any project. However, a much-overlooked additional feature of the Gem Dependencies is the ability to package traditional Rails plugins as a gem and have them hooked in properly. This article is designed as an introduction to how to write and use plugins as gems in Rails projects.

The Basics

The basic method by which this is achievable is that any plugin included through a config.gem command will automatically have the gem-packed file rails/init.rb run upon Rails’s initialization. All it takes is a little bit of effort, and any Rails plugin can be packaged as a gem and easily depended upon through gem dependencies.

You may be wondering why this is a “big deal.” Plugins are already dead simple to install in Rails (and you can even script/plugin install straight from Git now!), why do we need GemPlugins? It’s simple, really: RubyGems are a rock-solid established way of easily distributing versioned reusable bits of code. Using gems for plugins allows for a greater standardization of the way in which plugins are maintained and distributed, as well as a simple path for version-locking to ensure compatibility with legacy code etc.

Another reason that GemPlugins are important is that they provide a level of abstraction from Rails: by releasing a gem rails/init.rb you could also use the same exact code to release a Merb plugin or any other framework that supports gemified add-ons. I think you will begin to see a number of cross-framework plugins be developed as Rails gets some company and shares alike.

Using a GemPlugin

First, let’s go through the process required to use an existing gem plugin. I’m going to be using my Acts As Taggable On plugin as an example throughout because I just recently went through the process of making it available as a gem.

First, you will need to include the dependency in your environment.rb file. I’m assuming here that most plugins are going to be hosted on GitHub, but the same should be true for any gem source.

# in environment.rb

config.gem "mbleigh-acts-as-taggable-on", :source => "http://gems.github.com", :lib => "acts-as-taggable-on"

This is the standard usage of gem dependencies, and for more info on this you can see Ryan Daigle’s post or watch the RailsCast on the subject. Now assuming that you don’t already have the gem in question installed, it’s simple to grab it:

rake gems:install

This will automatically install any gem dependencies in your project, and will tell you what’s happening the same as if you had run gem install from the command line.

That’s it! Once you have successfully installed the necessary gem, you can simply start up your Rails server and the plugin will be loaded and initialized as though it were living in your vendor/plugins directory.

Now that you know how to use a GemPlugin, I’ll show you how you can take an existing plugin and gemify it quickly and painlessly.

Making a GemPlugin

Let’s say I have a plugin called awesome_fu that lives on GitHub at mbleigh/awesome-fu. I’ve already released this plugin, it works great, and now I want to make it compatible with GemPlugins.

First, let’s create a gemspec called awesome-fu.gemspec in line with the requirements for the GitHub Gem Repository. In order to make the file list, I usually find it’s easiest to “find **” in the plugin directory, then copy it into TextMate, make the modifications I need for manifest (using a regular expression to quote each of the files), and saving it in the spec. If you have only a few files in your plugin, it may be easier just to add them by hand.

Next we need to add rails/init.rb. This is a little bit troublesome, because we still want our plugin to work if installed through the traditional method, so we also need init.rb to run the same code (this is automatically fine in edge Rails). What I did for my plugin is copy all of my init.rb code into rails/init.rb and then change init.rb to the following:

require File.dirname(__FILE__) + "/rails/init"

Now they both run the same code without any kind of replication, great! This means that now I have set up my plugin to work equally as a GemPlugin or a traditional plugin with just a couple minutes of work.

All that’s left to do is switch on the RubyGem setting for my GitHub project, update the README, and push! Now anyone will be able to easily require the plugin as a gem dependency and you will get all of the accolades associated with releasing your plugin the “new and hip” way.

Caveat Coder

The one problem with GemPlugins that I have run into is that if you unpack your gems using “rake gems:unpack” the rails/init.rb file is not run on initialization. This is a known issue that is supposed (?) to be resolved but I have still experienced this problem in my experiments. Hopefully this issue will be fully resolved in edge Rails soon and the glorious future of GemPlugins can begin.

Acts As Taggable On (original post here), the tagging plugin with custom tag contexts, has gathered up some great new features over the past weeks thanks to the efforts of the community as well as fellow Intrideans Pradeep Elankumaran and Brendan Lim. I just wanted to take this opportunity to go over some of what’s new and interesting in the world of acts_as_taggable_on.

Community Fixes

First, Peter Cooper was kind enough to submit a patch that allows acts_as_taggable_on to work with Rails 2.1’s named_scope when using find_options_for_tag_counts.

Secondly, the much requested support for Single Table Inheritance is finally in! It was just a matter of using a class inheritable attribute instead of a class instance variable, and big thanks to slainer68 for hunting that down and taking the time to submit a patch.

If there’s anything you’ve hacked on to Acts As Taggable On, I urge you to submit a patch to the Lighthouse Project. I try to get new patches integrated into the codebase as quickly as possible, so please do submit anything!

During the Community Code Drive at RailsConf two great features were added: taggers and related objects.

Taggers

Tags can now have ownership, allowing for such things as User-tracked tags and more. This was a requested feature and something that I’d been looking forward to myself. Here’s the usage:

class User < ActiveRecord::Base
  acts_as_tagger
end

class Photo < ActiveRecord::Base
  acts_as_taggable_on :locations
end

@some_user.tag(@some_photo, :with => "paris, normandy", :on => :locations)
@some_user.owned_taggings
@some_user.owned_tags
@some_photo.locations_from(@some_user)

Find Related

Another request (and another great idea) is the ability to find related objects by similar tags. This is now available through the @object.find_related_on_tags syntax:

@bobby = User.find_by_name("Bobby")
@bobby.skill_list # => ["jogging", "diving"]

@frankie = User.find_by_name("Frankie")
@frankie.skill_list # => ["hacking"]

@tom = User.find_by_name("Tom")
@tom.skill_list # => ["hacking", "jogging", "diving"]

@tom.find_related_on_skills # => [<User name="Bobby">,<User name="Frankie">]
@bobby.find_related_on_skills # => [<User name="Tom">] 
@frankie.find_related_on_skills # => [<User name="Tom">] 

Gemified!

Acts As Taggable On now works as a GemPlugin in Rails. This is a new way (as of Rails 2.1) of distributing plugins as gems and having them still automatically link up and do their magic. To use it as a gem, add it to your config/environment.rb like so:

config.gem "mbleigh-acts-as-taggable-on", :source => "http://gems.github.com", :lib => "acts-as-taggable-on"

Now you should be able to get the latest version of the plugin just by running rake gems:install. However, this hasn’t been working for me so the alternative is just to install the gem directly:

gem install mbleigh-acts-as-taggable-on --source http://gems.github.com/

Now when you run your Rails app, even though it’s not in vendor/plugins it should be running! To make sure, look for this line on startup:

** acts_as_taggable_on: initialized properly

There are still a couple of issues outstanding in Rails regarding GemPlugins (if you unpack it, it will not run the initialization properly for some reason), but I wanted to give everyone the latest and greatest way to install the plugin possible. It will still work fine using the conventional methods as well.

Community and Future

I’ve been really happy with the response and support of the community, and I would like to do everything possible to cultivate future participation. To that end, I have created an Acts As Community Project for acts_as_taggable_on that will hopefully provide some casual communication about the project. Feel free to post on the wall or in the forums, and look out for additions soon.

Finally, the area of the plugin that still needs some work is tag caching. This is not a particular area of my expertise, so I’m hoping that someone from the community will write up some specs that flesh out the caching functionality in new and interesting ways.

Thanks for all of the patches, and I hope you continue to enjoy using Acts As Taggable On!

UPDATE (6/10/08): The improvements keep on rolling! After writing the post, I went off on a tangent and decided to make the plugin work both traditionally and as a gem. See more details above in the “Gemified” section.

May 28th, 2008

Heading to RailsConf

Just dropping a note that I will be attending RailsConf 2008 in Portland, OR this weekend along with the Intridea crew. Be sure to stop by the Intridea booth during the show’s expo hours (we have t-shirts!). Looking forward to it, and for some I’ll see you there!

The addition of RESTful routing and the #to_param method in Rails has undoubtedly improved both the ease and usefulness of URL generation in Rails apps. However, there has come to be a rather insidious tendency to have an auto-generated id as the true reference for a given item. This may make sense for some or even many apps, but by and large records should be referred to by their content, not their ids, in URLs if at all possible. With this goal in mind, I have released from_param, a simple addition to ActiveRecord that makes it dead simple to use better URL finders.

Example

First of all, from_param is meant to simply be the complement of to_param, that is, you should be able to pass Model.from_param(some_parameter) the same way you would pass Model.from_xml(some_xml). Let’s examine how this works in practice:

class User < ActiveRecord::Base
  def to_param
    "#{id}-#{login.downcase}" 
  end
end

class UsersController < ApplicationController
  # GET /users/1-mbleigh
  def show
    @user = User.from_param(params[:id]) # => <User id=1 login="mbleigh">
  end
end

Simple enough, and it should look very familiar except that instead of a User.find or User.find_by_id, we have User.from_param. In fact, that’s exactly what this code does: from_param by default will call find_by_id since that is how the default to_param is configured.

Now let’s take something a little more complicated: a blog post title. I definitely don’t want an id in the URL if it’s a permalink, so how can I make an arbitrary parameter found as easily as the id? Simple, just add a param column to your table! Take a look:

class Post < ActiveRecord::Base
  def to_param
    "#{created_at.strftime("%Y-%m-%d")}-#{title.gsub(" ","-").downcase.gsub(/[^a-z0-9-]/,"")}" 
  end
end

class PostsController < ApplicationController
  # GET /posts/2008-04-26-from-param-plugin-released
  @post = Post.from_param(params[:id]) # => <Post title="From Param: Plugin Released" created_at="2008-04-26">
end

From Param will auto-magically save the to_param of your model to the specified parameter column (defaults to “param” but you can set it by calling set_param_column) and then automatically know to find by that column if it exists when from_param is called. This way, all you have to do is define a to_param that will be unique to your record and everything else is handled for you!

This plugin is really quite simple, but it establishes a convention that I feel has been missing from Rails for some time: a standard method to call to retrieve a record based on its URL parameter.

Installation

To install the plugin on Edge Rails or Rails 2.1 and greater:

script/plugin install git://github.com/mbleigh/from_param.git

On earlier versions of Rails:

git clone git://github.com/mbleigh/from_param.git vendor/plugins/from_param

Resources

If you have any suggestions for expanding the usefulness of the plugin or run into any problems, please report them on the From Param Lighthouse Project.

The issue of pre-loading needed data for a Rails application has always been somewhat confusing and difficult. A great post on Rail Spikes discusses the issue in-depth and offers a number of different solutions, but ultimately they all seem just a little short of the desired simplicity. By combining a few of the ideas and adding a few of my own, I have created a seeding system that I feel is very straightforward and easy to use.

Borrowing the basic premise of the db-populate plugin, Seed Fu is based around loading ruby scripts located in db/fixtures via a Rake task. What db-populate doesn’t offer is a clear syntax for describing the records to be seeded. That’s where Seed Fu comes in.

Usage

First, just create a new ruby script in db/fixtures (and create the directory itself, obviously). Any script that you drop in this folder will be automatically run when you execute your seeding rake task. Additionally, you can load environment-specific data by adding scripts in a folder of the same name (i.e. db/fixtures/development. In these scripts you can execute arbitrary Ruby code with the full Rails environment loaded; however, you should remember that this code will be executed every time the rake task is called and should not cause duplication or destroy anything that can’t be retrieved. The syntax for Seed Fu works as follows (with a User model as an example):

# db/fixtures/users.rb
# put as many seeds as you like in

User.seed(:login, :email) do |s|
  s.login = "admin" 
  s.email = "admin@adminnerson.com" 
  s.first_name = "Bob" 
  s.last_name = "Bobson" 
end

User.seed(:login, :email) do |s|
  s.login = "michael" 
  s.email = "michael@abc.com" 
  s.first_name = "Michael" 
  s.last_name = "Bleigh" 
end

The seed method is available on any ActiveRecord. It takes as parameters the ‘constraints’ for that seeding; in other words, the fixed attributes that will not change in the record’s life. The constraints are used to find the record and update instead of creating it with the attributes provided if it already exists. This way your seeds can change without mucking with other live data on your server.

Once you have set up your fixtures, it’s simple to run them:

rake db:seed

Or if you want to run them for a targeted environment:

rake db:seed RAILS_ENV=production

Installation

In edge Rails or Rails 2.1 and beyond:

script/plugin install git://github.com/mbleigh/seed-fu.git

In previous versions of Rails:

git clone git://github.com/mbleigh/seed-fu.git vendor/plugins/seed-fu

I have some ideas for the expansion of this plugin (loading from CSV for larger datasets, etc.), so stay tuned! If you have ideas for additional features or encounter any problems, I have set up a Lighthouse project for your enjoyment.

There are a number of times when I need something like an OpenStruct with a little more power. Often times this is for API-esque calls that don’t merit a full on ActiveResource. I wrote a small class for use with my ruby-github library and wanted to make it a separate gem because I think it’s pretty useful to have around.

Usage

Basically a Mash is a Hash that acts a little more like a full-fledged object when it comes to the keyed values. Using Ruby’s method punctuation idioms, you can easily create pseudo-objects that store information in a clean, easy way. At a basic level this just means writing and reading arbitrary attributes, like so:

author = Mash.new
author.name # => nil
author.name = "Michael Bleigh" 
author.name # => "Michael Bleigh" 
author.email = "michael@intridea.com" 
author.inspect # => <Mock name="Michael Bleigh" email="michael@intridea.com">

So far that’s pretty much how an OpenStruct behaves. And, like an OpenStruct, you can pass in a hash and it will convert it. Unlike an OpenStruct, however, Mash will recursively descend, converting Hashes into Mashes so you can assign multiple levels from a single source hash. Take this as an example:

hash = { :author => {:name => "Michael Bleigh", :email => "michael@intridea.com"},
       :gems => [{:name => "ruby-github", :id => 1}, {:name => "mash", :id => 2}]}

mash = Mash.new(hash)
mash.author.name # => "Michael Bleigh" 
mash.gems.first.name # => "ruby-github"

This can be really useful if you have just parsed out XML or JSON into a hash and just want to dump it into a richer format. It’s just that easy! You can use the ? operator at the end to check for whether or not an attribute has already been assigned:

mash = Mash.new
mash.name? # => false
mash.name = "Michael Bleigh" 
mash.name? # => true

A final, and a little more difficult to understand, method modifier is a bang (!) at the end of the method. This essentially forces the Mash to initialize that value as a Mash if it isn’t already initialized (it will return the existing value if one does exist). Using this method, you can set ‘deep’ values without the hassle of going through many lines of code. Example:

mash = Mash.new
mash.author!.name = "Michael Bleigh" 
mash.author.info!.url = "http://www.mbleigh.com/" 
mash.inspect # => <Mash author=<Mash name="Michael Bleigh" info=<Mash url="http://www.mbleigh.com/">>>
mash.author.info.url # => "http://www.mbleigh.com/"

One final useful way to use the Mash library is by extending it! Subclassing Mash can give you some nice easy ways to create simple record-like objects:

class Person < Mash
  def full_name
    "#{first_name}#{" " if first_name? && last_name?}#{last_name}" 
  end
end

bob = Person.new(:first_name => "Bob", :last_name => "Bobson")
bob.full_name # => "Michael Bleigh"

For advanced usage that I’m not quite ready to tackle in a blog post, you can override assignment methods (such as name= and this behavior will be picked up even when the Mash is being initialized by cloning a Hash.

Installation

It’s available as a gem on Rubyforge, so your easiest method will be:

sudo gem install mash

If you prefer to clone the GitHub source directly:

git clone git://github.com/mbleigh/mash.git

This is all very simple but also very powerful. I have a number of projects that will be getting some Mashes now that I’ve written the library, and maybe you’ll find a use for it as well.

Though I feel bad for jumping on the bandwagon, the Google App Engine is worth talking about because it represents what could be a major critical point in the development of web applications.

To this point the production of web applications has largely been the business of startups small and large. While the development of the applications involved can sometimes be a one-person operation, the infrastructure to run a production-ready scalable app has largely been beyond the reach of individuals and hobbyists. Google’s move, while still in its early stages, could be a watershed moment in app development.

De-Brandification

One thing that the App Engine might do is begin to separate web applications from the branding/marketing needs they currently have, at least in the initial stages. The app gallery access to a list of applications, all of which are easily accessible with your Google account, provides a brand-new experience for users. Rather than hearing about a new app that happens to do something that a user wants, instead they can now actively browse through a listing of applications, all rated and all easily accessible with a single sign-on. Do you care what company makes the widgets you stick on your Dashboard or your iGoogle page?

This isn’t necessarily bad news. Apps can still generate ad revenue and charge for services, but who makes the app becomes far less of a factor than how good it is at doing its thing.

Apps Get Smaller

With a central directory and a free base infrastructure, applications do not need to be all things to all people. They don’t even need to be most things to most people. The profit model for an application simply becomes a question of revenue recouping development time. While AWS provides a much less expensive server infrastructure than traditional mediums, the value of free cannot be underestimated, especially when it comes to the individual.

If a developer knows they can host an app that can scale to thousands of users with no cost, there is a stronger incentive than ever before for ‘the hobby project’, something that the developer thinks is cool but may or may not have real-world marketability. This is a dream come true in an increasingly agile industry: ‘throw it at the wall and see what sticks’ becomes not only viable but likely preferable when the barrier to entry is lowered by such a drastic degree.

Enter the Hobbyist

This low barrier to entry is going to foster a new generation of hobbyist apps. Many, perhaps most, will likely be worthless at least in some aspect. But a few will be brilliant gems, developed by a single person on a zero dollar budget. Hobbyists can create their projects on the side, release them in the wild, and see what gets a following, all at zero cost except the time spent developing the app.

This obviously isn’t applicable to all apps and situations, and venture-backed startups will still have just as much of a place, but the entry of hobbists into the web app mainstream could be a kickstart for competition and interoperability.

Nothing if Not Consistent

The final piece of the Google App Engine puzzle is that by providing a single platform – BigTable, Python, Google Account Access, and a standardized SDK, it will be much easier for App Engine projects to interact with each other semantically since they will all be built on essentially the same structure. As a Rubyist, I certainly look forward to more language support, but I think there is some power in the one-dimensional approach that, although it limits options, also provides a consistent target.

An analog to this is video game development. Up until the most recent generation, PC gaming was considered the platform for the truly ‘hardcore.’ But for developers, a consistent platform target that was deployed in millions and millions of homes won out against the configurability of the PC market. I think the same thing could be happening in the web app world: configurability is great, but if 90% of all apps can be hit by a single offering, it may be more worth it to have the consistency. This does not include language dependence, but if the App Engine SDK were available for all the big scripting languages, it would maintain that consistency while giving developers flexibility in implementation.

There are exciting times ahead, and the community will certainly be watching as more developments arise in the App Engine arena.

While the GitHub folks have produced their own github-gem that provides some useful command-line tools for GitHub users, the library they have written isn’t your traditional API wrapper since it’s focused around using GitHub rather than getting information from GitHub.

I’ve thrown together a small library called ruby-github that provides that kind of functionality. It’s extremely simple and works with all of the currently available API but that only comes down to three read-only calls at this point. Use like so:

user = GitHub::API.user('mbleigh')
user.name # => "Michael Bleigh" 
user.repositories # => array of repositories
user.repositories.last.name # => "ruby-github" 
user.repositories.last.url # => "http://github.com/mbleigh/ruby-github" 
user.repositories.last.commits # => array of commits (see below)

commits = GitHub::API.commits('mbleigh','ruby-github')
commits.first.message # => "Moved github.rb to ruby-github.rb..." 
commits.first.id # => "1d8c21062e11bb1ecd51ab840aa13d906993f3f7" 

commit = GitHub::API.commit('mbleigh','ruby-github','1d8c21062e11bb1ecd51ab840aa13d906993f3f7')
commit.message # => "Moved github.rb to ruby-github.rb..." 
commit.added.collect{|c| c.filename} # => ["init.rb", "lib/ruby-github.rb"]

Installation

The easiest way to install ruby-github is as a gem:

gem install ruby-github

You can also install it as a Rails plugin if that’s your thing:

git clone git://github.com/mbleigh/ruby-github.git  vendor/plugins/ruby-github

Update 4/12/2008: Version 0.0.2 of the gem has been released and I have revised this post to adhere to the new gem’s requirements.

My company, Intridea are avid users of Unfuddle as it’s by far the most feature-filled-while-remaining-simple solution out there for software project management. Unfuddle comes with Subversion hosting, but it’s harder and harder to ignore the strengths of Git as a SCM solution. Not one to sit idly by when there’s hackery to be done, I put together a rough tool for anyone who has a GitHub account that provides a webhook to automatically create Unfuddle changesets.

All you need to do is grab the bridge code from It’s GitHub Home like so:

git clone git://github.com/mbleigh/github-unfuddle.git

And follow the setup instructions in the README. The webhook is running off of Sinatra and is very lightweight. Once it’s running you can start seeing changesets just by pointing your post-receive URL to the webhook, and you’ll soon start seeing updates in Unfuddle like so:

This is still in its beginning stages so there’s bound to be a kink or two, but that’s the beauty of GitHub! Just fork, fix, and pull.