ry / ebb fork watch download tarball
public
Description: web server
Homepage: http://ebb.rubyforge.org
Clone URL: git://github.com/ry/ebb.git
Search Repo:
ryah (author)
Mon Apr 07 11:03:08 -0700 2008
commit  590255007398ef5bc9bbaa0e5697dde73239f245
tree    38a0424b633cbf69f8a5cf98d9afa0a62ebad534
parent  f16270d317fca555c68a294db72a0db1d4767dee
ebb / ruby_lib / rack / adapter / rails.rb
100644 159 lines (124 sloc) 5.267 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
require 'cgi'
require 'rubygems'
require 'rack'
 
# Adapter to run a Rails app with any supported Rack handler.
# By default it will try to load the Rails application in the
# current directory in the development environment.
# Options:
# root: Root directory of the Rails app
# env: Rails environment to run in (development, production or test)
# Based on http://fuzed.rubyforge.org/ Rails adapter
module Rack
  module Adapter
    class Rails
      def initialize(options={})
        @root = options[:root] || Dir.pwd
        @env = options[:environment] || 'development'
        @prefix = options[:prefix]
        
        load_application
        
        @file_server = Rack::File.new(::File.join(RAILS_ROOT, "public"))
      end
      
      def load_application
        ENV['RAILS_ENV'] = @env
 
        require "#{@root}/config/environment"
        require 'dispatcher'
        
        ActionController::AbstractRequest.relative_url_root = @prefix if @prefix
      end
      
      # TODO refactor this in File#can_serve?(path) ??
      def file_exist?(path)
        full_path = ::File.join(@file_server.root, Utils.unescape(path))
        ::File.file?(full_path) && ::File.readable?(full_path)
      end
      
      def serve_file(env)
        @file_server.call(env)
      end
      
      def serve_rails(env)
        request = Request.new(env)
        response = Response.new
        
        session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
        cgi = CGIWrapper.new(request, response)
    
        Dispatcher.dispatch(cgi, session_options, response)
 
        response.finish
      end
      
      def call(env)
        path = env['PATH_INFO'].chomp('/')
        cached_path = (path.empty? ? 'index' : path) + ActionController::Base.page_cache_extension
        
        if file_exist?(path) # Serve the file if it's there
          serve_file(env)
        elsif file_exist?(cached_path) # Serve the page cache if it's there
          env['PATH_INFO'] = cached_path
          serve_file(env)
        else # No static file, let Rails handle it
          serve_rails(env)
        end
      end
      
      # Never spawn threads for a request
      def spawn_thread?(env)
        false
      end
    
      protected
        
        class CGIWrapper < ::CGI
          def initialize(request, response, *args)
            @request = request
            @response = response
            @args = *args
            @input = request.body
 
            super *args
          end
        
          def header(options = "text/html")
            if options.is_a?(String)
              @response['Content-Type'] = options unless @response['Content-Type']
            else
              @response['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length']
            
              @response['Content-Type'] = options.delete('type') || "text/html"
              @response['Content-Type'] += "; charset=" + options.delete('charset') if options['charset']
                        
              @response['Content-Language'] = options.delete('language') if options['language']
              @response['Expires'] = options.delete('expires') if options['expires']
 
              @response.status = options.delete('Status') if options['Status']
              
              # Convert 'cookie' header to 'Set-Cookie' headers.
              # Because Set-Cookie header can appear more the once in the response body,
              # we store it in a line break seperated string that will be translated to
              # multiple Set-Cookie header by the handler.
              if cookie = options.delete('cookie')
                cookies = []
                
                case cookie
                  when Array then cookie.each { |c| cookies << c.to_s }
                  when Hash then cookie.each { |_, c| cookies << c.to_s }
                  else cookies << cookie.to_s
                end
        
                @output_cookies.each { |c| cookies << c.to_s } if @output_cookies
                
                @response['Set-Cookie'] = [@response['Set-Cookie'], cookies].compact.join("\n")
              end
              
              options.each { |k,v| @response[k] = v }
            end
            
            ""
          end
                        
          def params
            @params ||= @request.params
          end
        
          def cookies
            @request.cookies
          end
        
          def query_string
            @request.query_string
          end
          
          # Used to wrap the normal args variable used inside CGI.
          def args
            @args
          end
    
          # Used to wrap the normal env_table variable used inside CGI.
          def env_table
            @request.env
          end
    
          # Used to wrap the normal stdinput variable used inside CGI.
          def stdinput
            @input
          end
        
          def stdoutput
            STDERR.puts "stdoutput should not be used."
            @response.body
          end
      end
    end
  end
end