Class: Tap::Server

Inherits:
Object
  • Object
show all
Includes:
Configurable, Rack::Utils
Defined in:
lib/tap/server.rb

Overview

:::- Server is a Rack application that dispatches calls to other Rack apps, most commonly a Tap::Controller.

Routes

Routing is fixed and very simple:

/:controller/path/to/resource

Server dispatches the request to the controller keyed by :controller after shifting the key from PATH_INFO to SCRIPT_NAME.

server = Server.new
server.controllers['sample'] = lambda do |env|
  [200, {}, ["Sample got #{env['SCRIPT_NAME']} : #{env['PATH_INFO']}"]]
end

req = Rack::MockRequest.new(server)
req.get('/sample/path/to/resource').body      # => "Sample got /sample : /path/to/resource"

Server automatically maps unknown keys to controllers discovered via the env.controllers manifest. The only requirement is that the controller constant is a Rack application. For instance:

# [lib/example.rb] => %q{
# ::controller
# class Example
#   def self.call(env)
#     [200, {}, ["Example got #{env['SCRIPT_NAME']} : #{env['PATH_INFO']}"]]
#   end
# end 
# }

req.get('/example/path/to/resource').body     # => "Example got /example : /path/to/resource"

If desired, controllers can be set with aliases to map a path key to a lookup key.

server.controllers['sample'] = 'example'
req.get('/sample/path/to/resource').body      # => "Example got /sample : /path/to/resource"

If no controller can be found, the request is routed using the default_controller_key and the request is NOT adjusted.

server.default_controller_key = 'app'
server.controllers['app'] = lambda do |env|
  [200, {}, ["App got #{env['SCRIPT_NAME']} : #{env['PATH_INFO']}"]]
end

req.get('/unknown/path/to/resource').body     # => "App got  : /unknown/path/to/resource"

In development mode, the controller constant is removed and the constant require path is reloaded each time it gets called. This system allows many web frameworks to be hooked into a Tap server.

:::+

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env = Env.new, app = Tap::App.instance, config = {}) ⇒ Server

Returns a new instance of Server.



135
136
137
138
139
140
141
# File 'lib/tap/server.rb', line 135

def initialize(env=Env.new, app=Tap::App.instance, config={})
  @env = env
  @app = app
  @cache = {}
  @handler = nil
  initialize_config(config)
end

Instance Attribute Details

#envObject (readonly)

Returns the value of attribute env.



132
133
134
# File 'lib/tap/server.rb', line 132

def env
  @env
end

#handlerObject (readonly)

Returns the value of attribute handler.



133
134
135
# File 'lib/tap/server.rb', line 133

def handler
  @handler
end

Class Method Details

.instantiate(root, shutdown_key = false) ⇒ Object

Instantiates a Server in the specified directory, configured as specified in root/server.yml. If shutdown_key is specified, a random shutdown key will be generated and set on the sever.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/tap/server.rb', line 80

def instantiate(root, shutdown_key=false)
  # setup the server directory
  root = File.expand_path(root)
  FileUtils.mkdir_p(root) unless File.exists?(root)

  # initialize the server
  app = Tap::App.instance
  env = Tap::Exe.instantiate(root)
  env.activate
  config = Configurable::Utils.load_file(env.root['server.yml'])
  
  server = new(env, app, config)
  server.config[:shutdown_key] = rand(1000000) if shutdown_key
  server
end

.run(server) ⇒ Object

Runs the server



97
98
99
100
# File 'lib/tap/server.rb', line 97

def run(server)
  cookie_server = Rack::Session::Pool.new(server)
  server.run!
end

Instance Method Details

#app(id = nil) ⇒ Object

Returns the session-specific App, or the server app if id is nil.



203
204
205
# File 'lib/tap/server.rb', line 203

def app(id=nil)
  @app
end

#call(rack_env) ⇒ Object

The Rack interface method.



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/tap/server.rb', line 225

def call(rack_env)
  if development?
    env.reset 
    @cache.clear
  end
  
  # route to a controller
  blank, key, path_info = rack_env['PATH_INFO'].split("/", 3)
  controller = lookup(unescape(key))
  
  if controller
    # adjust env if key routes to a controller
    rack_env['SCRIPT_NAME'] = ["#{rack_env['SCRIPT_NAME'].chomp('/')}/#{key}"]
    rack_env['PATH_INFO'] = ["/#{path_info}"]
  else
    # use default controller key
    controller = lookup(default_controller_key)
    
    unless controller
      raise ServerError.new("404 Error: could not route to controller", 404)
    end
  end
  
  # handle the request
  rack_env['tap.server'] = self
  controller.call(rack_env)
rescue ServerError
  $!.response
rescue Exception
  ServerError.response($!)
end

#initialize_sessionObject

Currently a stub for initializing a session. initialize_session returns an integer session id.



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/tap/server.rb', line 168

def initialize_session
  id = 0
  session_app = app(id)
  session_root = root(id)
  
  # setup expiration information...
  
  # setup a session log
  log_path = session_root.prepare(:log, 'session.log')
  session_app.logger = Logger.new(log_path)
  session_app.on_complete do |_result|
    # find the template
    class_name = _result.key.class.to_s.underscore
    pattern = "#{class_name}/result\.*"
    template = nil
    env.each do |e|
      templates = e.root.glob(views_dir, pattern)
      unless templates.empty?
        template = templates[0]
        break
      end
    end
    
    if template
      extname = File.extname(template)
      env.root.prepare(:results, id.to_s, "#{class_name}#{extname}") do |file|
        file << Support::Templater.new(File.read(template)).build(:_result => _result)
      end
    end
  end unless session_app.on_complete_block
  
  id
end

#root(id = nil) ⇒ Object

Returns the session-specific Root, or the server env.root if id is nil.



208
209
210
# File 'lib/tap/server.rb', line 208

def root(id=nil)
  @env.root
end

#run!(handler = rack_handler) ⇒ Object

Runs self as configured, on the specified server, host, and port. Use an INT signal to interrupt.



145
146
147
148
149
150
151
# File 'lib/tap/server.rb', line 145

def run!(handler=rack_handler)
  app.log :run, "#{host}:#{port} (#{handler})"
  handler.run self, :Host => host, :Port => port do |handler_instance|
    @handler = handler_instance
    trap(:INT) { stop! }
  end
end

#search(dir, path) ⇒ Object

Searches env for the first matching file, directories are not matched.



258
259
260
# File 'lib/tap/server.rb', line 258

def search(dir, path)
  env.search(dir, path) {|file| File.file?(file) }
end

#stop!Object

Stops the server if running (ie a handler is set). Returns true if the server was stopped, and false otherwise.



155
156
157
158
159
160
161
162
163
164
# File 'lib/tap/server.rb', line 155

def stop!
  if handler
    # Use thins' hard #stop! if available, otherwise just #stop
    handler.respond_to?(:stop!) ? handler.stop! : handler.stop
    @handler = nil
    false
  else
    true
  end
end

#uri(controller = nil, action = nil, params = {}) ⇒ Object

Returns a uri mapping to the specified controller and action. Parameters may be specified; they are built as a query and attached to the uri as normal.

Currenlty uri does not map the controller to a minipath, but in the future it will.



218
219
220
221
222
# File 'lib/tap/server.rb', line 218

def uri(controller=nil, action=nil, params={})
  query = build_query(params)
  uri = ["http://#{host}:#{port}", escape(controller), action].compact.join("/")
  query.empty? ? uri : "#{uri}?#{query}"
end