tap-server 0.2.0 → 0.3.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 +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
|