<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>test/ext/object.rb</filename>
    </added>
    <added>
      <filename>test/ext/shortcuts.rb</filename>
    </added>
    <added>
      <filename>test/match/path.rb</filename>
    </added>
    <added>
      <filename>test/resources/path.rb</filename>
    </added>
    <added>
      <filename>test/runtime/configurations.rb</filename>
    </added>
    <added>
      <filename>test/runtime/request.rb</filename>
    </added>
    <added>
      <filename>test/runtime/response.rb</filename>
    </added>
    <added>
      <filename>test/views/views.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -54,7 +54,7 @@ module Waves
       def extract_path( request )
         path = request.traits.waves.path
         return path if path
-        path = request.path.split('/')
+        path = request.path.split('/').map { |e| Rack::Utils.unescape(e) }
         path.shift unless path.empty?
         request.traits.waves.path = path
       end</diff>
      <filename>lib/matchers/path.rb</filename>
    </modified>
    <modified>
      <diff>@@ -98,7 +98,9 @@ module Waves
             when Symbol, String
               begin 
                 Waves.main::Resources[ resource ]
-              rescue NameError
+              rescue NameError =&gt; e
+                Waves::Logger.debug e.to_s
+                e.backtrace.each { |t| Waves::Logger.debug &quot;    #{t}&quot; }
                 raise Waves::Dispatchers::NotFoundError
               end
               Waves.main::Resources[ resource ]</diff>
      <filename>lib/resources/mixin.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,30 +4,128 @@ module Waves
     
     class Paths
       
-      attr_accessor :request
+      def self.compiled; @compiled ||= {} ; end
       
-      include Waves::ResponseMixin
-      
-      def initialize( request ) ; @request = request ; end
+      def compiled_paths; self.class.compiled ; end
       
       def generate( template, args )
-        return &quot;/#{ args * '/' }&quot; unless template.is_a?( Array ) and not template.empty?
+        return &quot;/&quot; if template.empty?
+        if template.is_a? Array
+          if args.size == 1 and args.first.is_a? Hash
+            process_hash( template, args.first )
+          else
+            process_array( template, args)
+          end
+        else
+          &quot;/#{ args * '/' }&quot;
+        end
+      end
+      
+      def process_array( template, args )
+        template_key = template
+        compiled = compiled_paths[template_key]
+        if compiled
+          return ( compiled % args ) rescue raise [template, args].inspect
+        end
+        compilable = true
+        cpath, interpolations = &quot;&quot;, []
+        result = ( cpath % interpolations ) if template.all? do | want |
+          case want
+          when Symbol
+            cpath &lt;&lt; &quot;/%s&quot; ; interpolations &lt;&lt; args.shift
+          when String
+            cpath &lt;&lt; &quot;/#{want}&quot;
+          when true
+            compilable = false
+            cpath += &quot;/#{args.join(&quot;/&quot;)}&quot;; args = []
+          when Hash
+            compilable = false
+            key, value = want.to_a.first
+            case value
+            when true
+              cpath += &quot;/#{args.join(&quot;/&quot;)}&quot;; args = []
+            when String, Symbol
+              compilable = true
+              component = args.shift
+              cpath &lt;&lt; &quot;/%s&quot;
+              component ? interpolations &lt;&lt; component : interpolations &lt;&lt; value 
+            when Regexp
+              component = args.shift.to_s
+              raise ArgumentError, &quot;#{component} does not match #{want.inspect}&quot; unless component =~ value
+              cpath &lt;&lt; &quot;/%s&quot;; interpolations &lt;&lt; component
+            end
+          when Regexp
+            compilable = false
+            component = args.shift.to_s
+            raise ArgumentError, &quot;#{component} does not match #{want.inspect}&quot; unless component =~ want
+            cpath &lt;&lt; &quot;/%s&quot;; interpolations &lt;&lt; component
+          end
+        end
+        raise ArgumentError, &quot;Too many args&quot; unless args.empty?
+        compiled_paths[template_key] = cpath if compilable
+        result
+      end
+      
+      def process_hash( template, hash )
         path = []
-        ( &quot;/#{ path * '/' }&quot; ) if template.all? do | want |
+        ( &quot;/#{ path * '/' }&quot; ) if template.all? do |want|
           case want
-          when true then path += args
+          when Symbol
+            raise ArgumentError, &quot;Path generator needs a value for #{want.inspect}&quot; unless component = hash[want]
+            path &lt;&lt; component
           when String then path &lt;&lt; want
-          when Symbol then path &lt;&lt; args.shift
-          when Regexp then path &lt;&lt; args.shift
           when Hash
             key, value = want.to_a.first
             case value
+            when Regexp
+              raise ArgumentError, &quot;Path generator needs a value for #{want.inspect}&quot; unless component = hash[key]
+              raise ArgumentError, &quot;#{component} does not match #{want.inspect}&quot; unless component =~ value
+              path &lt;&lt; component
+            when String, Symbol
+              hash.has_key?(key) ? path &lt;&lt; hash[key] : path &lt;&lt; value
+            when true
+              raise ArgumentError, &quot;Path generator needs a value for #{want.inspect}&quot; unless component = hash[key]
+              path += [component].flatten
+            end
+          when Regexp, true
+            raise ArgumentError, &quot;Path generator can't take an args hash, as it contains a Regexp or the value true&quot;           
+          end
+        end
+      end
+      
+      def original_generate( template, args )
+        if template.is_a? Array and not template.empty?
+          path = []
+          ( &quot;/#{ path * '/' }&quot; ) if template.all? do | want |
+            case want
             when true then path += args
-            when String, Symbol, RegExp then path &lt;&lt; args.unshift
+            when String then path &lt;&lt; want
+            when Symbol then path &lt;&lt; args.shift
+            when Regexp
+              component = args.shift.to_s
+              raise ArgumentError, &quot;#{component} does not match #{want.inspect}&quot; unless component =~ want
+              path &lt;&lt; component
+            when Hash
+              key, value = want.to_a.first
+              case value
+              when true then path += args
+              when String, Symbol
+                # if no args to interpolate, use hash element value as default
+                !args.empty? ? path &lt;&lt; args.shift : path &lt;&lt; value
+              when Regexp
+                component = args.shift.to_s
+                raise ArgumentError, &quot;#{component} does not match #{want.inspect}&quot; unless component =~ value
+                path &lt;&lt; component
+              end
             end
           end
+        else
+          &quot;/#{ args * '/' }&quot;
         end
       end
+      
+      
+      
     end
   end
 </diff>
      <filename>lib/resources/paths.rb</filename>
    </modified>
    <modified>
      <diff>@@ -26,7 +26,7 @@ module Waves
     
     # Forwards logging methods to the logger.
     def self.method_missing(name,*args,&amp;block)
-      @log.send name,*args, &amp;block if @log
+      cache_method_missing name, &quot;@log.#{name} *args, &amp;block if @log&quot;, *args, &amp;block
     end
 
   end</diff>
      <filename>lib/runtime/logger.rb</filename>
    </modified>
    <modified>
      <diff>@@ -53,10 +53,16 @@ module Waves
     
     # access HTTP headers as methods
     def method_missing( name, *args, &amp;body )
-      return super unless args.empty? and body.nil?
-      key = &quot;HTTP_#{name.to_s.upcase}&quot; 
-      @request.env[ key ] if @request.env.has_key?( key )
+      if args.empty? and not body
+        cache_method_missing name, &lt;&lt;-CODE, *args, &amp;body
+          key = &quot;HTTP_#{name.to_s.upcase}&quot;
+          @request.env[ key ] if @request.env.has_key?( key )
+        CODE
+      else
+        super
+      end
     end
+    
 
     # Raise a not found exception.
     def not_found</diff>
      <filename>lib/runtime/request.rb</filename>
    </modified>
    <modified>
      <diff>@@ -36,7 +36,7 @@ module Waves
     # Methods not explicitly defined by Waves::Response are delegated to Rack::Response.
     # Check the Rack documentation for more informations
     def method_missing(name,*args)
-      @response.send(name,*args)
+      cache_method_missing name, &quot;@response.#{name} *args&quot;, *args
     end
 
   end</diff>
      <filename>lib/runtime/response.rb</filename>
    </modified>
    <modified>
      <diff>@@ -10,7 +10,7 @@ module Waves
     # Access the response.
     def response; request.response; end
     
-    def resource ; traits.waves.resource ; end
+    def resource; traits.waves.resource || ( self if self.kind_of? Waves::Resources::Mixin ) ; end
 
     def traits ; request.traits ; end
     
@@ -39,7 +39,7 @@ module Waves
     def app_name ; self.class.rootname.snake_case.to_sym ; end
     def app ; eval(  &quot;::#{app_name.to_s.camel_case}&quot; ) ; end    
     def paths( rname = nil )
-      ( rname.nil? ? resource.class.paths : app::Resources[ rname ].paths ).new( request )
+      ( rname ? app::Resources[ rname ].paths : resource.class.paths ).new
     end
 
     # these take strings or operate on the path by default</diff>
      <filename>lib/runtime/response_mixin.rb</filename>
    </modified>
    <modified>
      <diff>@@ -25,7 +25,9 @@ module Waves
   def self.version ; File.read( File.expand_path( &quot;#{File.dirname(__FILE__)}/../../doc/VERSION&quot; ) ) ; end
   def self.license ; File.read( File.expand_path( &quot;#{File.dirname(__FILE__)}/../../doc/LICENSE&quot; ) ) ; end
   
-  def self.method_missing(name,*args,&amp;block) ; instance.send(name,*args,&amp;block) ; end
+  def self.method_missing(name,*args,&amp;block)
+    cache_method_missing name, &quot;instance.#{name}( *args, &amp;block)&quot;, *args, &amp;block
+  end
 
   # A Waves::Runtime takes an inert application module and gives it concrete, pokeable form.
   # Waves::Server and Waves::Console are types of runtime.</diff>
      <filename>lib/runtime/runtime.rb</filename>
    </modified>
    <modified>
      <diff>@@ -12,3 +12,5 @@ Bacon::Context.module_eval do
   alias_method :specification, :describe
   alias_method :feature, :it
 end
+
+Bacon.summary_on_exit
\ No newline at end of file</diff>
      <filename>test/helpers.rb</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>test/match/uri.rb</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>2518fa029d6fcb6d20c861c290521454f5f12bd7</id>
    </parent>
    <parent>
      <id>f4313a2bf08dbbbcdd40e0e81143a361d33b8991</id>
    </parent>
  </parents>
  <author>
    <name>Jason Rush</name>
    <email>jrush@ypcmc03420.yellowpages.local</email>
  </author>
  <url>http://github.com/dyoder/waves/commit/982d16c6d9cdfa10f94a731c43cf35869c497411</url>
  <id>982d16c6d9cdfa10f94a731c43cf35869c497411</id>
  <committed-date>2009-01-06T09:09:59-08:00</committed-date>
  <authored-date>2009-01-06T09:09:59-08:00</authored-date>
  <message>merged</message>
  <tree>cd4b1d9c8aa7abf87f48cc38cbb692cde3f9e37c</tree>
  <committer>
    <name>Jason Rush</name>
    <email>jrush@ypcmc03420.yellowpages.local</email>
  </committer>
</commit>
