tap-server 0.1.0
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/History +5 -0
- data/MIT-LICENSE +19 -0
- data/README +49 -0
- data/cmd/server.rb +34 -0
- data/controllers/app_controller.rb +92 -0
- data/controllers/schema_controller.rb +255 -0
- data/lib/tap/controller.rb +231 -0
- data/lib/tap/server.rb +219 -0
- data/lib/tap/server_error.rb +34 -0
- data/lib/tap/tasks/echo.rb +14 -0
- data/lib/tap/tasks/server.rb +30 -0
- data/public/javascripts/prototype.js +4221 -0
- data/public/javascripts/tap.js +112 -0
- data/public/stylesheets/tap.css +6 -0
- data/tap.yml +0 -0
- data/views/404.erb +9 -0
- data/views/500.erb +7 -0
- data/views/app_controller/index.erb +44 -0
- data/views/app_controller/info.erb +8 -0
- data/views/app_controller/tail.erb +8 -0
- data/views/layout.erb +11 -0
- data/views/schema_controller/config/default.erb +4 -0
- data/views/schema_controller/config/flag.erb +3 -0
- data/views/schema_controller/config/switch.erb +6 -0
- data/views/schema_controller/configurations.erb +27 -0
- data/views/schema_controller/join.erb +4 -0
- data/views/schema_controller/node.erb +47 -0
- data/views/schema_controller/preview.erb +3 -0
- data/views/schema_controller/round.erb +30 -0
- data/views/schema_controller/schema.erb +28 -0
- data/views/tap/tasks/echo/result.html +1 -0
- metadata +109 -0
data/lib/tap/server.rb
ADDED
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'rack/mock'
|
3
|
+
|
4
|
+
require 'tap'
|
5
|
+
require 'tap/server_error'
|
6
|
+
|
7
|
+
module Tap
|
8
|
+
Env.manifest(:controllers) do |env|
|
9
|
+
entries = env.root.glob(:controllers, "*_controller.rb").collect do |path|
|
10
|
+
const_name = File.basename(path).chomp('.rb').camelize
|
11
|
+
Support::Constant.new(const_name, path)
|
12
|
+
end
|
13
|
+
|
14
|
+
Support::Manifest.intern(entries) do |manifest, const|
|
15
|
+
const.basename.chomp('_controller')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Server is a Rack application that dispatches calls to other Rack apps, most
|
20
|
+
# commonly a Tap::Controller.
|
21
|
+
#
|
22
|
+
# == Routes
|
23
|
+
#
|
24
|
+
# Routing is fixed and very simple:
|
25
|
+
#
|
26
|
+
# /:controller/path/to/resource
|
27
|
+
#
|
28
|
+
# Server dispatches the request to the controller keyed by :controller after
|
29
|
+
# shifting the key from PATH_INFO to SCRIPT_NAME.
|
30
|
+
#
|
31
|
+
# server = Server.new
|
32
|
+
# server.controllers['sample'] = lambda do |env|
|
33
|
+
# [200, {}, ["Sample got #{env['SCRIPT_NAME']} : #{env['PATH_INFO']}"]]
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# req = Rack::MockRequest.new(server)
|
37
|
+
# req.get('/sample/path/to/resource').body # => "Sample got /sample : /path/to/resource"
|
38
|
+
#
|
39
|
+
# Server automatically maps unknown keys to a controller by searching
|
40
|
+
# env.controllers. As a result '/example' maps to the ExampleController
|
41
|
+
# defined in 'controllers/example_controller.rb'.
|
42
|
+
#
|
43
|
+
# # [controllers/example_controller.rb] => %q{
|
44
|
+
# # class ExampleController
|
45
|
+
# # def self.call(env)
|
46
|
+
# # [200, {}, ["ExampleController got #{env['SCRIPT_NAME']} : #{env['PATH_INFO']}"]]
|
47
|
+
# # end
|
48
|
+
# # end
|
49
|
+
# # }
|
50
|
+
#
|
51
|
+
# req.get('/example/path/to/resource').body # => "ExampleController got /example : /path/to/resource"
|
52
|
+
#
|
53
|
+
# If desired, controllers can be set with aliases to map a path key to a
|
54
|
+
# lookup key.
|
55
|
+
#
|
56
|
+
# server.controllers['sample'] = 'example'
|
57
|
+
# req.get('/sample/path/to/resource').body # => "ExampleController got /sample : /path/to/resource"
|
58
|
+
#
|
59
|
+
# If no controller can be found, the request is routed using the
|
60
|
+
# default_controller_key and the request is NOT adjusted.
|
61
|
+
#
|
62
|
+
# server.default_controller_key = 'app'
|
63
|
+
# server.controllers['app'] = lambda do |env|
|
64
|
+
# [200, {}, ["App got #{env['SCRIPT_NAME']} : #{env['PATH_INFO']}"]]
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# req.get('/unknown/path/to/resource').body # => "App got : /unknown/path/to/resource"
|
68
|
+
#
|
69
|
+
class Server
|
70
|
+
include Rack::Utils
|
71
|
+
include Configurable
|
72
|
+
|
73
|
+
config :environment, :development
|
74
|
+
config :server, %w[thin mongrel webrick]
|
75
|
+
config :host, 'localhost'
|
76
|
+
config :port, 8080, &c.integer
|
77
|
+
|
78
|
+
config :views_dir, :views
|
79
|
+
config :public_dir, :public
|
80
|
+
config :controllers, {}
|
81
|
+
config :default_controller_key, 'app'
|
82
|
+
|
83
|
+
attr_reader :env
|
84
|
+
|
85
|
+
def initialize(env=Env.new, app=Tap::App.instance, config={})
|
86
|
+
@env = env
|
87
|
+
@app = app
|
88
|
+
@cache = {}
|
89
|
+
initialize_config(config)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Currently a stub for initializing a session. initialize_session returns
|
93
|
+
# an integer session id.
|
94
|
+
def initialize_session
|
95
|
+
id = 0
|
96
|
+
session_app = app(id)
|
97
|
+
log_path = session_app.prepare(:log, 'server.log')
|
98
|
+
session_app.logger = Logger.new(log_path)
|
99
|
+
|
100
|
+
session_app.on_complete do |_result|
|
101
|
+
# find the template
|
102
|
+
class_name = _result.key.class.to_s.underscore
|
103
|
+
pattern = "#{class_name}/result\.*"
|
104
|
+
template = nil
|
105
|
+
env.each do |e|
|
106
|
+
templates = e.root.glob(views_dir, pattern)
|
107
|
+
unless templates.empty?
|
108
|
+
template = templates[0]
|
109
|
+
break
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
if template
|
114
|
+
extname = File.extname(template)
|
115
|
+
session_app.prepare(:results, id.to_s, "#{class_name}#{extname}") do |file|
|
116
|
+
file << Support::Templater.new(File.read(template)).build(:_result => _result)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
id
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns the app provided during initialization. In the future this
|
125
|
+
# method may be extended to provide a session-specific App, hence it
|
126
|
+
# has been stubbed with an id input.
|
127
|
+
def app(id=nil)
|
128
|
+
@app
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns true if environment is :development.
|
132
|
+
def development?
|
133
|
+
environment == :development
|
134
|
+
end
|
135
|
+
|
136
|
+
# The {Rack}[http://rack.rubyforge.org/doc/] interface method.
|
137
|
+
def call(rack_env)
|
138
|
+
if development?
|
139
|
+
env.reset
|
140
|
+
@cache.clear
|
141
|
+
end
|
142
|
+
|
143
|
+
# route to a controller
|
144
|
+
blank, key, path_info = rack_env['PATH_INFO'].split("/", 3)
|
145
|
+
controller = lookup(unescape(key))
|
146
|
+
|
147
|
+
if controller
|
148
|
+
# adjust env if key routes to a controller
|
149
|
+
rack_env['SCRIPT_NAME'] = ["#{rack_env['SCRIPT_NAME'].chomp('/')}/#{key}"]
|
150
|
+
rack_env['PATH_INFO'] = ["/#{path_info}"]
|
151
|
+
else
|
152
|
+
# use default controller key
|
153
|
+
controller = lookup(default_controller_key)
|
154
|
+
|
155
|
+
unless controller
|
156
|
+
raise ServerError.new("404 Error: could not route to controller", 404)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# handle the request
|
161
|
+
rack_env['tap.server'] = self
|
162
|
+
controller.call(rack_env)
|
163
|
+
rescue ServerError
|
164
|
+
$!.response
|
165
|
+
rescue Exception
|
166
|
+
ServerError.response($!)
|
167
|
+
end
|
168
|
+
|
169
|
+
#--
|
170
|
+
# TODO: implement caching for path content
|
171
|
+
def content(path)
|
172
|
+
File.read(path)
|
173
|
+
end
|
174
|
+
|
175
|
+
#--
|
176
|
+
# TODO: implement caching for public_paths
|
177
|
+
def public_path(path)
|
178
|
+
env.search(public_dir, path) {|public_path| File.file?(public_path) }
|
179
|
+
end
|
180
|
+
|
181
|
+
#--
|
182
|
+
# TODO: implement caching for template_paths
|
183
|
+
def template_path(path)
|
184
|
+
env.search(views_dir, path) {|template_path| File.file?(template_path) }
|
185
|
+
end
|
186
|
+
|
187
|
+
protected
|
188
|
+
|
189
|
+
# a helper method for routing a key to a controller
|
190
|
+
def lookup(key) # :nodoc:
|
191
|
+
return @cache[key] if @cache.has_key?(key)
|
192
|
+
minikey = controllers[key] || key
|
193
|
+
|
194
|
+
# return registered controllers
|
195
|
+
if minikey.respond_to?(:call)
|
196
|
+
@cache[key] = minikey
|
197
|
+
return minikey
|
198
|
+
end
|
199
|
+
|
200
|
+
# return if no controller can be found
|
201
|
+
unless const = env.controllers.search(minikey)
|
202
|
+
@cache[key] = nil
|
203
|
+
return nil
|
204
|
+
end
|
205
|
+
|
206
|
+
# load the require_path in dev mode so that
|
207
|
+
# controllers will be reloaded each time
|
208
|
+
if development? && const.require_path
|
209
|
+
if Object.const_defined?(const.const_name)
|
210
|
+
Object.send(:remove_const, const.const_name)
|
211
|
+
end
|
212
|
+
|
213
|
+
load const.require_path
|
214
|
+
end
|
215
|
+
|
216
|
+
@cache[key] = const.constantize
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Tap
|
2
|
+
|
3
|
+
# A special type of error used for specifiying controller errors.
|
4
|
+
class ServerError < RuntimeError
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# A helper to format a non-ServerError into a ServerError response.
|
8
|
+
def response(err)
|
9
|
+
new("500 #{err.class}: #{err.message}\n#{err.backtrace.join("\n")}").response
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# The error status
|
14
|
+
attr_reader :status
|
15
|
+
|
16
|
+
# Headers for the error response
|
17
|
+
attr_reader :headers
|
18
|
+
|
19
|
+
# The error response body
|
20
|
+
attr_reader :body
|
21
|
+
|
22
|
+
def initialize(body="500 Server Error", status=500, headers={'Content-Type' => ['text/plain']})
|
23
|
+
@body = body
|
24
|
+
@status = status
|
25
|
+
@headers = headers
|
26
|
+
super(body)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Formats self as a rack response array (ie [status, headers, body]).
|
30
|
+
def response
|
31
|
+
[status, headers, [body]]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'tap/server'
|
2
|
+
|
3
|
+
module Tap
|
4
|
+
module Tasks
|
5
|
+
|
6
|
+
# ::manifest
|
7
|
+
class Server < Tap::Task
|
8
|
+
|
9
|
+
nest(:env, Tap::Env) do |config|
|
10
|
+
case config
|
11
|
+
when Tap::Env then config
|
12
|
+
else Tap::Env.new(config)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
nest_attr(:server, Tap::Server) do |config|
|
17
|
+
@server = Tap::Server.new(env, config)
|
18
|
+
end
|
19
|
+
|
20
|
+
def process(method='get', uri="/")
|
21
|
+
uri = URI(uri[0] == ?/ ? uri : "/#{uri}")
|
22
|
+
uri.host ||= server.host
|
23
|
+
uri.port ||= server.port
|
24
|
+
|
25
|
+
mock = Rack::MockRequest.new(server)
|
26
|
+
mock.request(method, uri.to_s)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|