Module: Tap::Support::Gems::Rack

Defined in:
lib/tap/support/gems/rack.rb

Overview

UNDER CONSTRUCTION

Support for a Tap::Server, built on Rack.

Tap::Support::Gems::Rack is intended to extend a Tap::Env

Defined Under Namespace

Modules: Render

Constant Summary collapse

DEFAULT_ERROR_TEMPLATE =

The default error template used by response when an error occurs.

%Q{
<html>
<body>
# Error handling request: <%= error.message %></br>
# <%= error.backtrace.join("<br/># ") %>

<code><pre>
<%= cgi.to_yaml %>
<%= rack.to_yaml %>
</pre></code>
</body>
</html>     
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#handlerObject

The handler for the server (ex Rack::Handler::WEBrick)



26
27
28
# File 'lib/tap/support/gems/rack.rb', line 26

def handler
  @handler
end

Instance Method Details

#call(rack_env) ⇒ Object

The Rack interface method. Call routesrequests (with preference) to:

  • static pages

  • cgi scripts

  • default responses

Static pages

Static pages may be served from any env in self. A static page is served if a file with the request path exists under the ‘public’ directory for any env.

Envs are searched in order, using the Env#search_path method.

CGI scripts

Like static pages, cgi scripts may be served from any env in self.

Scripts are discovered using a search of the cgi manifest. See cgi_response for more details.

Default responses

The default response is path-dependent:

path            action
/, /index       render the manifest.
all others      render a 404 response

The manifest may be refreshed by setting a query string:

/?refresh=true
/index?refresh=true


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
# File 'lib/tap/support/gems/rack.rb', line 89

def call(rack_env)
  path = rack_env['PATH_INFO']
  
  case 
  when static_path = search_path(:public, path) {|file| File.file?(file) }
    # serve named static pages
    file_response(static_path, rack_env)
    
  when cgi_path = cgis.search(path)
    # serve cgis
    cgi_response(cgi_path, rack_env)
    
  # when task_path = search(:tasks, path)
  #   # serve tasks
  #   cgi_response('task', rack_env)
    
  when path == "/" || path == "/index"
    # serve up the homepage
    if rack_env["QUERY_STRING"] == "refresh=true"
      # reset(:cgis) do |key, path|
      #   Support::Lazydoc[path].resolved = false
      # end
    end
    render_response('index.erb', rack_env)
    
  else
    # handle all other requests as errors
    render_response('404.erb', rack_env)
    
  end
end

#cgi_response(cgi_path, rack_env) ⇒ Object

Generates a [status, headers, body] response for the specified cgi. The cgi will be run with ENV set as specified in rack_env.



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/tap/support/gems/rack.rb', line 137

def cgi_response(cgi_path, rack_env)
  
  # setup standard ios for capture
  current_input = $stdin
  current_output = $stdout
  
  cgi_input = rack_env['rack.input']
  cgi_output = StringIO.new("")

  begin
    $stdin = cgi_input
    $stdout = cgi_output
    
    # run the cgi
    with_ENV(rack_env) { load(cgi_path) }

    # collect the headers and body from the output
    headers, body = cgi_output.string.split(/\r?\n\r?\n/, 2)

    raise "missing headers from: #{cgi_path}" if headers == nil
    body = "" if body == nil

    headers = headers.split(/\r?\n/).inject({}) do |hash, line|
      key, value = line.split(/:/, 2)
      hash[key] = value
      hash
    end

    # generate the response
    [headers.delete('Status') || 200, headers, body]
    
  rescue(Exception)
    # when an error occurs, return a standard cgi error with backtrace
    [500, {'Content-Type' => 'text/plain'}, %Q{#{$!.class}: #{$!.message}\n#{$!.backtrace.join("\n")}}]
    
  ensure
    $stdin = current_input
    $stdout = current_output
  end
end

#env_attrs(rack_env) ⇒ Object



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/tap/support/gems/rack.rb', line 236

def env_attrs(rack_env)
  # partition and sort the env variables into
  # cgi and rack variables.
  rack, cgi = rack_env.to_a.partition do |(key, value)|
    key =~ /^rack/
  end.collect do |part|
    part.sort_by do |key, value|
      key
    end.inject({}) do |hash, (key,value)|
      hash[key] = value
      hash
    end
  end
  
  {:env => self, :cgi => cgi, :rack => rack}
end

#file_response(path, rack_env) ⇒ Object

Generates a [status, headers, body] response for the specified file. Patterned after Rack::File#._call.



123
124
125
126
127
128
129
130
131
132
133
# File 'lib/tap/support/gems/rack.rb', line 123

def file_response(path, rack_env)
  response(rack_env) do |res|
    content = File.read(path)
    res.headers.merge!(
      "Last-Modified" => File.mtime(path).httpdate,
      "Content-Type" => ::Rack::File::MIME_TYPES[File.extname(path)] || "text/plain", # Rack::Mime.mime_type(File.extname(path), 'text/plain'), 
      "Content-Length" => content.size.to_s)
    
    content
  end
end

#render(path, attributes = {}) ⇒ Object

Builds the specified template using the rack_env and additional attributes. The rack_env is partitioned into rack-related and cgi-related hashes (all rack_env entries where the key starts with ‘rack’ are rack-related, the others are cgi-related).

The template is built with the following standard locals:

server   self
cgi      the cgi-related hash
rack     the rack-related hash

Plus the attributes.



229
230
231
232
233
234
# File 'lib/tap/support/gems/rack.rb', line 229

def render(path, attributes={}) # :nodoc:
  path = search_path(:template, path) {|file| File.file?(file) }
  raise("no such template: #{path}") if path == nil

  template(File.read(path) , attributes)
end

#render_response(path, rack_env) ⇒ Object

Generates a [status, headers, body] response using the first existing template matching path (as determined by Env#search_path) and the specified rack_env.



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/tap/support/gems/rack.rb', line 186

def render_response(path, rack_env)
  # partition and sort the env variables into
  # cgi and rack variables.
  rack, cgi = rack_env.to_a.partition do |(key, value)|
    key =~ /^rack/
  end.collect do |part|
    part.sort_by do |key, value|
      key
    end.inject({}) do |hash, (key,value)|
      hash[key] = value
      hash
    end
  end
  
  response(rack_env) do 
    render(path, env_attrs(rack_env))
  end
end

#response(rack_env) ⇒ Object

Creates a [status, headers, body] response using the result of the block as the body. The status and headers the defaults for Rack::Response. If an error occurs, a default error message is generated using the DEFAULT_ERROR_TEMPLATE.



49
50
51
52
53
54
55
56
57
# File 'lib/tap/support/gems/rack.rb', line 49

def response(rack_env)
  ::Rack::Response.new.finish do |res|
    res.write begin
      yield(res)
    rescue
      template(DEFAULT_ERROR_TEMPLATE, env_attrs(rack_env).merge(:error => $!))
    end
  end
end

#template(template, attributes = {}) ⇒ Object

:nodoc:



253
254
255
# File 'lib/tap/support/gems/rack.rb', line 253

def template(template, attributes={}) # :nodoc:
  Templater.new(template, attributes).extend(Render).build
end