tap-server 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|