tap-server 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +5 -0
- data/cmd/server.rb +36 -8
- data/lib/tap/controller/rest_routes.rb +77 -0
- data/lib/tap/controller/utils.rb +29 -0
- data/lib/tap/controller.rb +244 -145
- data/lib/tap/controllers/app.rb +97 -55
- data/lib/tap/controllers/data.rb +132 -0
- data/lib/tap/controllers/schema.rb +137 -223
- data/lib/tap/controllers/server.rb +70 -27
- data/lib/tap/server/data.rb +178 -0
- data/lib/tap/server/runner.rb +71 -0
- data/lib/tap/server/server_error.rb +35 -0
- data/lib/tap/server.rb +60 -275
- data/lib/tap/tasks/echo.rb +39 -6
- data/lib/tap/tasks/render.rb +65 -0
- data/public/stylesheets/tap.css +15 -2
- data/views/configurable/_config.erb +1 -0
- data/views/configurable/_configs.erb +28 -0
- data/views/configurable/_flag.erb +2 -0
- data/views/configurable/_list_select.erb +8 -0
- data/views/configurable/_select.erb +6 -0
- data/views/configurable/_switch.erb +2 -0
- data/views/layout.erb +1 -1
- data/views/object/obj.erb +1 -0
- data/views/tap/controllers/app/_action.erb +3 -0
- data/views/tap/controllers/app/build.erb +18 -0
- data/views/tap/controllers/app/enque.erb +13 -0
- data/views/tap/controllers/app/info.erb +20 -7
- data/views/tap/controllers/data/_controls.erb +21 -0
- data/views/tap/controllers/data/_index_entry.erb +1 -0
- data/views/tap/controllers/data/_upload.erb +8 -0
- data/views/tap/controllers/data/entry.erb +8 -0
- data/views/tap/controllers/data/index.erb +14 -0
- data/views/tap/controllers/schema/_build.erb +6 -0
- data/views/tap/controllers/schema/_index_entry.erb +6 -0
- data/views/tap/controllers/schema/entry.erb +135 -0
- data/views/tap/controllers/schema/join.erb +6 -4
- data/views/tap/controllers/schema/task.erb +6 -0
- data/views/tap/controllers/server/access.erb +4 -0
- data/views/tap/controllers/server/admin.erb +21 -0
- data/views/tap/controllers/server/help.erb +23 -0
- data/views/tap/controllers/server/index.erb +43 -3
- data/views/tap/task/input.erb +17 -0
- data/views/tap/tasks/load/input.erb +11 -0
- metadata +35 -17
- data/lib/tap/server_error.rb +0 -34
- data/lib/tap/support/persistence.rb +0 -71
- data/lib/tap/tasks/server.rb +0 -30
- data/views/tap/controllers/app/index.erb +0 -44
- data/views/tap/controllers/schema/config/default.erb +0 -4
- data/views/tap/controllers/schema/config/flag.erb +0 -3
- data/views/tap/controllers/schema/config/switch.erb +0 -6
- data/views/tap/controllers/schema/configurations.erb +0 -27
- data/views/tap/controllers/schema/node.erb +0 -47
- data/views/tap/controllers/schema/preview.erb +0 -3
- data/views/tap/controllers/schema/round.erb +0 -30
- data/views/tap/controllers/schema/schema.erb +0 -28
- data/views/tap/tasks/echo/result.html +0 -1
data/History
CHANGED
data/cmd/server.rb
CHANGED
@@ -1,26 +1,54 @@
|
|
1
|
-
# tap server {options}
|
1
|
+
# tap server {options}
|
2
2
|
#
|
3
3
|
# Initializes a tap server.
|
4
|
+
#
|
4
5
|
|
5
6
|
require 'tap'
|
6
7
|
require 'tap/server'
|
7
8
|
|
8
9
|
env = Tap::Env.instance
|
9
10
|
app = Tap::App.instance
|
10
|
-
|
11
|
+
|
12
|
+
begin
|
13
|
+
opts = ConfigParser.new('env' => env, 'app' => app)
|
14
|
+
opts.separator ""
|
15
|
+
opts.separator "configurations:"
|
16
|
+
opts.add(Tap::Server.configurations)
|
11
17
|
|
12
18
|
opts.separator ""
|
13
19
|
opts.separator "options:"
|
14
|
-
|
15
|
-
|
16
|
-
|
20
|
+
|
21
|
+
opts.on('--config FILE', 'Specifies a config file') do |config_file|
|
22
|
+
opts.config.merge! Configurable::Utils.load_file(config_file)
|
23
|
+
end
|
24
|
+
|
17
25
|
opts.on("-h", "--help", "Show this message") do
|
18
26
|
puts Lazydoc.usage(__FILE__)
|
19
27
|
puts opts
|
20
28
|
exit
|
21
29
|
end
|
30
|
+
|
31
|
+
# (note defaults are not added so they will not
|
32
|
+
# conflict with string keys from a config file)
|
33
|
+
args = opts.parse!(ARGV, :clear_config => false, :add_defaults => false)
|
34
|
+
|
35
|
+
if args.empty?
|
36
|
+
args << 'server'
|
37
|
+
end
|
38
|
+
|
39
|
+
controller = env[:controller][args.shift]
|
40
|
+
|
41
|
+
unless args.empty?
|
42
|
+
warn "ignoring args: #{args.inspect}"
|
43
|
+
end
|
44
|
+
|
45
|
+
Tap::Server.new(controller, opts.nested_config).run!
|
46
|
+
rescue
|
47
|
+
raise if $DEBUG
|
48
|
+
puts $!.message
|
49
|
+
exit(1)
|
22
50
|
end
|
23
|
-
argv = parser.parse(ARGV)
|
24
51
|
|
25
|
-
|
26
|
-
|
52
|
+
exit(0)
|
53
|
+
|
54
|
+
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Tap
|
2
|
+
class Controller
|
3
|
+
# Adds REST routing to a Tap::Controller.
|
4
|
+
#
|
5
|
+
# class Projects < Tap::Controller
|
6
|
+
# include RestRoutes
|
7
|
+
#
|
8
|
+
# # GET /projects
|
9
|
+
# def index...
|
10
|
+
#
|
11
|
+
# # GET /projects/*args
|
12
|
+
# def show(*args)...
|
13
|
+
#
|
14
|
+
# # POST /projects/*args
|
15
|
+
# def create(*args)...
|
16
|
+
#
|
17
|
+
# # PUT /projects/*args
|
18
|
+
# # POST /projects/*args?_method=put
|
19
|
+
# def update(*args)...
|
20
|
+
#
|
21
|
+
# # DELETE /projects/*args
|
22
|
+
# # POST /projects/*args?_method=delete
|
23
|
+
# def destroy(*args)...
|
24
|
+
#
|
25
|
+
# # extension...
|
26
|
+
#
|
27
|
+
# # POST /projects/*args?_method=another
|
28
|
+
# def another(*args)...
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# === Relation to RESTful Rails
|
32
|
+
#
|
33
|
+
# Unlike the REST syntax in Rails, '/projects/new' is treated like a show
|
34
|
+
# where the id is 'new'. Also missing is the routing for urls like
|
35
|
+
# '/projects/arg;edit/'. See these resources:
|
36
|
+
#
|
37
|
+
# * {RESTful Rails Development}[http://www.b-simple.de/download/restful_rails_en.pdf]
|
38
|
+
# * {REST cheatsheet}[topfunky.com/clients/peepcode/REST-cheatsheet.pdf]
|
39
|
+
#
|
40
|
+
module RestRoutes
|
41
|
+
def route
|
42
|
+
blank, *route = request.path_info.split("/").collect {|arg| unescape(arg) }
|
43
|
+
route.unshift rest_action(route)
|
44
|
+
route
|
45
|
+
end
|
46
|
+
|
47
|
+
def rest_action(args)
|
48
|
+
case request.request_method
|
49
|
+
when /GET/i
|
50
|
+
if args.empty?
|
51
|
+
:index
|
52
|
+
else
|
53
|
+
:show
|
54
|
+
end
|
55
|
+
when /POST/i
|
56
|
+
case _method = request[:_method]
|
57
|
+
when /put/i
|
58
|
+
:update
|
59
|
+
when /delete/i
|
60
|
+
:destroy
|
61
|
+
when nil
|
62
|
+
:create
|
63
|
+
else
|
64
|
+
if action?(_method)
|
65
|
+
_method
|
66
|
+
else
|
67
|
+
raise Server::ServerError.new("unknown post method: #{_method}")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
when /PUT/i then :update
|
71
|
+
when /DELETE/i then :destroy
|
72
|
+
else raise Server::ServerError.new("unknown request method: #{request.request_method}")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Tap
|
2
|
+
class Controller
|
3
|
+
module Utils
|
4
|
+
|
5
|
+
def static_file(path)
|
6
|
+
content = File.read(path)
|
7
|
+
headers = {
|
8
|
+
"Last-Modified" => File.mtime(path).httpdate,
|
9
|
+
"Content-Type" => Rack::Mime.mime_type(File.extname(path), 'text/plain'),
|
10
|
+
"Content-Length" => content.size.to_s
|
11
|
+
}
|
12
|
+
|
13
|
+
[200, headers, [content]]
|
14
|
+
end
|
15
|
+
|
16
|
+
def download(path)
|
17
|
+
content = File.read(path)
|
18
|
+
headers = {
|
19
|
+
"Last-Modified" => File.mtime(path).httpdate,
|
20
|
+
"Content-Type" => Rack::Mime.mime_type(File.extname(path), 'text/plain'),
|
21
|
+
"Content-Disposition" => "attachment; filename=#{File.basename(path)};",
|
22
|
+
"Content-Length" => content.size.to_s
|
23
|
+
}
|
24
|
+
|
25
|
+
[200, headers, [content]]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/tap/controller.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
require 'erb'
|
1
2
|
require 'tap/server'
|
2
|
-
require 'tap/
|
3
|
-
|
3
|
+
require 'tap/controller/rest_routes'
|
4
|
+
require 'tap/controller/utils'
|
4
5
|
|
5
6
|
module Tap
|
6
7
|
|
7
8
|
# === Declaring Actions
|
9
|
+
#
|
8
10
|
# By default all public methods in subclasses are declared as actions. You
|
9
11
|
# can declare a private or protected method as an action by:
|
10
12
|
#
|
@@ -17,119 +19,112 @@ module Tap
|
|
17
19
|
# * define it private or protected then call public(:method)
|
18
20
|
#
|
19
21
|
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
|
-
|
70
22
|
class << self
|
71
|
-
|
23
|
+
|
72
24
|
# Initialize instance variables on the child and inherit as necessary.
|
73
25
|
def inherited(child) # :nodoc:
|
74
26
|
super
|
27
|
+
|
28
|
+
unless child.instance_variable_defined?(:@source_file)
|
29
|
+
caller[0] =~ Lazydoc::CALLER_REGEXP
|
30
|
+
child.instance_variable_set(:@source_file, File.expand_path($1))
|
31
|
+
end
|
32
|
+
|
33
|
+
set_variables.each do |variable|
|
34
|
+
child.set(variable, get(variable))
|
35
|
+
end
|
36
|
+
|
75
37
|
child.set(:actions, actions.dup)
|
76
|
-
child.set(:default_layout, default_layout)
|
77
38
|
child.set(:define_action, true)
|
78
39
|
end
|
79
|
-
|
40
|
+
|
80
41
|
# An array of methods that can be called as actions. Actions must be
|
81
42
|
# stored as symbols. Actions are inherited.
|
82
43
|
attr_reader :actions
|
83
|
-
|
84
|
-
# The default
|
85
|
-
attr_reader :
|
86
|
-
|
87
|
-
# The base path prepended to render paths (ie render(<path>) renders
|
88
|
-
# <templates_dir/name/path>).
|
89
|
-
def name
|
90
|
-
@name ||= to_s.underscore
|
91
|
-
end
|
44
|
+
|
45
|
+
# The default action called for the request path '/'
|
46
|
+
attr_reader :default_action
|
92
47
|
|
93
48
|
# Instantiates self and performs call.
|
94
49
|
def call(env)
|
95
50
|
new.call(env)
|
96
51
|
end
|
97
|
-
|
98
|
-
# Sets an instance variable for self, short for:
|
52
|
+
|
53
|
+
# Sets an instance variable for self (ie the class), short for:
|
99
54
|
#
|
100
55
|
# instance_variable_set(:@attribute, input)
|
101
56
|
#
|
102
|
-
#
|
57
|
+
# These variables are meaningful to a default Tap::Controller and will
|
58
|
+
# be inherited by subclasses:
|
103
59
|
#
|
104
60
|
# actions:: sets actions
|
105
|
-
#
|
106
|
-
# default_layout:: the default layout (used by render)
|
61
|
+
# default_action:: the default action (:index)
|
107
62
|
#
|
108
63
|
def set(variable, input)
|
64
|
+
set_variables << variable
|
109
65
|
instance_variable_set("@#{variable}", input)
|
110
66
|
end
|
111
67
|
|
112
|
-
|
68
|
+
# Gets the value of an instance variable set via set. Returns nil for
|
69
|
+
# variables that have not been set through set.
|
70
|
+
def get(variable)
|
71
|
+
return nil unless set_variables.include?(variable)
|
72
|
+
instance_variable_get("@#{variable}")
|
73
|
+
end
|
113
74
|
|
75
|
+
# An array of variables set via set. set_variables are inherited.
|
76
|
+
def set_variables
|
77
|
+
@set_variables ||= []
|
78
|
+
end
|
79
|
+
|
80
|
+
def nest(key, controller, &block)
|
81
|
+
|
82
|
+
# generate a subclass if anything gets overridden
|
83
|
+
if block_given?
|
84
|
+
controller = Class.new(controller)
|
85
|
+
controller.class_eval(&block)
|
86
|
+
end
|
87
|
+
|
88
|
+
# this check prevents a warning in cases where the nesting
|
89
|
+
# class defines the nested class
|
90
|
+
const_name = key.to_s.camelize
|
91
|
+
unless const_defined?(const_name) && const_get(const_name) == subclass
|
92
|
+
const_set(const_name, controller)
|
93
|
+
end
|
94
|
+
|
95
|
+
define_method(key) do |*args|
|
96
|
+
instance = controller.new
|
97
|
+
|
98
|
+
instance.server = server
|
99
|
+
instance.controller_path = controller_path ? "#{controller_path}/#{key}" : key
|
100
|
+
instance.request = request
|
101
|
+
instance.response = response
|
102
|
+
|
103
|
+
instance.dispatch(args)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
protected
|
108
|
+
|
114
109
|
# Overridden so that if declare_action is set, new methods
|
115
110
|
# are added to actions.
|
116
111
|
def method_added(sym) # :nodoc:
|
117
112
|
actions << sym if @define_action
|
118
113
|
super
|
119
114
|
end
|
120
|
-
|
115
|
+
|
121
116
|
# Turns on declare_action when changing method context.
|
122
117
|
def public(*symbols) # :nodoc:
|
123
118
|
@define_action = true if symbols.empty?
|
124
119
|
super
|
125
120
|
end
|
126
|
-
|
121
|
+
|
127
122
|
# Turns off declare_action when changing method context.
|
128
123
|
def protected(*symbols) # :nodoc:
|
129
124
|
@define_action = false if symbols.empty?
|
130
125
|
super
|
131
126
|
end
|
132
|
-
|
127
|
+
|
133
128
|
# Turns off declare_action when changing method context.
|
134
129
|
def private(*symbols) # :nodoc:
|
135
130
|
@define_action = false if symbols.empty?
|
@@ -137,107 +132,234 @@ module Tap
|
|
137
132
|
end
|
138
133
|
end
|
139
134
|
|
135
|
+
extend Lazydoc::Attributes
|
136
|
+
include Rack::Utils
|
137
|
+
ServerError = Tap::Server::ServerError
|
138
|
+
|
139
|
+
lazy_attr :desc, 'controller'
|
140
|
+
|
140
141
|
set :actions, []
|
141
|
-
set :
|
142
|
+
set :default_action, :index
|
143
|
+
set :default_layout, 'layout.erb'
|
142
144
|
|
145
|
+
#--
|
143
146
|
# Ensures methods (even public methods) on Controller will
|
144
147
|
# not be actions in subclasses.
|
145
148
|
set :define_action, false
|
146
149
|
|
147
|
-
include Rack::Utils
|
148
|
-
|
149
|
-
# Accesses the 'tap.server' specified in env, set during call.
|
150
150
|
attr_accessor :server
|
151
151
|
|
152
|
+
attr_accessor :controller_path
|
153
|
+
|
152
154
|
# A Rack::Request wrapping env, set during call.
|
153
155
|
attr_accessor :request
|
154
|
-
|
156
|
+
|
155
157
|
# A Rack::Response. If the action returns a string, it will be written to
|
156
158
|
# response and response will be returned by call. Otherwise, call returns
|
157
159
|
# the action result and response is ignored.
|
158
160
|
attr_accessor :response
|
159
161
|
|
160
|
-
#
|
161
|
-
|
162
|
+
# Initializes a new instance of self.
|
163
|
+
def initialize
|
164
|
+
@request = @response = @server = @controller_path = nil
|
165
|
+
end
|
166
|
+
|
167
|
+
# Returns true if action is registered as an action for self.
|
168
|
+
def action?(action)
|
169
|
+
self.class.actions.include?(action.to_sym)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns a uri to the specified action on self.
|
173
|
+
def uri(action=nil, params={})
|
174
|
+
uri = []
|
175
|
+
|
176
|
+
if controller_path
|
177
|
+
uri << '/'
|
178
|
+
uri << controller_path
|
179
|
+
end
|
180
|
+
|
181
|
+
if action
|
182
|
+
uri << '/'
|
183
|
+
uri << action
|
184
|
+
end
|
185
|
+
|
186
|
+
unless params.empty?
|
187
|
+
uri << '?'
|
188
|
+
uri << build_query(params)
|
189
|
+
end
|
190
|
+
|
191
|
+
uri.join
|
192
|
+
end
|
193
|
+
|
194
|
+
def template_path(path)
|
195
|
+
server.env.path(:views, path) {|file| File.file?(file) }
|
196
|
+
end
|
162
197
|
|
163
|
-
|
164
|
-
|
165
|
-
def initialize(server=nil, request=nil, response=nil)
|
166
|
-
@server = server
|
167
|
-
@request = request
|
168
|
-
@response = response
|
169
|
-
@action = nil
|
198
|
+
def module_path(path, klass=self.class)
|
199
|
+
server.env.module_path(:views, klass.ancestors, path) {|file| File.file?(file) }
|
170
200
|
end
|
171
201
|
|
202
|
+
# Routes the request to an action and returns the response. Routing is
|
203
|
+
# simple and fixed (see route):
|
204
|
+
#
|
205
|
+
# route calls
|
206
|
+
# / default_action (ie 'index')
|
207
|
+
# /action/*args action(*args)
|
208
|
+
#
|
209
|
+
# If the action returns a string, it will be written to response.
|
210
|
+
# Otherwise, call returns the result of action. This allows actions like:
|
211
|
+
#
|
212
|
+
# class ActionsController < Tap::Controller
|
213
|
+
# def simple
|
214
|
+
# "html body"
|
215
|
+
# end
|
216
|
+
#
|
217
|
+
# def standard
|
218
|
+
# response["Content-Type"] = "text/plain"
|
219
|
+
# response << "text"
|
220
|
+
# response.finish
|
221
|
+
# end
|
222
|
+
#
|
223
|
+
# def custom
|
224
|
+
# [200, {"Content-Type" => "text/plain"}, ["text"]]
|
225
|
+
# end
|
226
|
+
# end
|
227
|
+
#
|
172
228
|
def call(env)
|
173
|
-
@server = env['tap.server']
|
229
|
+
@server = env['tap.server']
|
230
|
+
@controller_path = env['tap.controller_path']
|
231
|
+
|
174
232
|
@request = Rack::Request.new(env)
|
175
233
|
@response = Rack::Response.new
|
176
234
|
|
177
|
-
|
178
|
-
|
179
|
-
unless self.class.actions.include?(@action)
|
180
|
-
raise ServerError.new("404 Error: page not found", 404)
|
181
|
-
end
|
182
|
-
|
183
|
-
result = send(@action, *args)
|
184
|
-
if result.kind_of?(String)
|
235
|
+
case result = dispatch(route)
|
236
|
+
when String
|
185
237
|
response.write result
|
186
238
|
response.finish
|
239
|
+
when nil
|
240
|
+
response.finish
|
187
241
|
else
|
188
242
|
result
|
189
243
|
end
|
190
244
|
end
|
191
|
-
|
245
|
+
|
246
|
+
# Returns the action, args, and extname for the request.path_info. Routing
|
247
|
+
# is simple and fixed:
|
248
|
+
#
|
249
|
+
# route returns
|
250
|
+
# / [:index, []]
|
251
|
+
# /action/*args [:action, args]
|
252
|
+
#
|
253
|
+
# The action and args are unescaped by route. An alternate default action
|
254
|
+
# may be specified using set. Override this method in subclasses for
|
255
|
+
# fancier routes.
|
192
256
|
def route
|
193
|
-
blank,
|
194
|
-
|
195
|
-
|
257
|
+
blank, *route = request.path_info.split("/").collect {|arg| unescape(arg) }
|
258
|
+
route
|
259
|
+
end
|
260
|
+
|
261
|
+
def dispatch(route)
|
262
|
+
action, *args = route
|
263
|
+
|
264
|
+
if action == nil || action == ""
|
265
|
+
action = self.class.default_action
|
266
|
+
end
|
196
267
|
|
197
|
-
|
268
|
+
unless action?(action)
|
269
|
+
raise ServerError.new("404 Error: page not found", 404)
|
270
|
+
end
|
271
|
+
|
272
|
+
send(action, *args)
|
198
273
|
end
|
199
274
|
|
275
|
+
# Renders the class_file at path with the specified options. Path can be
|
276
|
+
# omitted if options specifies an alternate path to render. Options:
|
277
|
+
#
|
278
|
+
# template:: renders the template relative to the template directory
|
279
|
+
# file:: renders the specified file
|
280
|
+
# layout:: renders with the specified layout, or default_layout if true
|
281
|
+
# locals:: a hash of local variables used in the template
|
282
|
+
#
|
200
283
|
def render(path, options={})
|
201
284
|
options, path = path, nil if path.kind_of?(Hash)
|
202
|
-
|
285
|
+
|
203
286
|
# lookup template
|
204
287
|
template_path = case
|
205
|
-
when options
|
206
|
-
|
288
|
+
when options[:file]
|
289
|
+
options[:file]
|
290
|
+
when options[:template]
|
291
|
+
self.template_path(options[:template])
|
207
292
|
else
|
208
|
-
|
293
|
+
self.module_path(path)
|
209
294
|
end
|
210
|
-
|
295
|
+
|
211
296
|
unless template_path
|
212
|
-
raise "could not find template
|
297
|
+
raise "could not find template: (path: #{path.inspect}, file: #{options[:file].inspect}, template: #{options[:template].inspect})"
|
213
298
|
end
|
214
299
|
|
215
300
|
# render template
|
216
301
|
template = File.read(template_path)
|
217
|
-
content = render_erb(template, options)
|
302
|
+
content = render_erb(template, options, template_path)
|
218
303
|
|
219
304
|
# render layout
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
305
|
+
render_layout(options[:layout], content)
|
306
|
+
end
|
307
|
+
|
308
|
+
# Renders the specified layout with content as a local variable. If layout
|
309
|
+
# is true, the class default_layout will be rendered. Returns content if no
|
310
|
+
# layout is specified.
|
311
|
+
def render_layout(layout, content)
|
312
|
+
return content unless layout
|
313
|
+
|
314
|
+
if layout == true
|
315
|
+
layout = self.class.get(:default_layout)
|
316
|
+
end
|
317
|
+
|
318
|
+
if layout.kind_of?(Hash)
|
319
|
+
locals = layout[:locals] ||= {}
|
320
|
+
|
321
|
+
if locals.has_key?(:content)
|
322
|
+
raise "layout already has local content assigned: #{layout.inspect}"
|
323
|
+
end
|
324
|
+
|
325
|
+
locals[:content] = content
|
224
326
|
else
|
225
|
-
content
|
327
|
+
layout = {:template => layout, :locals => {:content => content}}
|
226
328
|
end
|
329
|
+
|
330
|
+
render(layout)
|
227
331
|
end
|
228
|
-
|
229
|
-
|
332
|
+
|
333
|
+
# Renders the specified template as ERB using the options. Options:
|
334
|
+
#
|
335
|
+
# locals:: a hash of local variables used in the template
|
336
|
+
#
|
337
|
+
# The filename used to identify errors in an erb template to a specific
|
338
|
+
# file and is completely options (but handy).
|
339
|
+
def render_erb(template, options={}, filename=nil)
|
230
340
|
# assign locals to the render binding
|
231
341
|
# this almost surely may be optimized...
|
232
342
|
locals = options[:locals]
|
233
|
-
binding =
|
234
|
-
|
343
|
+
binding = render_erb_binding
|
344
|
+
|
235
345
|
locals.each_pair do |key, value|
|
236
346
|
@assignment_value = value
|
237
347
|
eval("#{key} = remove_instance_variable(:@assignment_value)", binding)
|
238
348
|
end if locals
|
349
|
+
|
350
|
+
erb = ERB.new(template, nil, "<>")
|
351
|
+
erb.filename = filename
|
352
|
+
erb.result(binding)
|
353
|
+
end
|
354
|
+
|
355
|
+
def module_render(path, obj, options={})
|
356
|
+
obj = obj.class unless obj.kind_of?(Module)
|
357
|
+
options[:file] = module_path(path, obj) || module_path(path)
|
358
|
+
|
359
|
+
locals = options[:locals] ||= {}
|
360
|
+
locals[:obj] ||= obj
|
239
361
|
|
240
|
-
|
362
|
+
render options
|
241
363
|
end
|
242
364
|
|
243
365
|
# Redirects to the specified uri.
|
@@ -245,38 +367,15 @@ module Tap
|
|
245
367
|
response.status = status
|
246
368
|
response.headers.merge!(headers)
|
247
369
|
response.body = body
|
248
|
-
|
249
|
-
response['Location'] = uri
|
370
|
+
|
371
|
+
response['Location'] = [uri]
|
250
372
|
response.finish
|
251
373
|
end
|
252
374
|
|
253
|
-
|
254
|
-
def session
|
255
|
-
request.env['rack.session'] ||= {}
|
256
|
-
end
|
257
|
-
|
258
|
-
# Returns the app for the current session.
|
259
|
-
def app
|
260
|
-
server.app(session[:id] ||= server.initialize_session)
|
261
|
-
end
|
262
|
-
|
263
|
-
# Returns the root for the current session.
|
264
|
-
def root
|
265
|
-
server.root(session[:id] ||= server.initialize_session)
|
266
|
-
end
|
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
|
375
|
+
private
|
277
376
|
|
278
377
|
# Generates an empty binding to self without any locals assigned.
|
279
|
-
def
|
378
|
+
def render_erb_binding # :nodoc:
|
280
379
|
binding
|
281
380
|
end
|
282
381
|
end
|