tap-server 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +11 -0
- data/cmd/server.rb +4 -12
- data/lib/tap/controller.rb +84 -37
- data/lib/tap/controllers/app.rb +2 -2
- data/lib/tap/controllers/server.rb +58 -0
- data/lib/tap/server.rb +131 -38
- data/lib/tap/support/persistence.rb +71 -0
- data/views/tap/controllers/app/index.erb +1 -1
- data/views/tap/controllers/server/index.erb +3 -0
- metadata +5 -2
data/History
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
== 0.3.0 / 2009-03-17
|
2
|
+
|
3
|
+
Significant updates.
|
4
|
+
|
5
|
+
* added optional REST routes to Tap::Controller
|
6
|
+
* added persistence wrapper for session root
|
7
|
+
* significant cleanup
|
8
|
+
* removed middleware support
|
9
|
+
* added bindings for running server from
|
10
|
+
Firefox/Ubiquity
|
11
|
+
|
1
12
|
== 0.2.0 / 2009-03-07
|
2
13
|
|
3
14
|
* Reworked server routing to route by ::controller
|
data/cmd/server.rb
CHANGED
@@ -7,13 +7,7 @@ require 'tap/server'
|
|
7
7
|
|
8
8
|
env = Tap::Env.instance
|
9
9
|
app = Tap::App.instance
|
10
|
-
|
11
|
-
#
|
12
|
-
# handle options
|
13
|
-
#
|
14
|
-
|
15
|
-
config_path = nil
|
16
|
-
opts = ConfigParser.new do |opts|
|
10
|
+
parser = ConfigParser.new do |opts|
|
17
11
|
|
18
12
|
opts.separator ""
|
19
13
|
opts.separator "options:"
|
@@ -26,9 +20,7 @@ opts = ConfigParser.new do |opts|
|
|
26
20
|
exit
|
27
21
|
end
|
28
22
|
end
|
23
|
+
argv = parser.parse(ARGV)
|
29
24
|
|
30
|
-
#
|
31
|
-
|
32
|
-
server = Tap::Server.new(env, app, opts.config)
|
33
|
-
cookie_server = Rack::Session::Pool.new(server)
|
34
|
-
Rack::Handler::WEBrick.run(cookie_server, :Port => server.port)
|
25
|
+
# launch server
|
26
|
+
Tap::Server.new(env, app, parser.config).run!
|
data/lib/tap/controller.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'tap/server'
|
2
|
+
require 'tap/support/persistence'
|
2
3
|
autoload(:ERB, 'erb')
|
3
4
|
|
4
5
|
module Tap
|
@@ -16,13 +17,62 @@ module Tap
|
|
16
17
|
# * define it private or protected then call public(:method)
|
17
18
|
#
|
18
19
|
class Controller
|
20
|
+
|
21
|
+
# Adds REST routing (a-la Rails[http://www.b-simple.de/download/restful_rails_en.pdf])
|
22
|
+
# to a Tap::Controller.
|
23
|
+
#
|
24
|
+
# class Projects < Tap::Controller
|
25
|
+
# include RestRoutes
|
26
|
+
#
|
27
|
+
# # GET /projects
|
28
|
+
# def index...
|
29
|
+
#
|
30
|
+
# # GET /projects/*args
|
31
|
+
# def show(*args)...
|
32
|
+
#
|
33
|
+
# # GET /projects/arg;edit/*args
|
34
|
+
# def edit(arg, *args)...
|
35
|
+
#
|
36
|
+
# # POST /projects/*args
|
37
|
+
# def create(*args)...
|
38
|
+
#
|
39
|
+
# # PUT /projects/*args
|
40
|
+
# def update(*args)...
|
41
|
+
#
|
42
|
+
# # DELETE /projects/*args
|
43
|
+
# def destroy(*args)...
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
module RestRoutes
|
47
|
+
def route
|
48
|
+
blank, *args = request.path_info.split("/").collect {|arg| unescape(arg) }
|
49
|
+
action = case request.request_method
|
50
|
+
when /GET/i
|
51
|
+
case
|
52
|
+
when args.empty?
|
53
|
+
:index
|
54
|
+
when args[0] =~ /(.*);edit$/
|
55
|
+
args[0] = $1
|
56
|
+
:edit
|
57
|
+
else
|
58
|
+
:show
|
59
|
+
end
|
60
|
+
when /POST/i then :create
|
61
|
+
when /PUT/i then :update
|
62
|
+
when /DELETE/i then :destroy
|
63
|
+
else raise ServerError.new("unknown request method: #{request.request_method}")
|
64
|
+
end
|
65
|
+
|
66
|
+
[action, args]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
19
70
|
class << self
|
20
71
|
|
21
72
|
# Initialize instance variables on the child and inherit as necessary.
|
22
73
|
def inherited(child) # :nodoc:
|
23
74
|
super
|
24
75
|
child.set(:actions, actions.dup)
|
25
|
-
child.set(:middleware, middleware.dup)
|
26
76
|
child.set(:default_layout, default_layout)
|
27
77
|
child.set(:define_action, true)
|
28
78
|
end
|
@@ -31,10 +81,6 @@ module Tap
|
|
31
81
|
# stored as symbols. Actions are inherited.
|
32
82
|
attr_reader :actions
|
33
83
|
|
34
|
-
# An array of Rack middleware that will be applied when handing requests
|
35
|
-
# through the class call method. Middleware is inherited.
|
36
|
-
attr_reader :middleware
|
37
|
-
|
38
84
|
# The default layout rendered when the render option :layout is true.
|
39
85
|
attr_reader :default_layout
|
40
86
|
|
@@ -44,30 +90,9 @@ module Tap
|
|
44
90
|
@name ||= to_s.underscore
|
45
91
|
end
|
46
92
|
|
47
|
-
#
|
48
|
-
# with the specified args and block, and applied to in the order in
|
49
|
-
# which they are declared (ie first use processes requests first).
|
50
|
-
#
|
51
|
-
# Middleware is applied through the class call method, and on a per-call
|
52
|
-
# basis... middleware like Rack::Session::Pool that is supposed to
|
53
|
-
# persist for the life of an application will not work properly.
|
54
|
-
#
|
55
|
-
# Middleware is inherited.
|
56
|
-
def use(middleware, *args, &block)
|
57
|
-
@middleware << [middleware, args, block]
|
58
|
-
end
|
59
|
-
|
60
|
-
# Instantiates self and performs call. Middleware is applied in the
|
61
|
-
# order in which it was declared.
|
62
|
-
#--
|
63
|
-
# Note that middleware needs to be initialized in reverese, so that
|
64
|
-
# the first declared middleware runs first.
|
93
|
+
# Instantiates self and performs call.
|
65
94
|
def call(env)
|
66
|
-
|
67
|
-
middleware.reverse_each do |(m, args, block)|
|
68
|
-
app = m.new(app, *args, &block)
|
69
|
-
end
|
70
|
-
app.call(env)
|
95
|
+
new.call(env)
|
71
96
|
end
|
72
97
|
|
73
98
|
# Sets an instance variable for self, short for:
|
@@ -113,8 +138,10 @@ module Tap
|
|
113
138
|
end
|
114
139
|
|
115
140
|
set :actions, []
|
116
|
-
set :middleware, []
|
117
141
|
set :default_layout, nil
|
142
|
+
|
143
|
+
# Ensures methods (even public methods) on Controller will
|
144
|
+
# not be actions in subclasses.
|
118
145
|
set :define_action, false
|
119
146
|
|
120
147
|
include Rack::Utils
|
@@ -130,12 +157,16 @@ module Tap
|
|
130
157
|
# the action result and response is ignored.
|
131
158
|
attr_accessor :response
|
132
159
|
|
160
|
+
# The action currently being called by self.
|
161
|
+
attr_accessor :action
|
162
|
+
|
133
163
|
# Initializes a new instance of self. The input attributes are reset by
|
134
164
|
# call and are only provided for convenience during testing.
|
135
165
|
def initialize(server=nil, request=nil, response=nil)
|
136
166
|
@server = server
|
137
167
|
@request = request
|
138
168
|
@response = response
|
169
|
+
@action = nil
|
139
170
|
end
|
140
171
|
|
141
172
|
def call(env)
|
@@ -144,14 +175,12 @@ module Tap
|
|
144
175
|
@response = Rack::Response.new
|
145
176
|
|
146
177
|
# route to an action
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
unless self.class.actions.include?(action.to_sym)
|
178
|
+
@action, args = route
|
179
|
+
unless self.class.actions.include?(@action)
|
151
180
|
raise ServerError.new("404 Error: page not found", 404)
|
152
181
|
end
|
153
182
|
|
154
|
-
result = send(action, *args)
|
183
|
+
result = send(@action, *args)
|
155
184
|
if result.kind_of?(String)
|
156
185
|
response.write result
|
157
186
|
response.finish
|
@@ -160,15 +189,23 @@ module Tap
|
|
160
189
|
end
|
161
190
|
end
|
162
191
|
|
192
|
+
def route
|
193
|
+
blank, action, *args = request.path_info.split("/").collect {|arg| unescape(arg) }
|
194
|
+
action = "index" if action == nil || action.empty?
|
195
|
+
action = action.chomp(File.extname(action)).to_sym
|
196
|
+
|
197
|
+
[action, args]
|
198
|
+
end
|
199
|
+
|
163
200
|
def render(path, options={})
|
164
201
|
options, path = path, nil if path.kind_of?(Hash)
|
165
202
|
|
166
203
|
# lookup template
|
167
204
|
template_path = case
|
168
205
|
when options.has_key?(:template)
|
169
|
-
server.
|
206
|
+
server.search(:views, options[:template])
|
170
207
|
else
|
171
|
-
server.
|
208
|
+
server.search(:views, "#{self.class.name}/#{path}")
|
172
209
|
end
|
173
210
|
|
174
211
|
unless template_path
|
@@ -176,7 +213,7 @@ module Tap
|
|
176
213
|
end
|
177
214
|
|
178
215
|
# render template
|
179
|
-
template =
|
216
|
+
template = File.read(template_path)
|
180
217
|
content = render_erb(template, options)
|
181
218
|
|
182
219
|
# render layout
|
@@ -228,6 +265,16 @@ module Tap
|
|
228
265
|
server.root(session[:id] ||= server.initialize_session)
|
229
266
|
end
|
230
267
|
|
268
|
+
# Returns the file-based controller persistence.
|
269
|
+
def persistence
|
270
|
+
@persistence ||= Support::Persistence.new(root)
|
271
|
+
end
|
272
|
+
|
273
|
+
# Returns a controller uri.
|
274
|
+
def uri(action=nil, params={})
|
275
|
+
server.uri(self.class.name, action, params)
|
276
|
+
end
|
277
|
+
|
231
278
|
# Generates an empty binding to self without any locals assigned.
|
232
279
|
def empty_binding # :nodoc:
|
233
280
|
binding
|
data/lib/tap/controllers/app.rb
CHANGED
@@ -13,7 +13,7 @@ module Tap
|
|
13
13
|
# serve public files before actions
|
14
14
|
server = env['tap.server'] ||= Tap::Server.new
|
15
15
|
|
16
|
-
if path = server.
|
16
|
+
if path = server.search(:public, env['PATH_INFO'])
|
17
17
|
content = File.read(path)
|
18
18
|
headers = {
|
19
19
|
"Last-Modified" => [File.mtime(path).httpdate],
|
@@ -35,7 +35,7 @@ module Tap
|
|
35
35
|
|
36
36
|
render('index.erb', :locals => {:env => server.env, :env_names => env_names}, :layout => true)
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def info
|
40
40
|
if request.post?
|
41
41
|
app.info
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'tap/controller'
|
2
|
+
|
3
|
+
module Tap
|
4
|
+
module Controllers
|
5
|
+
|
6
|
+
# :startdoc::controller remotely controls and monitors server
|
7
|
+
#
|
8
|
+
# Server provides several uris to control and monitor the server behavior.
|
9
|
+
# Importantly, server allows the remote shutdown of a Tap::Server if a
|
10
|
+
# shutdown_key is set. This makes it possible to run servers in the
|
11
|
+
# background and still have a shutdown handle on them.
|
12
|
+
#
|
13
|
+
class Server < Tap::Controller
|
14
|
+
set :default_layout, 'layout.erb'
|
15
|
+
|
16
|
+
def index
|
17
|
+
render 'index.erb'
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns 'pong'.
|
21
|
+
def ping
|
22
|
+
response['Content-Type'] = 'text/plain'
|
23
|
+
"pong"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the public server configurations as xml.
|
27
|
+
def config
|
28
|
+
response['Content-Type'] = 'text/xml'
|
29
|
+
%Q{<?xml version="1.0"?>
|
30
|
+
<server>
|
31
|
+
<uri>#{uri}</uri>
|
32
|
+
<shutdown_key>#{shutdown_key}</shutdown_key>
|
33
|
+
</server>}
|
34
|
+
end
|
35
|
+
|
36
|
+
# Shuts down the server. Shutdown requires a shutdown key which
|
37
|
+
# is setup when the server is launched. If no shutdown key was
|
38
|
+
# setup, shutdown does nothing and responds accordingly.
|
39
|
+
def shutdown
|
40
|
+
if shutdown_key && request['shutdown_key'].to_i == shutdown_key
|
41
|
+
# wait a second to shutdown, so the response is sent out.
|
42
|
+
Thread.new {sleep 1; server.stop! }
|
43
|
+
"shutdown"
|
44
|
+
else
|
45
|
+
"you do not have permission to shutdown this server"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
# returns the server shutdown key. the shutdown key is required
|
52
|
+
# for shutdown to function, a nil shutdown key disables shutdown.
|
53
|
+
def shutdown_key # :nodoc:
|
54
|
+
server.shutdown_key
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/tap/server.rb
CHANGED
@@ -34,9 +34,9 @@ module Tap
|
|
34
34
|
# req = Rack::MockRequest.new(server)
|
35
35
|
# req.get('/sample/path/to/resource').body # => "Sample got /sample : /path/to/resource"
|
36
36
|
#
|
37
|
-
# Server automatically maps unknown keys to
|
38
|
-
# env.controllers.
|
39
|
-
#
|
37
|
+
# Server automatically maps unknown keys to controllers discovered via the
|
38
|
+
# env.controllers manifest. The only requirement is that the controller
|
39
|
+
# constant is a Rack application. For instance:
|
40
40
|
#
|
41
41
|
# # [lib/example.rb] => %q{
|
42
42
|
# # ::controller
|
@@ -65,38 +65,116 @@ module Tap
|
|
65
65
|
#
|
66
66
|
# req.get('/unknown/path/to/resource').body # => "App got : /unknown/path/to/resource"
|
67
67
|
#
|
68
|
+
# In development mode, the controller constant is removed and the constant
|
69
|
+
# require path is reloaded each time it gets called. This system allows many
|
70
|
+
# web frameworks to be hooked into a Tap server.
|
71
|
+
#
|
68
72
|
# :::+
|
69
73
|
class Server
|
74
|
+
class << self
|
75
|
+
|
76
|
+
# Instantiates a Server in the specified directory, configured as
|
77
|
+
# specified in root/server.yml. If shutdown_key is specified, a
|
78
|
+
# random shutdown key will be generated and set on the sever.
|
79
|
+
#
|
80
|
+
def instantiate(root, shutdown_key=false)
|
81
|
+
# setup the server directory
|
82
|
+
root = File.expand_path(root)
|
83
|
+
FileUtils.mkdir_p(root) unless File.exists?(root)
|
84
|
+
|
85
|
+
# initialize the server
|
86
|
+
app = Tap::App.instance
|
87
|
+
env = Tap::Exe.instantiate(root)
|
88
|
+
env.activate
|
89
|
+
config = Configurable::Utils.load_file(env.root['server.yml'])
|
90
|
+
|
91
|
+
server = new(env, app, config)
|
92
|
+
server.config[:shutdown_key] = rand(1000000) if shutdown_key
|
93
|
+
server
|
94
|
+
end
|
95
|
+
|
96
|
+
# Runs the server
|
97
|
+
def run(server)
|
98
|
+
cookie_server = Rack::Session::Pool.new(server)
|
99
|
+
server.run!
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
70
103
|
include Rack::Utils
|
71
104
|
include Configurable
|
72
105
|
|
73
|
-
config :environment, :development
|
74
|
-
config :
|
75
|
-
config :host, 'localhost'
|
76
|
-
config :port, 8080, &c.integer
|
106
|
+
config :environment, :development
|
107
|
+
config :servers, %w[thin mongrel webrick], &c.list # a list of preferred handlers
|
108
|
+
config :host, 'localhost', &c.string # the server host
|
109
|
+
config :port, 8080, &c.integer # the server port
|
77
110
|
|
78
|
-
|
79
|
-
|
111
|
+
# A hash of (key, controller) pairs mapping the controller part of a route
|
112
|
+
# to a Rack application. Typically controllers is used to specify aliases
|
113
|
+
# when the defaults are not preferable.
|
80
114
|
config :controllers, {}
|
115
|
+
|
116
|
+
# config :infer_controllers, true, &c.switch
|
117
|
+
|
118
|
+
# The default controller key used in routes that cannot be directly mapped
|
119
|
+
# to a controller
|
120
|
+
#--
|
121
|
+
# Set to nil to force controller mapping?
|
81
122
|
config :default_controller_key, 'app'
|
82
123
|
|
124
|
+
# Server implements a shutdown key so the server can be shutdown remotely
|
125
|
+
# via an HTTP request to the app/shutdown method. Remote shutdown is
|
126
|
+
# useful when the user is running a local server (especially from a
|
127
|
+
# background process). Under many circumstances remote shutdown is
|
128
|
+
# undesirable; specify a nil shutdown key, the default, to turn off
|
129
|
+
# shutdown.
|
130
|
+
config :shutdown_key, nil, &c.integer_or_nil # specifies a public shutdown key
|
131
|
+
|
83
132
|
attr_reader :env
|
133
|
+
attr_reader :handler
|
84
134
|
|
85
135
|
def initialize(env=Env.new, app=Tap::App.instance, config={})
|
86
136
|
@env = env
|
87
137
|
@app = app
|
88
138
|
@cache = {}
|
139
|
+
@handler = nil
|
89
140
|
initialize_config(config)
|
90
141
|
end
|
91
142
|
|
143
|
+
# Runs self as configured, on the specified server, host, and port. Use an
|
144
|
+
# INT signal to interrupt.
|
145
|
+
def run!(handler=rack_handler)
|
146
|
+
app.log :run, "#{host}:#{port} (#{handler})"
|
147
|
+
handler.run self, :Host => host, :Port => port do |handler_instance|
|
148
|
+
@handler = handler_instance
|
149
|
+
trap(:INT) { stop! }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Stops the server if running (ie a handler is set). Returns true if the
|
154
|
+
# server was stopped, and false otherwise.
|
155
|
+
def stop!
|
156
|
+
if handler
|
157
|
+
# Use thins' hard #stop! if available, otherwise just #stop
|
158
|
+
handler.respond_to?(:stop!) ? handler.stop! : handler.stop
|
159
|
+
@handler = nil
|
160
|
+
false
|
161
|
+
else
|
162
|
+
true
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
92
166
|
# Currently a stub for initializing a session. initialize_session returns
|
93
167
|
# an integer session id.
|
94
168
|
def initialize_session
|
95
169
|
id = 0
|
96
170
|
session_app = app(id)
|
97
|
-
|
98
|
-
session_app.logger = Logger.new(log_path)
|
171
|
+
session_root = root(id)
|
99
172
|
|
173
|
+
# setup expiration information...
|
174
|
+
|
175
|
+
# setup a session log
|
176
|
+
log_path = session_root.prepare(:log, 'session.log')
|
177
|
+
session_app.logger = Logger.new(log_path)
|
100
178
|
session_app.on_complete do |_result|
|
101
179
|
# find the template
|
102
180
|
class_name = _result.key.class.to_s.underscore
|
@@ -116,28 +194,31 @@ module Tap
|
|
116
194
|
file << Support::Templater.new(File.read(template)).build(:_result => _result)
|
117
195
|
end
|
118
196
|
end
|
119
|
-
end
|
197
|
+
end unless session_app.on_complete_block
|
120
198
|
|
121
199
|
id
|
122
200
|
end
|
123
201
|
|
124
|
-
# Returns the
|
125
|
-
# method may be extended to provide a session-specific App, hence it
|
126
|
-
# has been stubbed with an id input.
|
202
|
+
# Returns the session-specific App, or the server app if id is nil.
|
127
203
|
def app(id=nil)
|
128
204
|
@app
|
129
205
|
end
|
130
206
|
|
131
|
-
# Returns the env.root
|
132
|
-
# method may be extended to provide a session-specific Root, hence it
|
133
|
-
# has been stubbed with an id input.
|
207
|
+
# Returns the session-specific Root, or the server env.root if id is nil.
|
134
208
|
def root(id=nil)
|
135
209
|
@env.root
|
136
210
|
end
|
137
211
|
|
138
|
-
# Returns
|
139
|
-
|
140
|
-
|
212
|
+
# Returns a uri mapping to the specified controller and action. Parameters
|
213
|
+
# may be specified; they are built as a query and attached to the uri as
|
214
|
+
# normal.
|
215
|
+
#
|
216
|
+
# Currenlty uri does not map the controller to a minipath, but in the
|
217
|
+
# future it will.
|
218
|
+
def uri(controller=nil, action=nil, params={})
|
219
|
+
query = build_query(params)
|
220
|
+
uri = ["http://#{host}:#{port}", escape(controller), action].compact.join("/")
|
221
|
+
query.empty? ? uri : "#{uri}?#{query}"
|
141
222
|
end
|
142
223
|
|
143
224
|
# The {Rack}[http://rack.rubyforge.org/doc/] interface method.
|
@@ -173,25 +254,31 @@ module Tap
|
|
173
254
|
ServerError.response($!)
|
174
255
|
end
|
175
256
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
File.read(path)
|
257
|
+
# Searches env for the first matching file, directories are not matched.
|
258
|
+
def search(dir, path)
|
259
|
+
env.search(dir, path) {|file| File.file?(file) }
|
180
260
|
end
|
181
261
|
|
182
|
-
|
183
|
-
# TODO: implement caching for public_paths
|
184
|
-
def public_path(path)
|
185
|
-
env.search(public_dir, path) {|public_path| File.file?(public_path) }
|
186
|
-
end
|
262
|
+
protected
|
187
263
|
|
188
|
-
|
189
|
-
#
|
190
|
-
|
191
|
-
env.search(views_dir, path) {|template_path| File.file?(template_path) }
|
264
|
+
# Returns true if environment is :development.
|
265
|
+
def development? # :nodoc:
|
266
|
+
environment == :development
|
192
267
|
end
|
193
268
|
|
194
|
-
|
269
|
+
# Looks up and returns the first available Rack::Handler as listed in the
|
270
|
+
# servers configuration. (Note rack_handler returns a handler class, not
|
271
|
+
# an instance). Adapted from Sinatra.detect_rack_handler
|
272
|
+
def rack_handler # :nodoc:
|
273
|
+
servers.each do |server_name|
|
274
|
+
begin
|
275
|
+
return Rack::Handler.get(server_name)
|
276
|
+
rescue LoadError
|
277
|
+
rescue NameError
|
278
|
+
end
|
279
|
+
end
|
280
|
+
raise "Server handler (#{servers.join(',')}) not found."
|
281
|
+
end
|
195
282
|
|
196
283
|
# a helper method for routing a key to a controller
|
197
284
|
def lookup(key) # :nodoc:
|
@@ -213,10 +300,16 @@ module Tap
|
|
213
300
|
# load the require_path in dev mode so that
|
214
301
|
# controllers will be reloaded each time
|
215
302
|
if development? && const.require_path
|
216
|
-
if
|
217
|
-
Object
|
303
|
+
parent = if const.nesting.empty?
|
304
|
+
Object
|
305
|
+
else
|
306
|
+
Tap::Support::Constant.constantize(const.nesting) { nil }
|
218
307
|
end
|
219
|
-
|
308
|
+
|
309
|
+
if parent && parent.const_defined?(const.const_name)
|
310
|
+
parent.send(:remove_const, const.const_name)
|
311
|
+
end
|
312
|
+
|
220
313
|
load const.require_path
|
221
314
|
end
|
222
315
|
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Tap
|
2
|
+
module Support
|
3
|
+
|
4
|
+
# A very simple wrapper for root providing a CRUD interface for reading and
|
5
|
+
# writing files.
|
6
|
+
class Persistence
|
7
|
+
|
8
|
+
# The Tap::Root for self.
|
9
|
+
attr_reader :root
|
10
|
+
|
11
|
+
# Initializes a new persistence wrapper for the specified root.
|
12
|
+
def initialize(root)
|
13
|
+
@root = root
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the filepath for the specified id. Non-string ids are allowed;
|
17
|
+
# they will be converted to strings using to_s.
|
18
|
+
def path(id)
|
19
|
+
root.subpath(:data, id.to_s)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns a list of existing ids.
|
23
|
+
def index
|
24
|
+
root.glob(:data).select do |path|
|
25
|
+
File.file?(path)
|
26
|
+
end.collect do |path|
|
27
|
+
root.relative_filepath(:data, path)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Creates the file for the specified id. If a block is given, an io to
|
32
|
+
# the file will be yielded to it; otherwise the file will be created
|
33
|
+
# without content. Returns the path to the persistence file.
|
34
|
+
#
|
35
|
+
# Raises an error if the file already exists.
|
36
|
+
def create(id)
|
37
|
+
filepath = path(id)
|
38
|
+
raise "already exists: #{filepath}" if File.exists?(filepath)
|
39
|
+
root.prepare(filepath) {|io| yield(io) if block_given? }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Reads and returns the data for the specified id, or an empty string if
|
43
|
+
# the persistence file doesn't exist.
|
44
|
+
def read(id)
|
45
|
+
filepath = path(id)
|
46
|
+
File.file?(filepath) ? File.read(filepath) : ''
|
47
|
+
end
|
48
|
+
|
49
|
+
# Overwrites the data for the specified id. A block must be given to
|
50
|
+
# provide the new content; a persistence file will be created if one
|
51
|
+
# does not exist already.
|
52
|
+
def update(id)
|
53
|
+
root.prepare(path(id)) {|io| yield(io) }
|
54
|
+
end
|
55
|
+
|
56
|
+
# Removes the persistence file for id, if it exists. Returns true if
|
57
|
+
# the file was removed.
|
58
|
+
def destroy(id)
|
59
|
+
filepath = path(id)
|
60
|
+
|
61
|
+
if File.file?(filepath)
|
62
|
+
FileUtils.rm(filepath)
|
63
|
+
true
|
64
|
+
else
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tap-server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Chiang
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-03-
|
12
|
+
date: 2009-03-17 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -47,10 +47,12 @@ files:
|
|
47
47
|
- lib/tap/controller.rb
|
48
48
|
- lib/tap/controllers/app.rb
|
49
49
|
- lib/tap/controllers/schema.rb
|
50
|
+
- lib/tap/controllers/server.rb
|
50
51
|
- lib/tap/server.rb
|
51
52
|
- lib/tap/server_error.rb
|
52
53
|
- lib/tap/tasks/echo.rb
|
53
54
|
- lib/tap/tasks/server.rb
|
55
|
+
- lib/tap/support/persistence.rb
|
54
56
|
- public/javascripts/prototype.js
|
55
57
|
- public/javascripts/tap.js
|
56
58
|
- public/stylesheets/tap.css
|
@@ -70,6 +72,7 @@ files:
|
|
70
72
|
- views/tap/controllers/schema/preview.erb
|
71
73
|
- views/tap/controllers/schema/round.erb
|
72
74
|
- views/tap/controllers/schema/schema.erb
|
75
|
+
- views/tap/controllers/server/index.erb
|
73
76
|
- views/tap/tasks/echo/result.html
|
74
77
|
- README
|
75
78
|
- MIT-LICENSE
|