blix-rest 0.1.30
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.
- checksums.yaml +7 -0
- data/LICENSE +25 -0
- data/README.md +457 -0
- data/lib/blix/rest.rb +145 -0
- data/lib/blix/rest/controller.rb +512 -0
- data/lib/blix/rest/cucumber.rb +8 -0
- data/lib/blix/rest/cucumber/hooks.rb +5 -0
- data/lib/blix/rest/cucumber/request_steps.rb +207 -0
- data/lib/blix/rest/cucumber/resource_steps.rb +28 -0
- data/lib/blix/rest/cucumber/world.rb +273 -0
- data/lib/blix/rest/format.rb +15 -0
- data/lib/blix/rest/format_parser.rb +167 -0
- data/lib/blix/rest/handlebars_assets_fix.rb +10 -0
- data/lib/blix/rest/request_mapper.rb +332 -0
- data/lib/blix/rest/resource_cache.rb +50 -0
- data/lib/blix/rest/response.rb +26 -0
- data/lib/blix/rest/server.rb +208 -0
- data/lib/blix/rest/string_hash.rb +100 -0
- data/lib/blix/rest/version.rb +5 -0
- data/lib/blix/utils.rb +2 -0
- data/lib/blix/utils/misc.rb +62 -0
- data/lib/blix/utils/redis_store.rb +173 -0
- data/lib/blix/utils/yaml_config.rb +74 -0
- metadata +126 -0
data/lib/blix/rest.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'logger'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module Blix
|
6
|
+
module Rest
|
7
|
+
MIME_TYPE_JSON = 'application/json'.freeze
|
8
|
+
# EXPIRED_TOKEN_MESSAGE = 'token expired'
|
9
|
+
# INVALID_TOKEN_MESSAGE = 'invalid token'
|
10
|
+
|
11
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
12
|
+
CONTENT_TYPE_JSON = 'application/json'.freeze
|
13
|
+
CONTENT_TYPE_HTML = 'text/html; charset=utf-8'.freeze
|
14
|
+
CONTENT_TYPE_XML = 'application/xml'.freeze
|
15
|
+
AUTH_HEADER = 'WWW-Authenticate'.freeze
|
16
|
+
CACHE_CONTROL = 'Cache-Control'.freeze
|
17
|
+
CACHE_NO_STORE = 'no-store'.freeze
|
18
|
+
PRAGMA = 'Pragma'.freeze
|
19
|
+
NO_CACHE = 'no-cache'.freeze
|
20
|
+
URL_ENCODED = %r{^application/x-www-form-urlencoded}.freeze
|
21
|
+
JSON_ENCODED = %r{^application/json}.freeze # NOTE: "text/json" and "text/javascript" are deprecated forms
|
22
|
+
HTML_ENCODED = %r{^text/html}.freeze
|
23
|
+
XML_ENCODED = %r{^application/xml}.freeze
|
24
|
+
|
25
|
+
HTTP_DATE_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'.freeze
|
26
|
+
HTTP_VERBS = %w[GET HEAD POST PUT DELETE OPTIONS PATCH].freeze
|
27
|
+
HTTP_BODY_VERBS = %w[POST PUT PATCH].freeze
|
28
|
+
|
29
|
+
# the test/development/production environment
|
30
|
+
def self.environment
|
31
|
+
@_environment ||= ENV['RACK_ENV'] || 'development'
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.environment?(val)
|
35
|
+
environment == val.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.environment=(val)
|
39
|
+
@_environment = val.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.logger=(val)
|
43
|
+
@_logger = val
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.logger
|
47
|
+
@_logger ||= begin
|
48
|
+
l = Logger.new(STDOUT)
|
49
|
+
unless l.respond_to? :write # common logger needs a write method
|
50
|
+
def l.write(*args)
|
51
|
+
self.<<(*args)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
l
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
class BinaryData < String
|
60
|
+
def as_json(*_a)
|
61
|
+
{ 'base64Binary' => Base64.encode64(self) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_json(*a)
|
65
|
+
as_json.to_json(*a)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# interpret payload string as json
|
70
|
+
class RawJsonString
|
71
|
+
def initialize(str)
|
72
|
+
@str = str
|
73
|
+
end
|
74
|
+
|
75
|
+
def as_json(*_a)
|
76
|
+
@str
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_json(*a)
|
80
|
+
as_json.to_json(*a)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class BadRequestError < StandardError; end
|
85
|
+
|
86
|
+
class ServiceError < StandardError
|
87
|
+
attr_reader :status
|
88
|
+
attr_reader :headers
|
89
|
+
|
90
|
+
def initialize(message, status = nil, headers = nil)
|
91
|
+
super(message || "")
|
92
|
+
@status = status || 406
|
93
|
+
@headers = headers
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class AuthorizationError < StandardError
|
98
|
+
attr_reader :realm, :type
|
99
|
+
def initialize(message=nil, realm=nil, type=nil)
|
100
|
+
super(message || "")
|
101
|
+
@realm = realm || 'rest'
|
102
|
+
@type = type || 'Basic'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class NilClass
|
109
|
+
def empty?
|
110
|
+
true
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# common classes
|
115
|
+
require 'multi_json'
|
116
|
+
require 'logger'
|
117
|
+
require 'blix/rest/version'
|
118
|
+
require 'blix/rest/string_hash'
|
119
|
+
|
120
|
+
# client classes
|
121
|
+
# require 'blix/rest/remote_service'
|
122
|
+
# require 'blix/rest/web_frame_service'
|
123
|
+
# require 'blix/rest/service'
|
124
|
+
# require 'blix/rest/service_resource'
|
125
|
+
|
126
|
+
# provider classes
|
127
|
+
require 'rack'
|
128
|
+
require 'blix/rest/response'
|
129
|
+
require 'blix/rest/format_parser'
|
130
|
+
require 'blix/rest/request_mapper'
|
131
|
+
require 'blix/rest/server'
|
132
|
+
# require 'blix/rest/provider'
|
133
|
+
require 'blix/rest/controller'
|
134
|
+
# require 'blix/rest/provider_controller'
|
135
|
+
|
136
|
+
# ensure that that times are sent in the correct json format
|
137
|
+
class Time
|
138
|
+
def as_json(*_a)
|
139
|
+
utc.iso8601
|
140
|
+
end
|
141
|
+
|
142
|
+
def to_json(*a)
|
143
|
+
as_json.to_json(*a)
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,512 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
require 'erb'
|
5
|
+
require 'securerandom'
|
6
|
+
|
7
|
+
module Blix::Rest
|
8
|
+
# base class for controllers. within your block handling a particular route you
|
9
|
+
# have access to a number of methods
|
10
|
+
#
|
11
|
+
# env : the request environment hash
|
12
|
+
# body : the request body as a string
|
13
|
+
# body_hash : the request body as a hash constructed from json
|
14
|
+
# query_params : a hash of parameters as passed in the url as parameters
|
15
|
+
# path_params : a hash of parameters constructed from variable parts of the path
|
16
|
+
# params : all the params combined
|
17
|
+
# user : the user making this request ( or nil if
|
18
|
+
# format : the format the response should be in :json or :html
|
19
|
+
# session : the rack session if middleware has been used
|
20
|
+
#
|
21
|
+
# to accept requests other thatn json then set :accept=>[:json,:html] as options in the route
|
22
|
+
# eg post '/myform' :accept=>[:html] # this will only accept html requests.
|
23
|
+
|
24
|
+
class Controller
|
25
|
+
|
26
|
+
#--------------------------------------------------------------------------------------------------------
|
27
|
+
# convenience methods
|
28
|
+
#--------------------------------------------------------------------------------------------------------
|
29
|
+
def env
|
30
|
+
@_env
|
31
|
+
end
|
32
|
+
|
33
|
+
# options that were passed to the server at create time.
|
34
|
+
def server_options
|
35
|
+
@_server_options
|
36
|
+
end
|
37
|
+
|
38
|
+
def logger
|
39
|
+
Blix::Rest.logger
|
40
|
+
end
|
41
|
+
|
42
|
+
def rack_env
|
43
|
+
ENV['RACK_ENV']
|
44
|
+
end
|
45
|
+
|
46
|
+
def mode_test?
|
47
|
+
rack_env == 'test'
|
48
|
+
end
|
49
|
+
|
50
|
+
def mode_development?
|
51
|
+
rack_env == 'development'
|
52
|
+
end
|
53
|
+
|
54
|
+
def mode_production?
|
55
|
+
rack_env == 'production'
|
56
|
+
end
|
57
|
+
|
58
|
+
def body
|
59
|
+
@_body ||= env['rack.input'].read
|
60
|
+
# env['rack.input'].rewindreq.POST #env["body"]
|
61
|
+
end
|
62
|
+
|
63
|
+
def path
|
64
|
+
req.path
|
65
|
+
end
|
66
|
+
|
67
|
+
def form_hash
|
68
|
+
StringHash.new(req.POST)
|
69
|
+
end
|
70
|
+
|
71
|
+
def body_hash
|
72
|
+
@_body_hash ||= if body.empty?
|
73
|
+
{}
|
74
|
+
else
|
75
|
+
# should we check the content type here?
|
76
|
+
begin
|
77
|
+
StringHash.new(MultiJson.load(body))
|
78
|
+
rescue StandardError
|
79
|
+
raise ServiceError, "error in data json format/#{body}/"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_data(field)
|
85
|
+
body_hash['data'] && body_hash['data'][field]
|
86
|
+
end
|
87
|
+
|
88
|
+
def format
|
89
|
+
@_format
|
90
|
+
end
|
91
|
+
|
92
|
+
def query_params
|
93
|
+
@_query_params
|
94
|
+
end
|
95
|
+
|
96
|
+
def path_params
|
97
|
+
@_path_params
|
98
|
+
end
|
99
|
+
|
100
|
+
def params
|
101
|
+
@_params ||= StringHash.new(@_query_params,@_path_params)
|
102
|
+
end
|
103
|
+
|
104
|
+
def post_params
|
105
|
+
@_post_params ||= begin
|
106
|
+
type = req.media_type
|
107
|
+
if type && Rack::Request::FORM_DATA_MEDIA_TYPES.include?(type)
|
108
|
+
form_hash
|
109
|
+
else
|
110
|
+
body_hash
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def path_for(path)
|
116
|
+
File.join(RequestMapper.path_root, path)
|
117
|
+
end
|
118
|
+
|
119
|
+
def url_for(path)
|
120
|
+
req.base_url + path_for(path)
|
121
|
+
end
|
122
|
+
|
123
|
+
def req
|
124
|
+
@_req
|
125
|
+
end
|
126
|
+
|
127
|
+
def verb
|
128
|
+
@_verb
|
129
|
+
end
|
130
|
+
|
131
|
+
def method
|
132
|
+
env['REQUEST_METHOD'].downcase
|
133
|
+
end
|
134
|
+
|
135
|
+
def session
|
136
|
+
req.session
|
137
|
+
end
|
138
|
+
|
139
|
+
# add on the root path
|
140
|
+
def full_path(path)
|
141
|
+
RequestMapper.full_path(path)
|
142
|
+
end
|
143
|
+
|
144
|
+
# the full url of this path.
|
145
|
+
def full_url(_path)
|
146
|
+
raise 'not yet implemented'
|
147
|
+
end
|
148
|
+
|
149
|
+
def redirect(path, status = 302)
|
150
|
+
raise ServiceError.new(nil, status, 'Location' => path)
|
151
|
+
end
|
152
|
+
|
153
|
+
alias redirect_to redirect
|
154
|
+
|
155
|
+
def request_ip
|
156
|
+
req.ip
|
157
|
+
end
|
158
|
+
|
159
|
+
# render an erb template with the variables in the controller
|
160
|
+
def render_erb(template_name, opts = {})
|
161
|
+
self.class.render_erb(template_name, self, opts)
|
162
|
+
end
|
163
|
+
|
164
|
+
def render(text, opts = {})
|
165
|
+
self.class.render_erb(text, self, opts)
|
166
|
+
end
|
167
|
+
|
168
|
+
def rawjson(str)
|
169
|
+
RawJsonString.new(str)
|
170
|
+
end
|
171
|
+
|
172
|
+
def _get_binding
|
173
|
+
binding
|
174
|
+
end
|
175
|
+
|
176
|
+
# extract the user and login from the basic authentication
|
177
|
+
def get_basic_auth(realm=nil)
|
178
|
+
data = env['HTTP_AUTHORIZATION']
|
179
|
+
raise AuthorizationError.new('authentication missing',realm) unless data
|
180
|
+
|
181
|
+
type = data[0, 5]
|
182
|
+
rest = data[6..-1]
|
183
|
+
|
184
|
+
raise AuthorizationError.new('wrong authentication method',realm) unless type == 'Basic'
|
185
|
+
raise AuthorizationError.new('username:password missing',realm) unless rest
|
186
|
+
|
187
|
+
auth_parts = Base64.decode64(rest).split(':')
|
188
|
+
login = auth_parts[0]
|
189
|
+
password = auth_parts[1]
|
190
|
+
[login, password]
|
191
|
+
end
|
192
|
+
|
193
|
+
def set_status(value)
|
194
|
+
@_response.status = value
|
195
|
+
end
|
196
|
+
|
197
|
+
def add_headers(headers)
|
198
|
+
@_response.headers.merge!(headers)
|
199
|
+
end
|
200
|
+
|
201
|
+
# the following is copied from Rack::Utils
|
202
|
+
ESCAPE_HTML = {
|
203
|
+
'&' => '&',
|
204
|
+
'<' => '<',
|
205
|
+
'>' => '>',
|
206
|
+
"'" => ''',
|
207
|
+
'"' => '"',
|
208
|
+
'/' => '/'
|
209
|
+
}.freeze
|
210
|
+
|
211
|
+
JS_ESCAPE_MAP = { '\\' => '\\\\', '</' => '<\/', "\r\n" => '\n', "\n" => '\n', "\r" => '\n', '"' => '\\"', "'" => "\\'" }.freeze
|
212
|
+
|
213
|
+
ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
|
214
|
+
|
215
|
+
# Escape ampersands, brackets and quotes to their HTML/XML entities.
|
216
|
+
def h(string)
|
217
|
+
string.to_s.gsub(ESCAPE_HTML_PATTERN) { |c| ESCAPE_HTML[c] }
|
218
|
+
end
|
219
|
+
|
220
|
+
# escape javascript
|
221
|
+
def escape_javascript(javascript)
|
222
|
+
if javascript
|
223
|
+
javascript.gsub(%r{(\|</|\r\n|\342\200\250|\342\200\251|[\n\r"'])}u) { |match| JS_ESCAPE_MAP[match] }
|
224
|
+
else
|
225
|
+
''
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# send a (default) error
|
230
|
+
def send_error(message, status = nil, headers = nil)
|
231
|
+
raise ServiceError.new(message, status, headers)
|
232
|
+
end
|
233
|
+
|
234
|
+
def auth_error(*params)
|
235
|
+
if params[0].kind_of?(String)
|
236
|
+
message = params[0]
|
237
|
+
opts = params[1] || {}
|
238
|
+
else
|
239
|
+
message = nil
|
240
|
+
opts = params[-1] || {}
|
241
|
+
end
|
242
|
+
raise AuthorizationError.new(message,opts[:realm], opts[:type])
|
243
|
+
end
|
244
|
+
|
245
|
+
def get_cookie(name)
|
246
|
+
cookie_header = env['HTTP_COOKIE']
|
247
|
+
cookie_length = name.length
|
248
|
+
parts = cookie_header&.split(';')
|
249
|
+
value = nil
|
250
|
+
parts&.reverse&.each do |cookie|
|
251
|
+
cookie.strip!
|
252
|
+
if cookie[0..cookie_length] == name + '='
|
253
|
+
value = cookie[cookie_length + 1..-1]
|
254
|
+
break
|
255
|
+
end
|
256
|
+
end
|
257
|
+
value
|
258
|
+
end
|
259
|
+
|
260
|
+
def store_cookie(name, value, opts={})
|
261
|
+
cookie_text = String.new("#{name}=#{value}")
|
262
|
+
cookie_text << '; Secure' if _opt?(opts,:secure)
|
263
|
+
cookie_text << '; HttpOnly' if _opt?(opts,:http)
|
264
|
+
cookie_text << "; HostOnly=#{_opt(opts,:hostOnly)}" if _opt?(opts,:hostOnly)
|
265
|
+
cookie_text << "; Expires=#{_opt(opts,:expires).httpdate}" if _opt?(opts,:expires)
|
266
|
+
cookie_text << "; Max-Age=#{_opt(opts,:max_age)}" if _opt?(opts,:max_age)
|
267
|
+
cookie_text << "; Domain=#{_opt(opts,:domain)}" if _opt?(opts,:domain)
|
268
|
+
cookie_text << "; Path=#{_opt(opts,:path)}" if _opt?(opts,:path)
|
269
|
+
if policy = _opt(opts,:samesite)
|
270
|
+
cookie_text << '; SameSite=Strict' if policy.to_s.downcase == 'strict'
|
271
|
+
cookie_text << '; SameSite=Lax' if policy.to_s.downcase == 'lax'
|
272
|
+
cookie_text << '; SameSite=None' if policy.to_s.downcase == 'none'
|
273
|
+
end
|
274
|
+
@_cookies ||= {}
|
275
|
+
@_cookies[name] = cookie_text
|
276
|
+
# cookie_header = @_response.headers['Set-Cookie']
|
277
|
+
# if cookie_header
|
278
|
+
# cookie_header = cookie_header << "\n" << cookie_text
|
279
|
+
# else
|
280
|
+
# cookie_header = cookie_text
|
281
|
+
# end
|
282
|
+
@_response.headers['Set-Cookie'] = @_cookies.values.join("\n")
|
283
|
+
value
|
284
|
+
end
|
285
|
+
|
286
|
+
# manage session handling --------------------------------------------------
|
287
|
+
# setup the session and retrieve the session_id
|
288
|
+
# this id can be used to retrieve and data associated
|
289
|
+
# with the session_id in eg: a database or a memory hash
|
290
|
+
def get_session_id(session_name, opts = {})
|
291
|
+
session_id = get_cookie(session_name)
|
292
|
+
session_id || refresh_session_id(session_name, opts)
|
293
|
+
end
|
294
|
+
|
295
|
+
# generate an new session_id for the current session
|
296
|
+
def refresh_session_id(session_name, opts = {})
|
297
|
+
session_id = SecureRandom.hex(32)
|
298
|
+
store_session_id(session_name, session_id, opts)
|
299
|
+
end
|
300
|
+
|
301
|
+
def _opt?(opts,key)
|
302
|
+
opts.key?(key.to_sym) || opts.key?(key.to_s)
|
303
|
+
end
|
304
|
+
|
305
|
+
def _opt(opts,key)
|
306
|
+
if opts.key?(key.to_sym)
|
307
|
+
opts[key.to_sym]
|
308
|
+
else
|
309
|
+
opts[key.to_s]
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# set the cookie header that stores the session_id on the browser.
|
314
|
+
def store_session_id(session_name, session_id, opts = {})
|
315
|
+
store_cookie(session_name, session_id, opts)
|
316
|
+
end
|
317
|
+
|
318
|
+
#----------------------------------------------------------------------------------------------------------
|
319
|
+
# template methods that can be overwritten
|
320
|
+
|
321
|
+
# a hook used to insert processing for before the method call
|
322
|
+
def before(opts); end
|
323
|
+
|
324
|
+
# a hook used to insert processing for after the method call. return a hash containing
|
325
|
+
# the response.
|
326
|
+
def after(_opts, response)
|
327
|
+
response
|
328
|
+
end
|
329
|
+
|
330
|
+
#----------------------------------------------------------------------------------------------------------
|
331
|
+
|
332
|
+
def initialize(path_params, _params, req, format, verb, response, server_options)
|
333
|
+
@_req = req
|
334
|
+
@_env = req.env
|
335
|
+
@_query_params = StringHash.new(req.GET)
|
336
|
+
@_path_params = StringHash.new(path_params)
|
337
|
+
@_format = format
|
338
|
+
@_verb = verb
|
339
|
+
@_response = response
|
340
|
+
@_server_options = server_options
|
341
|
+
end
|
342
|
+
|
343
|
+
# do not cache templates in development mode
|
344
|
+
def self.no_template_cache
|
345
|
+
@_no_template_cache = (Blix::Rest.environment != 'production') if @_no_template_cache.nil?
|
346
|
+
@_no_template_cache
|
347
|
+
end
|
348
|
+
|
349
|
+
def self.no_template_cache=(val)
|
350
|
+
@_no_template_cache = val
|
351
|
+
end
|
352
|
+
|
353
|
+
# cache templates here
|
354
|
+
def self.erb_templates
|
355
|
+
@_erb ||= {}
|
356
|
+
end
|
357
|
+
|
358
|
+
def self.set_erb_root(dir)
|
359
|
+
@_erb_root = dir
|
360
|
+
end
|
361
|
+
|
362
|
+
def self.erb_root
|
363
|
+
@_erb_root ||= begin
|
364
|
+
root = File.join(Dir.pwd, 'app', 'views')
|
365
|
+
raise('use set_erb_root() to specify the location of your views') unless Dir.exist?(root)
|
366
|
+
|
367
|
+
root
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
class << self
|
372
|
+
|
373
|
+
# render a string within a layout.
|
374
|
+
def render(text, context, opts = {})
|
375
|
+
layout_name = opts[:layout]
|
376
|
+
path = opts[:path] || __erb_path || Controller.erb_root
|
377
|
+
|
378
|
+
layout = layout_name && if no_template_cache
|
379
|
+
ERB.new(File.read(File.join(path, layout_name + '.html.erb')),nil,'-')
|
380
|
+
else
|
381
|
+
erb_templates[layout_name] ||= ERB.new(File.read(File.join(path, layout_name + '.html.erb')),nil,'-')
|
382
|
+
end
|
383
|
+
|
384
|
+
begin
|
385
|
+
if layout
|
386
|
+
layout.result(context._get_binding { |*_args| text })
|
387
|
+
else
|
388
|
+
text
|
389
|
+
end
|
390
|
+
rescue Exception
|
391
|
+
puts $!
|
392
|
+
puts $@
|
393
|
+
'*** TEMPLATE ERROR ***'
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
def render_erb(name, context, opts = {})
|
398
|
+
name = name.to_s
|
399
|
+
layout_name = opts[:layout] && opts[:layout].to_s
|
400
|
+
locals = opts[:locals]
|
401
|
+
path = opts[:erb_dir] || __erb_path || Controller.erb_root
|
402
|
+
|
403
|
+
layout = layout_name && if no_template_cache
|
404
|
+
ERB.new(File.read(File.join(path, layout_name + '.html.erb')),nil,'-')
|
405
|
+
else
|
406
|
+
erb_templates[layout_name] ||= ERB.new(File.read(File.join(path, layout_name + '.html.erb')),nil,'-')
|
407
|
+
end
|
408
|
+
|
409
|
+
erb = if no_template_cache
|
410
|
+
ERB.new(File.read(File.join(path, name + '.html.erb')),nil,'-')
|
411
|
+
else
|
412
|
+
erb_templates[name] ||= ERB.new(File.read(File.join(path, name + '.html.erb')),nil,'-')
|
413
|
+
end
|
414
|
+
|
415
|
+
begin
|
416
|
+
bind = context._get_binding
|
417
|
+
locals&.each { |k, v| bind.local_variable_set(k, v) } # works from ruby 2.1
|
418
|
+
if layout
|
419
|
+
layout.result(context._get_binding { |*_args| erb.result(bind) })
|
420
|
+
else
|
421
|
+
erb.result(bind)
|
422
|
+
end
|
423
|
+
rescue Exception
|
424
|
+
puts $!
|
425
|
+
puts $@
|
426
|
+
'*** TEMPLATE ERROR ***'
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
# default method .. will be overridden with erb_path method
|
431
|
+
def __erb_path
|
432
|
+
nil
|
433
|
+
end
|
434
|
+
|
435
|
+
# redefine the __erb_path method for this and derived classes
|
436
|
+
def erb_dir(val)
|
437
|
+
str = "def self.__erb_path;\"#{val}\";end"
|
438
|
+
class_eval str
|
439
|
+
end
|
440
|
+
|
441
|
+
def check_format(accept, format)
|
442
|
+
return if (format == :json) && accept.nil? # the majority of cases
|
443
|
+
return if (format == :_) && accept.nil? # assume json by default.
|
444
|
+
|
445
|
+
accept ||= :json
|
446
|
+
accept = [accept].flatten
|
447
|
+
raise ServiceError, 'invalid format for this request' unless accept.index format
|
448
|
+
end
|
449
|
+
|
450
|
+
def route(verb, path, opts = {}, &blk)
|
451
|
+
proc = lambda do |_path_params, _params, _req, _format, _response, server_options|
|
452
|
+
unless opts[:force] && (opts[:accept] == :*)
|
453
|
+
check_format(opts[:accept], _format)
|
454
|
+
end
|
455
|
+
app = new(_path_params, _params, _req, _format, verb, _response, server_options)
|
456
|
+
begin
|
457
|
+
app.before(opts)
|
458
|
+
response = app.instance_eval( &blk )
|
459
|
+
rescue
|
460
|
+
raise
|
461
|
+
ensure
|
462
|
+
app.after(opts, response)
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
RequestMapper.add_path(verb.to_s.upcase, path, opts, &proc)
|
467
|
+
end
|
468
|
+
|
469
|
+
def get(*a, &b)
|
470
|
+
route 'GET', *a, &b
|
471
|
+
end
|
472
|
+
|
473
|
+
def head(*a, &b)
|
474
|
+
route 'HEAD', *a, &b
|
475
|
+
end
|
476
|
+
|
477
|
+
def post(*a, &b)
|
478
|
+
route 'POST', *a, &b
|
479
|
+
end
|
480
|
+
|
481
|
+
def put(*a, &b)
|
482
|
+
route 'PUT', *a, &b
|
483
|
+
end
|
484
|
+
|
485
|
+
def patch(*a, &b)
|
486
|
+
route 'PATCH', *a, &b
|
487
|
+
end
|
488
|
+
|
489
|
+
def delete(*a, &b)
|
490
|
+
route 'DELETE', *a, &b
|
491
|
+
end
|
492
|
+
|
493
|
+
def all(*a, &b)
|
494
|
+
route 'ALL', *a, &b
|
495
|
+
end
|
496
|
+
|
497
|
+
def options(*a, &b)
|
498
|
+
route 'OPTIONS', *a, &b
|
499
|
+
end
|
500
|
+
|
501
|
+
end
|
502
|
+
|
503
|
+
end
|
504
|
+
|
505
|
+
def self.set_erb_root(*args)
|
506
|
+
Controller.set_erb_root(*args)
|
507
|
+
end
|
508
|
+
|
509
|
+
def self.no_template_cache=(val)
|
510
|
+
Controller.no_template_cache = val
|
511
|
+
end
|
512
|
+
end
|