serve 1.5.1 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +18 -18
 - data/Gemfile.lock +64 -61
 - data/README.rdoc +14 -2
 - data/VERSION +1 -1
 - data/lib/serve.rb +2 -0
 - data/lib/serve/application.rb +1 -1
 - data/lib/serve/bootstrap/Gemfile +4 -1
 - data/lib/serve/bootstrap/README.md +2 -7
 - data/lib/serve/bootstrap/config.ru +6 -2
 - data/lib/serve/handlers/coffee_handler.rb +18 -0
 - data/lib/serve/handlers/dynamic_handler.rb +11 -65
 - data/lib/serve/handlers/file_type_handler.rb +34 -14
 - data/lib/serve/handlers/less_handler.rb +1 -1
 - data/lib/serve/handlers/redirect_handler.rb +9 -5
 - data/lib/serve/handlers/sass_handler.rb +6 -8
 - data/lib/serve/pipeline.rb +119 -0
 - data/lib/serve/rack.rb +3 -8
 - data/lib/serve/router.rb +12 -8
 - data/lib/serve/templates/default/views/view_helpers.rb +2 -2
 - data/lib/serve/view_helpers.rb +14 -12
 - data/spec/fixtures/directory/_layout.erb +3 -0
 - data/spec/fixtures/directory/coffee.coffee +0 -0
 - data/spec/fixtures/directory/markdown.html.markdown +0 -0
 - data/spec/fixtures/directory/markdown.markdown +0 -0
 - data/spec/fixtures/directory/markdown_erb.markdown.erb +0 -0
 - data/spec/fixtures/directory/subdirectory/test.erb +1 -0
 - data/spec/fixtures/file.erb +1 -0
 - data/spec/pipeline_spec.rb +67 -0
 - data/spec/router_spec.rb +4 -2
 - data/spec/views/view_helpers.rb +7 -7
 - metadata +259 -152
 
| 
         @@ -9,29 +9,49 @@ module Serve #:nodoc: 
     | 
|
| 
       9 
9 
     | 
    
         
             
                    FileTypeHandler.handlers[ext] = self
         
     | 
| 
       10 
10 
     | 
    
         
             
                  end
         
     | 
| 
       11 
11 
     | 
    
         
             
                end
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
                def self. 
     | 
| 
       14 
     | 
    
         
            -
                   
     | 
| 
       15 
     | 
    
         
            -
                    handlers[ext.sub(/\A\./, '')]
         
     | 
| 
       16 
     | 
    
         
            -
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def self.extensions
         
     | 
| 
      
 14 
     | 
    
         
            +
                  handlers.keys
         
     | 
| 
       17 
15 
     | 
    
         
             
                end
         
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
                def  
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def self.handlers_for(path)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  extensions = File.basename(path).split(".")[1..-1]
         
     | 
| 
      
 19 
     | 
    
         
            +
                  extensions.collect{|e| [handlers[e], e] if handlers[e]}.compact
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def self.configure(extension, options)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  extension_options[extension.to_sym].merge!(options)
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def self.extension_options
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @extension_options ||= Hash.new{|h,k| h[k] = {}}
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def self.options_for(extension)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  extension_options[extension.to_sym]
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                attr_reader :extension
         
     | 
| 
      
 35 
     | 
    
         
            +
                def initialize(root_path, template_path, extension)
         
     | 
| 
       20 
36 
     | 
    
         
             
                  @root_path = root_path
         
     | 
| 
       21 
     | 
    
         
            -
                  @ 
     | 
| 
      
 37 
     | 
    
         
            +
                  @template_path = template_path
         
     | 
| 
      
 38 
     | 
    
         
            +
                  @extension = extension
         
     | 
| 
       22 
39 
     | 
    
         
             
                end
         
     | 
| 
       23 
