<?xml version="1.0" encoding="UTF-8"?>
<article-node>
  <account-id type="integer">2</account-id>
  <author>Christopher Haupt</author>
  <aux>Rails&amp;#8217; routing infrastructure supports the concept of conditional routes: preconditions that must be satisfied before a particular route will trigger. Rails 2.1 supports one built-in condition, &lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt; method checking, which is of some use but rather limited. What I needed was to be able to limit certain routes to only trigger when a particular host-name was used to access the application. I show one implementation in this article.</aux>
  <body>&lt;p&gt;Rails&amp;#8217; routing infrastructure supports the concept of conditional routes: preconditions that must be satisfied before a particular route will trigger. Rails 2.1 supports one built-in condition, &lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt; method checking, which is of some use but rather limited. What I needed was to be able to limit certain routes to only trigger when a particular host-name was used to access the application.&lt;/p&gt;
&lt;p&gt;I thought I&amp;#8217;d have to write messy additional logic until a little comment tucked away in ActionController::Routing::RouteSet and ActionController::Routing::Routing caught my eye. Here I briefly show you how to leverage this functionality for your own purposes.&lt;/p&gt;
&lt;h2&gt;The Goal &amp;#8212; Conditional Routes in routes.rb&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s work backwards and see the result I was aiming for. I wanted to expand the existing capabilities of the routing engine and be able to restrict routes to specific hosts. The conditional routing option works by adding a parameter to your route specifications. Here are some examples:&lt;/p&gt;
&lt;pre&gt;
map.with_options(:controller =&amp;gt; 'feeds', :conditions =&amp;gt; {:hosts =&amp;gt; MY_HOSTS}) do |feed|
  feed.feeds_articles '/feeds/articles', :action =&amp;gt; 'articles'
  feed.feeds_podcast '/feeds/podcast', :action =&amp;gt; 'podcast'
end
&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;pre&gt;
map.resources :podcasts, :conditions =&amp;gt; {:hosts =&amp;gt; MY_HOSTS}, 
   :member =&amp;gt; {:show_notes =&amp;gt; :get, :transcript =&amp;gt; :get},
   :collection =&amp;gt; {:admin =&amp;gt; :get} do |podcast|
     podcast.resources :comments, :member =&amp;gt; {:report_as_ham =&amp;gt; :get, :report_as_spam =&amp;gt; :get}
   end
&lt;/pre&gt;
&lt;p&gt;or even&lt;/p&gt;
&lt;pre&gt;
map.connect ':controller/:action/:id', :conditions =&amp;gt; {:hosts =&amp;gt; MY_HOSTS}
&lt;/pre&gt;
&lt;p&gt;In Rails 2.1, however, no such option &lt;code&gt;:hosts&lt;/code&gt; exists, only an option to check the &lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt; method via &lt;code&gt;:method&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The Implementation&lt;/h2&gt;
&lt;p&gt;I haven&amp;#8217;t really ever needed to use the conditional routing support before, and didn&amp;#8217;t really think about it due to it only supporting the &lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt; method check. For that reason, I originally thought I&amp;#8217;d have to write my own logic, either patching existing Routing routines (nearly right!) or by writing new stuff that could get messy (bad idea).&lt;/p&gt;
&lt;p&gt;During a last scan through the code for the keyword &amp;#8220;conditions&amp;#8221;, I saw this comment:&lt;/p&gt;
&lt;pre&gt;
# Plugins may override this method to add other conditions, like checks on
# host, subdomain, and so forth. Note that changes here only affect route
# recognition, not generation.
&lt;/pre&gt;
&lt;p&gt;Good, a place to start afterall! The solution is elegant as it only requires overriding two simple routines. You can do this in your own app by writing code that gets loaded at startup. Here is one implementation in its entirety:&lt;/p&gt;
&lt;pre&gt;
require 'action_controller'

module ActionController
  module Routing
    class RouteSet
      def extract_request_environment(request)
        { :method =&amp;gt; request.method, :host =&amp;gt; request.host }
      end
    end

    class Route
      def recognition_conditions
        result = [&quot;(match = #{Regexp.new(recognition_pattern).inspect}.match(path))&quot;]
        result &amp;lt;&amp;lt; &quot;conditions[:method] === env[:method]&quot; if conditions[:method]
        result &amp;lt;&amp;lt; &quot;conditions[:hosts].include?(env[:host])&quot; if conditions[:hosts]
        result
      end
    end
  end
end	
&lt;/pre&gt;
&lt;p&gt;My code is very simplistic and tuned for my needs, but gives you an example of where to patch in. Here, I simply supply a list of host names I care about, and check the incoming host against that list.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;extract_request_environment&lt;/code&gt; to parse out and store any data you will want to use in your conditional checks. This data will be available in the &lt;code&gt;env&lt;/code&gt; hash later on.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;recognition_conditions&lt;/code&gt; generates an Array of String objects that contain the Ruby code that will be used to build dynamic conditional test methods when the routing engine compiles the routes data in &lt;code&gt;routes.rb&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I drop the source file into my project&amp;#8217;s pre-existing &lt;code&gt;lib/plugins/action_controller_extensions/lib&lt;/code&gt; directory as &lt;code&gt;action_controller_extensions.rb&lt;/code&gt; and include an &lt;code&gt;init.rb&lt;/code&gt; loader stub in my &lt;code&gt;lib/plugins/action_controller_extensions&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre&gt;
require 'action_controller_extensions'	
&lt;/pre&gt;
&lt;p&gt;My app deals with loading up such &amp;#8220;plugins&amp;#8221; at startup. You may have a different set-up. You can get the same effect by putting a &lt;code&gt;require&lt;/code&gt; for the main source file in your startup code.&lt;/p&gt;
&lt;p&gt;It would be great to see other generally useful conditionals contributed by the community.&lt;/p&gt;</body>
  <created-at type="datetime">2008-08-27T13:21:08-07:00</created-at>
  <created-by type="integer" nil="true"></created-by>
  <dy-schema-id type="integer" nil="true"></dy-schema-id>
  <dy-schema-type nil="true"></dy-schema-type>
  <filter-id nil="true"></filter-id>
  <flags type="integer">1</flags>
  <historic-id type="integer" nil="true"></historic-id>
  <id type="integer">7082</id>
  <kind-id type="integer">5019</kind-id>
  <lock-version type="integer">3</lock-version>
  <name>Enhancing Conditional Routing in Rails</name>
  <owner-id type="integer" nil="true"></owner-id>
  <owner-type nil="true"></owner-type>
  <published-at type="datetime">2008-08-26T17:00:00-07:00</published-at>
  <rating type="integer">4</rating>
  <ref-count type="integer">0</ref-count>
  <sequence type="integer">0</sequence>
  <status type="integer">0</status>
  <updated-at type="datetime">2009-03-23T22:59:58-07:00</updated-at>
  <updated-by type="integer">2</updated-by>
  <url nil="true"></url>
  <user-id type="integer">2</user-id>
  <version type="integer">4</version>
  <workflow-task-status-id type="integer" nil="true"></workflow-task-status-id>
</article-node>