40 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
                def process( 
     | 
| 
       25 
     | 
    
         
            -
                   
     | 
| 
       26 
     | 
    
         
            -
                  response.body = parse(open(@script_filename){|io| io.read })
         
     | 
| 
      
 41 
     | 
    
         
            +
                def process(input, context)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  parse(input, context)
         
     | 
| 
       27 
43 
     | 
    
         
             
                end
         
     | 
| 
       28 
44 
     | 
    
         | 
| 
       29 
45 
     | 
    
         
             
                def content_type
         
     | 
| 
       30 
46 
     | 
    
         
             
                  'text/html'
         
     | 
| 
       31 
47 
     | 
    
         
             
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                def layout?
         
     | 
| 
      
 50 
     | 
    
         
            +
                  true
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
       32 
52 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
                def parse( 
     | 
| 
       34 
     | 
    
         
            -
                   
     | 
| 
      
 53 
     | 
    
         
            +
                def parse(input, context)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  input.dup
         
     | 
| 
       35 
55 
     | 
    
         
             
                end
         
     | 
| 
       36 
56 
     | 
    
         
             
              end
         
     | 
| 
       37 
     | 
    
         
            -
            end
         
     | 
| 
      
 57 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -2,13 +2,17 @@ module Serve #:nodoc: 
     | 
|
| 
       2 
2 
     | 
    
         
             
              class RedirectHandler < FileTypeHandler  #:nodoc:
         
     | 
| 
       3 
3 
     | 
    
         
             
                extension 'redirect'
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
                def process( 
     | 
| 
       6 
     | 
    
         
            -
                  lines =  
     | 
| 
      
 5 
     | 
    
         
            +
                def process(input, context)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  lines = input.strip.split("\n")
         
     | 
| 
       7 
7 
     | 
    
         
             
                  url = lines.last.strip
         
     | 
| 
       8 
     | 
    
         
            -
                  unless url =~ %r{^\w[\w 
     | 
| 
       9 
     | 
    
         
            -
                    url = request.protocol + request.host_with_port + url
         
     | 
| 
      
 8 
     | 
    
         
            +
                  unless url =~ %r{^\w[\w+.-]*:.*}
         
     | 
| 
      
 9 
     | 
    
         
            +
                    url = context.request.protocol + context.request.host_with_port + url
         
     | 
| 
       10 
10 
     | 
    
         
             
                  end
         
     | 
| 
       11 
     | 
    
         
            -
                  response.redirect(url, '302')
         
     | 
| 
      
 11 
     | 
    
         
            +
                  context.response.redirect(url, '302')
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def layout?
         
     | 
| 
      
 15 
     | 
    
         
            +
                  false
         
     | 
| 
       12 
16 
     | 
    
         
             
                end
         
     | 
| 
       13 
17 
     | 
    
         
             
              end
         
     | 
| 
       14 
18 
     | 
    
         
             
            end
         
     | 
| 
         @@ -7,20 +7,18 @@ module Serve #:nodoc: 
     | 
|
| 
       7 
7 
     | 
    
         
             
              class SassHandler < FileTypeHandler #:nodoc:
         
     | 
| 
       8 
8 
     | 
    
         
             
                extension 'sass', 'scss'
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
                def parse(string)
         
     | 
| 
      
 10 
     | 
    
         
            +
                def parse(string, context)
         
     | 
| 
       11 
11 
     | 
    
         
             
                  require 'sass'
         
     | 
| 
       12 
12 
     | 
    
         
             
                  engine = Sass::Engine.new(string,
         
     | 
| 
       13 
     | 
    
         
            -
                    :load_paths => [@root_path],
         
     | 
| 
      
 13 
     | 
    
         
            +
                    :load_paths => [@root_path] + Sass::Engine::DEFAULT_OPTIONS[:load_paths],
         
     | 
| 
       14 
14 
     | 
    
         
             
                    :style => :expanded,
         
     | 
| 
       15 
     | 
    
         
            -
                    : 
     | 
| 
       16 
     | 
    
         
            -
                    :syntax => syntax(@script_filename)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    :syntax => syntax
         
     | 
| 
       17 
16 
     | 
    
         
             
                  )
         
     | 
| 
       18 
17 
     | 
    
         
             
                  engine.render
         
     | 
| 
       19 
18 
     | 
    
         
             
                end
         
     | 
| 
       20 
19 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
                def syntax 
     | 
| 
       22 
     | 
    
         
            -
                   
     | 
| 
       23 
     | 
    
         
            -
                  if ext == '.scss'
         
     | 
| 
      
 20 
     | 
    
         
            +
                def syntax
         
     | 
| 
      
 21 
     | 
    
         
            +
                  if extension == 'scss'
         
     | 
| 
       24 
22 
     | 
    
         
             
                    :scss
         
     | 
| 
       25 
23 
     | 
    
         
             
                  else
         
     | 
| 
       26 
24 
     | 
    
         
             
                    :sass
         
     | 
| 
         @@ -31,4 +29,4 @@ module Serve #:nodoc: 
     | 
|
| 
       31 
29 
     | 
    
         
             
                  'text/css'
         
     | 
| 
       32 
30 
     | 
    
         
             
                end
         
     | 
| 
       33 
31 
     | 
    
         
             
              end
         
     | 
| 
       34 
     | 
    
         
            -
            end
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,119 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'serve/view_helpers'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Serve
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Pipeline
         
     | 
| 
      
 5 
     | 
    
         
            +
                def self.handles?(path)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  !FileTypeHandler.handlers_for(path).empty?
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def self.build(root, path)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  return nil unless handles?(path)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  Pipeline.new(root, path, extensions_for(path))
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
                
         
     | 
| 
      
 14 
     | 
    
         
            +
                attr_reader :template, :layout
         
     | 
| 
      
 15 
     | 
    
         
            +
                def initialize(root_path, path)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @root_path = root_path
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @template = Template.new(File.join(@root_path, path))
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @layout = find_layout_for(@template.path)
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def find_layout_for(template_path)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  return Template::Passthrough.new(@template) unless @template.layout?
         
     | 
| 
      
 23 
     | 
    
         
            +
                  root = @root_path
         
     | 
| 
      
 24 
     | 
    
         
            +
                  layout = nil
         
     | 
| 
      
 25 
     | 
    
         
            +
                  search = File.split(template_path[root.size..-1])
         
     | 
| 
      
 26 
     | 
    
         
            +
                  until(layout || search.empty?)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    possible_layouts = FileTypeHandler.extensions.map do |ext|
         
     | 
| 
      
 28 
     | 
    
         
            +
                      l = "_layout.#{ext}"
         
     | 
| 
      
 29 
     | 
    
         
            +
                      possible_layout = File.join(File.join(root, *search), l)
         
     | 
| 
      
 30 
     | 
    
         
            +
                      File.file?(possible_layout) ? possible_layout : false
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
                    layout = possible_layouts.detect { |o| o }
         
     | 
| 
      
 33 
     | 
    
         
            +
                    search.pop
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                  if layout
         
     | 
| 
      
 36 
     | 
    
         
            +
                    Template.new(layout)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  else
         
     | 
| 
      
 38 
     | 
    
         
            +
                    Template::Passthrough.new(@template)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
                
         
     | 
| 
      
 42 
     | 
    
         
            +
                def process(request, response)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  response.headers['Content-Type'] = @layout.content_type
         
     | 
| 
      
 44 
     | 
    
         
            +
                  context = Context.new(@root_path, request, response)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  @template.process(context)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  @layout.process(context)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  response.body = context.content
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                class Template
         
     | 
| 
      
 51 
     | 
    
         
            +
                  attr_reader :file, :path, :handlers
         
     | 
| 
      
 52 
     | 
    
         
            +
                  def initialize(file)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    @file = File.basename(file)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    @path = File.dirname(file)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    @raw = File.read(file)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    @handlers = FileTypeHandler.handlers_for(file).collect{|h, extension| h.new(@root_path, @path, extension)}
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  def content_type
         
     | 
| 
      
 60 
     | 
    
         
            +
                    @handlers.first.content_type
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  def process(context)
         
     | 
| 
      
 64 
     | 
    
         
            +
                    context.content = @handlers.reverse.inject(@raw.dup) do |body, handler|
         
     | 
| 
      
 65 
     | 
    
         
            +
                      handler.process(body, context)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  def layout?
         
     | 
| 
      
 70 
     | 
    
         
            +
                    @handlers.first.layout?
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  def passthrough?
         
     | 
| 
      
 74 
     | 
    
         
            +
                    false
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  class Passthrough
         
     | 
| 
      
 78 
     | 
    
         
            +
                    def initialize(template)
         
     | 
| 
      
 79 
     | 
    
         
            +
                      @template = template
         
     | 
| 
      
 80 
     | 
    
         
            +
                    end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                    def process(context)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    def layout?
         
     | 
| 
      
 86 
     | 
    
         
            +
                      false
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                    def content_type
         
     | 
| 
      
 90 
     | 
    
         
            +
                      @template.content_type
         
     | 
| 
      
 91 
     | 
    
         
            +
                    end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                    def passthrough?
         
     | 
| 
      
 94 
     | 
    
         
            +
                      true
         
     | 
| 
      
 95 
     | 
    
         
            +
                    end
         
     | 
| 
      
 96 
     | 
    
         
            +
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                class Context #:nodoc:
         
     | 
| 
      
 100 
     | 
    
         
            +
                  attr_accessor :content, :parser
         
     | 
| 
      
 101 
     | 
    
         
            +
                  attr_reader :request, :response
         
     | 
| 
      
 102 
     | 
    
         
            +
                  
         
     | 
| 
      
 103 
     | 
    
         
            +
                  def initialize(root_path, request, response)
         
     | 
| 
      
 104 
     | 
    
         
            +
                    @root_path, @request, @response = root_path, request, response
         
     | 
| 
      
 105 
     | 
    
         
            +
                    @content = ''
         
     | 
| 
      
 106 
     | 
    
         
            +
                    install_view_helpers
         
     | 
| 
      
 107 
     | 
    
         
            +
                  end
         
     | 
| 
      
 108 
     | 
    
         
            +
                  
         
     | 
| 
      
 109 
     | 
    
         
            +
                  def install_view_helpers
         
     | 
| 
      
 110 
     | 
    
         
            +
                    view_helpers_file_path = @root_path + '/view_helpers.rb'
         
     | 
| 
      
 111 
     | 
    
         
            +
                    if File.file?(view_helpers_file_path)
         
     | 
| 
      
 112 
     | 
    
         
            +
                      singleton_class.module_eval(File.read(view_helpers_file_path) + "\ninclude ViewHelpers", view_helpers_file_path)
         
     | 
| 
      
 113 
     | 
    
         
            +
                    end
         
     | 
| 
      
 114 
     | 
    
         
            +
                  end
         
     | 
| 
      
 115 
     | 
    
         
            +
                  
         
     | 
| 
      
 116 
     | 
    
         
            +
                  include Serve::ViewHelpers
         
     | 
| 
      
 117 
     | 
    
         
            +
                end
         
     | 
| 
      
 118 
     | 
    
         
            +
              end
         
     | 
| 
      
 119 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/serve/rack.rb
    CHANGED
    
    | 
         @@ -61,8 +61,6 @@ module Serve 
     | 
|
| 
       61 
61 
     | 
    
         
             
              # A specialized hash for the environment variables on a request.
         
     | 
| 
       62 
62 
     | 
    
         
             
              # Borrowed from ActionDispatch in Rails.
         
     | 
| 
       63 
63 
     | 
    
         
             
              class Headers < Hash
         
     | 
| 
       64 
     | 
    
         
            -
                extend ActiveSupport::Memoizable
         
     | 
| 
       65 
     | 
    
         
            -
                
         
     | 
| 
       66 
64 
     | 
    
         
             
                def initialize(*args)
         
     | 
| 
       67 
65 
     | 
    
         
             
                  if args.size == 1 && args[0].is_a?(Hash)
         
     | 
| 
       68 
66 
     | 
    
         
             
                    super()
         
     | 
| 
         @@ -83,9 +81,8 @@ module Serve 
     | 
|
| 
       83 
81 
     | 
    
         
             
                private
         
     | 
| 
       84 
82 
     | 
    
         
             
                  # Converts a HTTP header name to an environment variable name.
         
     | 
| 
       85 
83 
     | 
    
         
             
                  def env_name(header_name)
         
     | 
| 
       86 
     | 
    
         
            -
                    "HTTP_#{header_name.upcase.gsub(/-/, '_')}"
         
     | 
| 
      
 84 
     | 
    
         
            +
                    @env_name ||= "HTTP_#{header_name.upcase.gsub(/-/, '_')}"
         
     | 
| 
       87 
85 
     | 
    
         
             
                  end
         
     | 
| 
       88 
     | 
    
         
            -
                  memoize :env_name
         
     | 
| 
       89 
86 
     | 
    
         
             
              end
         
     | 
| 
       90 
87 
     | 
    
         | 
| 
       91 
88 
     | 
    
         
             
              class RackAdapter
         
     | 
| 
         @@ -111,11 +108,9 @@ module Serve 
     | 
|
| 
       111 
108 
     | 
    
         
             
                    path = Serve::Router.resolve(@root, request.path_info)
         
     | 
| 
       112 
109 
     | 
    
         
             
                    if path
         
     | 
| 
       113 
110 
     | 
    
         
             
                      # Fetch the file handler for a file with a given extension/
         
     | 
| 
       114 
     | 
    
         
            -
                       
     | 
| 
       115 
     | 
    
         
            -
                      handler = Serve::FileTypeHandler.handlers[ext]
         
     | 
| 
       116 
     | 
    
         
            -
                      if handler
         
     | 
| 
      
 111 
     | 
    
         
            +
                      if Serve::Pipeline.handles?(path)
         
     | 
| 
       117 
112 
     | 
    
         
             
                        # Handler exists? Process the request and response.
         
     | 
| 
       118 
     | 
    
         
            -
                         
     | 
| 
      
 113 
     | 
    
         
            +
                        Serve::Pipeline.new(@root, path).process(request, response)
         
     | 
| 
       119 
114 
     | 
    
         
             
                        response
         
     | 
| 
       120 
115 
     | 
    
         
             
                      else
         
     | 
| 
       121 
116 
     | 
    
         
             
                        # Handler doesn't exist? Rewrite the request to use the new path.
         
     | 
    
        data/lib/serve/router.rb
    CHANGED
    
    | 
         @@ -11,23 +11,23 @@ module Serve 
     | 
|
| 
       11 
11 
     | 
    
         
             
                  full_path = File.join(root, path)
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
                  case
         
     | 
| 
       14 
     | 
    
         
            -
                  when File.file?(full_path)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  when File.file?(best_match(full_path))
         
     | 
| 
       15 
15 
     | 
    
         
             
                    # A file exists! Return the matching path.
         
     | 
| 
       16 
16 
     | 
    
         
             
                    path
         
     | 
| 
       17 
     | 
    
         
            -
                  when File.directory?(full_path) 
     | 
| 
      
 17 
     | 
    
         
            +
                  when File.directory?(best_match(full_path))
         
     | 
| 
       18 
18 
     | 
    
         
             
                    # It's a directory? Try a directory index.
         
     | 
| 
       19 
19 
     | 
    
         
             
                    resolve(root, File.join(path, 'index'))
         
     | 
| 
       20 
     | 
    
         
            -
                  when path 
     | 
| 
      
 20 
     | 
    
         
            +
                  when path =~ /\.css\Z/i
         
     | 
| 
       21 
21 
     | 
    
         
             
                    # CSS not found? Try SCSS or Sass.
         
     | 
| 
       22 
22 
     | 
    
         
             
                    alternates = %w{.scss .sass}.map { |ext| path.sub(/\.css\Z/, ext) }
         
     | 
| 
       23 
     | 
    
         
            -
                     
     | 
| 
       24 
     | 
    
         
            -
                      File.file?(File.join(root, p))
         
     | 
| 
       25 
     | 
    
         
            -
                    end 
     | 
| 
      
 23 
     | 
    
         
            +
                    alternates.find do |p|
         
     | 
| 
      
 24 
     | 
    
         
            +
                      File.file?(best_match(File.join(root, p)))
         
     | 
| 
      
 25 
     | 
    
         
            +
                    end
         
     | 
| 
       26 
26 
     | 
    
         
             
                  else
         
     | 
| 
       27 
27 
     | 
    
         
             
                    # Still no luck? Check to see if a file with an extension exists by that name.
         
     | 
| 
       28 
28 
     | 
    
         
             
                    # TODO: Return a path with an extension based on priority, not just the first found.
         
     | 
| 
       29 
     | 
    
         
            -
                    result =  
     | 
| 
       30 
     | 
    
         
            -
                    result.sub(/^#{root} 
     | 
| 
      
 29 
     | 
    
         
            +
                    result = best_match(full_path + ".*")
         
     | 
| 
      
 30 
     | 
    
         
            +
                    result.sub(/^#{root}/i, '').sub(/^\//, '') if result && File.file?(result)
         
     | 
| 
       31 
31 
     | 
    
         
             
                  end
         
     | 
| 
       32 
32 
     | 
    
         
             
                end
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
         @@ -38,6 +38,10 @@ module Serve 
     | 
|
| 
       38 
38 
     | 
    
         
             
                    path = path.sub(%r{/\Z}, '') # remove trailing slash
         
     | 
| 
       39 
39 
     | 
    
         
             
                    path unless path =~ /\.\./   # guard against evil paths
         
     | 
| 
       40 
40 
     | 
    
         
             
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  def self.best_match(path)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    (Dir.glob(path, File::FNM_CASEFOLD).first || path)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
       41 
45 
     | 
    
         | 
| 
       42 
46 
     | 
    
         
             
              end
         
     | 
| 
       43 
47 
     | 
    
         
             
            end
         
     | 
    
        data/lib/serve/view_helpers.rb
    CHANGED
    
    | 
         @@ -35,8 +35,10 @@ module Serve #:nodoc: 
     | 
|
| 
       35 
35 
     | 
    
         
             
              end
         
     | 
| 
       36 
36 
     | 
    
         | 
| 
       37 
37 
     | 
    
         
             
              module ContentHelpers
         
     | 
| 
       38 
     | 
    
         
            -
                def content_for(symbol, &block)
         
     | 
| 
       39 
     | 
    
         
            -
                   
     | 
| 
      
 38 
     | 
    
         
            +
                def content_for(symbol, content = nil, &block)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  content = capture(&block) if block_given?
         
     | 
| 
      
 40 
     | 
    
         
            +
                  set_content_for(symbol, content) if content
         
     | 
| 
      
 41 
     | 
    
         
            +
                  get_content_for(symbol) unless content
         
     | 
| 
       40 
42 
     | 
    
         
             
                end
         
     | 
| 
       41 
43 
     | 
    
         | 
| 
       42 
44 
     | 
    
         
             
                def content_for?(symbol)
         
     | 
| 
         @@ -135,14 +137,14 @@ module Serve #:nodoc: 
     | 
|
| 
       135 
137 
     | 
    
         
             
                end
         
     | 
| 
       136 
138 
     | 
    
         | 
| 
       137 
139 
     | 
    
         
             
                def render_template(template, options={})
         
     | 
| 
       138 
     | 
    
         
            -
                  path =  
     | 
| 
      
 140 
     | 
    
         
            +
                  path = parser.template_path
         
     | 
| 
       139 
141 
     | 
    
         
             
                  if template =~ %r{^/}
         
     | 
| 
       140 
142 
     | 
    
         
             
                    template = template[1..-1]
         
     | 
| 
       141 
143 
     | 
    
         
             
                    path = @root_path
         
     | 
| 
       142 
144 
     | 
    
         
             
                  end
         
     | 
| 
       143 
     | 
    
         
            -
                  filename = template_filename( 
     | 
| 
       144 
     | 
    
         
            -
                  if File.file?(filename)
         
     | 
| 
       145 
     | 
    
         
            -
                    parser. 
     | 
| 
      
 145 
     | 
    
         
            +
                  filename = template_filename(path, template, :partial => options[:partial])
         
     | 
| 
      
 146 
     | 
    
         
            +
                  if filename && File.file?(filename)
         
     | 
| 
      
 147 
     | 
    
         
            +
                    parser.parse(File.read(filename), File.extname(filename).split(".").last, options[:locals])
         
     | 
| 
       146 
148 
     | 
    
         
             
                  else
         
     | 
| 
       147 
149 
     | 
    
         
             
                    raise "File does not exist #{filename.inspect}"
         
     | 
| 
       148 
150 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -150,12 +152,12 @@ module Serve #:nodoc: 
     | 
|
| 
       150 
152 
     | 
    
         | 
| 
       151 
153 
     | 
    
         
             
                private
         
     | 
| 
       152 
154 
     | 
    
         | 
| 
       153 
     | 
    
         
            -
                  def template_filename( 
     | 
| 
       154 
     | 
    
         
            -
                     
     | 
| 
       155 
     | 
    
         
            -
                     
     | 
| 
       156 
     | 
    
         
            -
                     
     | 
| 
       157 
     | 
    
         
            -
                     
     | 
| 
       158 
     | 
    
         
            -
                    File.join(path,  
     | 
| 
      
 155 
     | 
    
         
            +
                  def template_filename(path, template, options)
         
     | 
| 
      
 156 
     | 
    
         
            +
                    template_path = File.dirname(template)
         
     | 
| 
      
 157 
     | 
    
         
            +
                    template_file = File.basename(template)
         
     | 
| 
      
 158 
     | 
    
         
            +
                    template_file = "_" + template_file if options[:partial]
         
     | 
| 
      
 159 
     | 
    
         
            +
                    route = Serve::Router.resolve(path, File.join(template_path, template_file))
         
     | 
| 
      
 160 
     | 
    
         
            +
                    (route && File.join(path, route))
         
     | 
| 
       159 
161 
     | 
    
         
             
                  end
         
     | 
| 
       160 
162 
     | 
    
         | 
| 
       161 
163 
     | 
    
         
             
                  def extname(filename)
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Test
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Test
         
     | 
| 
         @@ -0,0 +1,67 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.dirname(__FILE__) + '/spec_helper.rb'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'serve/pipeline'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            describe Serve::Pipeline do
         
     | 
| 
      
 5 
     | 
    
         
            +
              before :each do
         
     | 
| 
      
 6 
     | 
    
         
            +
                @root = File.expand_path("../fixtures", __FILE__)
         
     | 
| 
      
 7 
     | 
    
         
            +
              end
         
     | 
| 
      
 8 
     | 
    
         
            +
              
         
     | 
| 
      
 9 
     | 
    
         
            +
              describe "self.handles?" do
         
     | 
| 
      
 10 
     | 
    
         
            +
                it "should not handle .html" do
         
     | 
| 
      
 11 
     | 
    
         
            +
                  Serve::Pipeline.handles?("dir/file.html").should be_false
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                it "should handle .markdown" do
         
     | 
| 
      
 15 
     | 
    
         
            +
                  Serve::Pipeline.handles?("dir/file.markdown").should be_true
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                it "should handle .html.markdown" do
         
     | 
| 
      
 19 
     | 
    
         
            +
                  Serve::Pipeline.handles?("dir/file.html.markdown").should be_true
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                it "should handle .coffee" do
         
     | 
| 
      
 23 
     | 
    
         
            +
                  Serve::Pipeline.handles?("dir/file.coffee").should be_true
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                it "should handle .markdown.erb" do
         
     | 
| 
      
 27 
     | 
    
         
            +
                  Serve::Pipeline.handles?("dir/file.markdown.erb").should be_true
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              describe "initialize" do
         
     | 
| 
      
 32 
     | 
    
         
            +
                it "should build a pipeline for .markdown" do
         
     | 
| 
      
 33 
     | 
    
         
            +
                  pipeline = Serve::Pipeline.new(@root, "directory/markdown.markdown")
         
     | 
| 
      
 34 
     | 
    
         
            +
                  pipeline.template.handlers.collect{|h| h.extension}.should == %w(markdown)
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                it "should build a pipeline for .html.markdown" do
         
     | 
| 
      
 38 
     | 
    
         
            +
                  pipeline = Serve::Pipeline.new(@root, "directory/markdown.html.markdown")
         
     | 
| 
      
 39 
     | 
    
         
            +
                  pipeline.template.handlers.collect{|h| h.extension}.should == %w(markdown)
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                it "should build a pipeline for .coffee" do
         
     | 
| 
      
 43 
     | 
    
         
            +
                  pipeline = Serve::Pipeline.new(@root, "directory/coffee.coffee")
         
     | 
| 
      
 44 
     | 
    
         
            +
                  pipeline.template.handlers.collect{|h| h.extension}.should == %w(coffee)
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                it "should build a pipeline for .markdown.erb" do
         
     | 
| 
      
 48 
     | 
    
         
            +
                  pipeline = Serve::Pipeline.new(@root, "directory/markdown_erb.markdown.erb")
         
     | 
| 
      
 49 
     | 
    
         
            +
                  pipeline.template.handlers.collect{|h| h.extension}.should == %w(markdown erb)
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
              end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
              describe "layout search" do
         
     | 
| 
      
 54 
     | 
    
         
            +
                it "should return no layout for a file at the root" do
         
     | 
| 
      
 55 
     | 
    
         
            +
                  pipeline = Serve::Pipeline.new(@root, "file.erb")
         
     | 
| 
      
 56 
     | 
    
         
            +
                  pipeline.layout.should be_passthrough
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                it "should search up the directory tree for layouts" do
         
     | 
| 
      
 60 
     | 
    
         
            +
                  root = File.join(@root, "directory")
         
     | 
| 
      
 61 
     | 
    
         
            +
                  pipeline = Serve::Pipeline.new(root, "subdirectory/test.erb")
         
     | 
| 
      
 62 
     | 
    
         
            +
                  pipeline.layout.should_not be_passthrough
         
     | 
| 
      
 63 
     | 
    
         
            +
                  pipeline.layout.path.should == root
         
     | 
| 
      
 64 
     | 
    
         
            +
                  pipeline.layout.file.should == "_layout.erb"
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
            end
         
     |