Class: Blix::Rest::Controller

Inherits:
Object
  • Object
show all
Defined in:
lib/blix/rest/controller.rb

Overview

base class for controllers. within your block handling a particular route you have access to a number of methods

env : the request environment hash body : the request body as a string body_hash : the request body as a hash constructed from json query_params : a hash of parameters as passed in the url as parameters path_params : a hash of parameters constructed from variable parts of the path params : all the params combined user : the user making this request ( or nil if format : the format the response should be in :json or :html session : the rack session if middleware has been used

to accept requests other thatn json then set :accept=>[:json,:html] as options in the route
  eg  post '/myform' :accept=>[:html]              # this will only accept html requests.

Constant Summary collapse

ESCAPE_HTML =

the following is copied from Rack::Utils

{
  '&' => '&',
  '<' => '&lt;',
  '>' => '&gt;',
  "'" => '&#x27;',
  '"' => '&quot;',
  '/' => '&#x2F;'
}.freeze
JS_ESCAPE_MAP =
{ '\\' => '\\\\', '</' => '<\/', "\r\n" => '\n', "\n" => '\n', "\r" => '\n', '"' => '\\"', "'" => "\\'" }.freeze
ESCAPE_HTML_PATTERN =
Regexp.union(*ESCAPE_HTML.keys)

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path_params, _params, req, format, verb, response, server_options) ⇒ Controller




332
333
334
335
336
337
338
339
340
341
# File 'lib/blix/rest/controller.rb', line 332

def initialize(path_params, _params, req, format, verb, response, server_options)
  @_req = req
  @_env = req.env
  @_query_params = StringHash.new(req.GET)
  @_path_params  = StringHash.new(path_params)
  @_format = format
  @_verb = verb
  @_response = response
  @_server_options = server_options
end

Class Method Details

.__erb_pathObject

default method .. will be overridden with erb_path method



431
432
433
# File 'lib/blix/rest/controller.rb', line 431

def __erb_path
  nil
end

.all(*a, &b) ⇒ Object



493
494
495
# File 'lib/blix/rest/controller.rb', line 493

def all(*a, &b)
  route 'ALL', *a, &b
end

.check_format(accept, format) ⇒ Object

Raises:



441
442
443
444
445
446
447
448
# File 'lib/blix/rest/controller.rb', line 441

def check_format(accept, format)
  return if (format == :json) && accept.nil?  # the majority of cases
  return if (format == :_) && accept.nil?     # assume json by default.

  accept ||= :json
  accept = [accept].flatten
  raise ServiceError, 'invalid format for this request' unless accept.index format
end

.delete(*a, &b) ⇒ Object



489
490
491
# File 'lib/blix/rest/controller.rb', line 489

def delete(*a, &b)
  route 'DELETE', *a, &b
end

.erb_dir(val) ⇒ Object

redefine the __erb_path method for this and derived classes



436
437
438
439
# File 'lib/blix/rest/controller.rb', line 436

def erb_dir(val)
  str = "def self.__erb_path;\"#{val}\";end"
  class_eval str
end

.erb_rootObject



362
363
364
365
366
367
368
369
# File 'lib/blix/rest/controller.rb', line 362

def self.erb_root
  @_erb_root ||= begin
    root = File.join(Dir.pwd, 'app', 'views')
    raise('use set_erb_root() to specify the location of your views') unless Dir.exist?(root)

    root
  end
end

.erb_templatesObject

cache templates here



354
355
356
# File 'lib/blix/rest/controller.rb', line 354

def self.erb_templates
  @_erb ||= {}
end

.get(*a, &b) ⇒ Object



469
470
471
# File 'lib/blix/rest/controller.rb', line 469

def get(*a, &b)
  route 'GET', *a, &b
end

.head(*a, &b) ⇒ Object



473
474
475
# File 'lib/blix/rest/controller.rb', line 473

def head(*a, &b)
  route 'HEAD', *a, &b
end

.no_template_cacheObject

do not cache templates in development mode



344
345
346
347
# File 'lib/blix/rest/controller.rb', line 344

def self.no_template_cache
  @_no_template_cache = (Blix::Rest.environment != 'production') if @_no_template_cache.nil?
  @_no_template_cache
end

.no_template_cache=(val) ⇒ Object



349
350
351
# File 'lib/blix/rest/controller.rb', line 349

def self.no_template_cache=(val)
  @_no_template_cache = val
end

.options(*a, &b) ⇒ Object



497
498
499
# File 'lib/blix/rest/controller.rb', line 497

def options(*a, &b)
  route 'OPTIONS', *a, &b
end

.patch(*a, &b) ⇒ Object



485
486
487
# File 'lib/blix/rest/controller.rb', line 485

def patch(*a, &b)
  route 'PATCH', *a, &b
end

.post(*a, &b) ⇒ Object



477
478
479
# File 'lib/blix/rest/controller.rb', line 477

def post(*a, &b)
  route 'POST', *a, &b
end

.put(*a, &b) ⇒ Object



481
482
483
# File 'lib/blix/rest/controller.rb', line 481

def put(*a, &b)
  route 'PUT', *a, &b
end

.render(text, context, opts = {}) ⇒ Object

render a string within a layout.



374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
# File 'lib/blix/rest/controller.rb', line 374

def render(text, context, opts = {})
  layout_name = opts[:layout]
  path        = opts[:path] || __erb_path || Controller.erb_root

  layout = layout_name && if no_template_cache
                            ERB.new(File.read(File.join(path, layout_name + '.html.erb')),nil,'-')
                          else
                            erb_templates[layout_name] ||= ERB.new(File.read(File.join(path, layout_name + '.html.erb')),nil,'-')
  end

  begin
    if layout
      layout.result(context._get_binding { |*_args| text })
    else
      text
    end
  rescue Exception
    puts $!
    puts $@
    '*** TEMPLATE ERROR ***'
  end
end

.render_erb(name, context, opts = {}) ⇒ Object



397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
# File 'lib/blix/rest/controller.rb', line 397

def render_erb(name, context, opts = {})
  name        = name.to_s
  layout_name = opts[:layout] && opts[:layout].to_s
  locals      = opts[:locals]
  path        = opts[:erb_dir] || __erb_path || Controller.erb_root

  layout = layout_name && if no_template_cache
                            ERB.new(File.read(File.join(path, layout_name + '.html.erb')),nil,'-')
                          else
                            erb_templates[layout_name] ||= ERB.new(File.read(File.join(path, layout_name + '.html.erb')),nil,'-')
  end

  erb = if no_template_cache
          ERB.new(File.read(File.join(path, name + '.html.erb')),nil,'-')
        else
          erb_templates[name] ||= ERB.new(File.read(File.join(path, name + '.html.erb')),nil,'-')
  end

  begin
    bind = context._get_binding
    locals&.each { |k, v| bind.local_variable_set(k, v) } # works from ruby 2.1
    if layout
      layout.result(context._get_binding { |*_args| erb.result(bind) })
    else
      erb.result(bind)
    end
  rescue Exception
    puts $!
    puts $@
    '*** TEMPLATE ERROR ***'
  end
end

.route(verb, path, opts = {}, &blk) ⇒ Object



450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# File 'lib/blix/rest/controller.rb', line 450

def route(verb, path, opts = {}, &blk)
  proc = lambda do |_path_params, _params, _req, _format, _response, server_options|
    unless opts[:force] && (opts[:accept] == :*)
      check_format(opts[:accept], _format)
    end
    app = new(_path_params, _params, _req, _format, verb, _response, server_options)
    begin
      app.before(opts)
      response = app.instance_eval( &blk )
    rescue
      raise
    ensure
      app.after(opts, response)
    end
  end

  RequestMapper.add_path(verb.to_s.upcase, path, opts, &proc)
end

.set_erb_root(dir) ⇒ Object



358
359
360
# File 'lib/blix/rest/controller.rb', line 358

def self.set_erb_root(dir)
  @_erb_root = dir
end

Instance Method Details

#_get_bindingObject



172
173
174
# File 'lib/blix/rest/controller.rb', line 172

def _get_binding
  binding
end

#_opt(opts, key) ⇒ Object



305
306
307
308
309
310
311
# File 'lib/blix/rest/controller.rb', line 305

def  _opt(opts,key)
  if opts.key?(key.to_sym)
    opts[key.to_sym]
  else
    opts[key.to_s]
  end
end

#_opt?(opts, key) ⇒ Boolean

Returns:

  • (Boolean)


301
302
303
# File 'lib/blix/rest/controller.rb', line 301

def _opt?(opts,key)
  opts.key?(key.to_sym) || opts.key?(key.to_s)
end

#add_headers(headers) ⇒ Object



197
198
199
# File 'lib/blix/rest/controller.rb', line 197

def add_headers(headers)
  @_response.headers.merge!(headers)
end

#after(_opts, response) ⇒ Object

a hook used to insert processing for after the method call. return a hash containing the response.



326
327
328
# File 'lib/blix/rest/controller.rb', line 326

def after(_opts, response)
  response
end

#auth_error(*params) ⇒ Object

Raises:



234
235
236
237
238
239
240
241
242
243
# File 'lib/blix/rest/controller.rb', line 234

def auth_error(*params)
  if params[0].kind_of?(String)
      message = params[0]
      opts = params[1] || {}
  else
      message = nil
      opts = params[-1] || {}
  end
  raise AuthorizationError.new(message,opts[:realm], opts[:type])
end

#before(opts) ⇒ Object

a hook used to insert processing for before the method call



322
# File 'lib/blix/rest/controller.rb', line 322

def before(opts); end

#bodyObject



58
59
60
61
# File 'lib/blix/rest/controller.rb', line 58

def body
  @_body ||= env['rack.input'].read
  #      env['rack.input'].rewindreq.POST #env["body"]
end

#body_hashObject



71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/blix/rest/controller.rb', line 71

def body_hash
  @_body_hash ||= if body.empty?
                    {}
                  else
                    # should we check the content type here?
                    begin
                      StringHash.new(MultiJson.load(body))
                    rescue StandardError
                      raise ServiceError, "error in data json format/#{body}/"
                    end
  end
end

#envObject


convenience methods




29
30
31
# File 'lib/blix/rest/controller.rb', line 29

def env
  @_env
end

#escape_javascript(javascript) ⇒ Object

escape javascript



221
222
223
224
225
226
227
# File 'lib/blix/rest/controller.rb', line 221

def escape_javascript(javascript)
  if javascript
    javascript.gsub(%r{(\|</|\r\n|\342\200\250|\342\200\251|[\n\r"'])}u) { |match| JS_ESCAPE_MAP[match] }
  else
    ''
  end
end

#form_hashObject



67
68
69
# File 'lib/blix/rest/controller.rb', line 67

def form_hash
  StringHash.new(req.POST)
end

#formatObject



88
89
90
# File 'lib/blix/rest/controller.rb', line 88

def format
  @_format
end

#full_path(path) ⇒ Object

add on the root path



140
141
142
# File 'lib/blix/rest/controller.rb', line 140

def full_path(path)
  RequestMapper.full_path(path)
end

#full_url(_path) ⇒ Object

the full url of this path.



145
146
147
# File 'lib/blix/rest/controller.rb', line 145

def full_url(_path)
  raise 'not yet implemented'
end

#get_basic_auth(realm = nil) ⇒ Object

extract the user and login from the basic authentication

Raises:



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/blix/rest/controller.rb', line 177

def get_basic_auth(realm=nil)
  data = env['HTTP_AUTHORIZATION']
  raise AuthorizationError.new('authentication missing',realm) unless data

  type = data[0, 5]
  rest = data[6..-1]

  raise  AuthorizationError.new('wrong authentication method',realm) unless type == 'Basic'
  raise  AuthorizationError.new('username:password missing',realm)   unless rest

  auth_parts = Base64.decode64(rest).split(':')
   = auth_parts[0]
  password = auth_parts[1]
  [, password]
end


245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/blix/rest/controller.rb', line 245

def get_cookie(name)
  cookie_header = env['HTTP_COOKIE']
  cookie_length = name.length
  parts = cookie_header&.split(';')
  value = nil
  parts&.reverse&.each do |cookie|
    cookie.strip!
    if cookie[0..cookie_length] == name + '='
      value = cookie[cookie_length + 1..-1]
      break
    end
  end
  value
end

#get_data(field) ⇒ Object



84
85
86
# File 'lib/blix/rest/controller.rb', line 84

def get_data(field)
  body_hash['data'] && body_hash['data'][field]
end

#get_session_id(session_name, opts = {}) ⇒ Object

manage session handling ————————————————– setup the session and retrieve the session_id this id can be used to retrieve and data associated with the session_id in eg: a database or a memory hash



290
291
292
293
# File 'lib/blix/rest/controller.rb', line 290

def get_session_id(session_name, opts = {})
  session_id = get_cookie(session_name)
  session_id || refresh_session_id(session_name, opts)
end

#h(string) ⇒ Object

Escape ampersands, brackets and quotes to their HTML/XML entities.



216
217
218
# File 'lib/blix/rest/controller.rb', line 216

def h(string)
  string.to_s.gsub(ESCAPE_HTML_PATTERN) { |c| ESCAPE_HTML[c] }
end

#loggerObject



38
39
40
# File 'lib/blix/rest/controller.rb', line 38

def logger
  Blix::Rest.logger
end

#methodObject



131
132
133
# File 'lib/blix/rest/controller.rb', line 131

def method
  env['REQUEST_METHOD'].downcase
end

#mode_development?Boolean

Returns:

  • (Boolean)


50
51
52
# File 'lib/blix/rest/controller.rb', line 50

def mode_development?
  rack_env == 'development'
end

#mode_production?Boolean

Returns:

  • (Boolean)


54
55
56
# File 'lib/blix/rest/controller.rb', line 54

def mode_production?
  rack_env == 'production'
end

#mode_test?Boolean

Returns:

  • (Boolean)


46
47
48
# File 'lib/blix/rest/controller.rb', line 46

def mode_test?
  rack_env == 'test'
end

#paramsObject



100
101
102
# File 'lib/blix/rest/controller.rb', line 100

def params
  @_params ||= StringHash.new(@_query_params,@_path_params)
end

#pathObject



63
64
65
# File 'lib/blix/rest/controller.rb', line 63

def path
  req.path
end

#path_for(path) ⇒ Object



115
116
117
# File 'lib/blix/rest/controller.rb', line 115

def path_for(path)
  File.join(RequestMapper.path_root, path)
end

#path_paramsObject



96
97
98
# File 'lib/blix/rest/controller.rb', line 96

def path_params
  @_path_params
end

#post_paramsObject



104
105
106
107
108
109
110
111
112
113
# File 'lib/blix/rest/controller.rb', line 104

def post_params
  @_post_params ||= begin
    type = req.media_type
    if type && Rack::Request::FORM_DATA_MEDIA_TYPES.include?(type)
      form_hash
    else
      body_hash
    end
  end
end

#query_paramsObject



92
93
94
# File 'lib/blix/rest/controller.rb', line 92

def query_params
  @_query_params
end

#rack_envObject



42
43
44
# File 'lib/blix/rest/controller.rb', line 42

def rack_env
  ENV['RACK_ENV']
end

#rawjson(str) ⇒ Object



168
169
170
# File 'lib/blix/rest/controller.rb', line 168

def rawjson(str)
  RawJsonString.new(str)
end

#redirect(path, status = 302) ⇒ Object Also known as: redirect_to

Raises:



149
150
151
# File 'lib/blix/rest/controller.rb', line 149

def redirect(path, status = 302)
  raise ServiceError.new(nil, status, 'Location' => path)
end

#refresh_session_id(session_name, opts = {}) ⇒ Object

generate an new session_id for the current session



296
297
298
299
# File 'lib/blix/rest/controller.rb', line 296

def refresh_session_id(session_name, opts = {})
  session_id = SecureRandom.hex(32)
  store_session_id(session_name, session_id, opts)
end

#render(text, opts = {}) ⇒ Object



164
165
166
# File 'lib/blix/rest/controller.rb', line 164

def render(text, opts = {})
  self.class.render_erb(text, self, opts)
end

#render_erb(template_name, opts = {}) ⇒ Object

render an erb template with the variables in the controller



160
161
162
# File 'lib/blix/rest/controller.rb', line 160

def render_erb(template_name, opts = {})
  self.class.render_erb(template_name, self, opts)
end

#reqObject



123
124
125
# File 'lib/blix/rest/controller.rb', line 123

def req
  @_req
end

#request_ipObject



155
156
157
# File 'lib/blix/rest/controller.rb', line 155

def request_ip
  req.ip
end

#send_error(message, status = nil, headers = nil) ⇒ Object

send a (default) error

Raises:



230
231
232
# File 'lib/blix/rest/controller.rb', line 230

def send_error(message, status = nil, headers = nil)
  raise ServiceError.new(message, status, headers)
end

#server_optionsObject

options that were passed to the server at create time.



34
35
36
# File 'lib/blix/rest/controller.rb', line 34

def server_options
  @_server_options
end

#sessionObject



135
136
137
# File 'lib/blix/rest/controller.rb', line 135

def session
  req.session
end

#set_status(value) ⇒ Object



193
194
195
# File 'lib/blix/rest/controller.rb', line 193

def set_status(value)
  @_response.status = value
end


260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/blix/rest/controller.rb', line 260

def store_cookie(name, value, opts={})
  cookie_text = String.new("#{name}=#{value}")
  cookie_text << '; Secure'                                  if _opt?(opts,:secure)
  cookie_text << '; HttpOnly'                                if _opt?(opts,:http)
  cookie_text << "; HostOnly=#{_opt(opts,:hostOnly)}"        if _opt?(opts,:hostOnly)
  cookie_text << "; Expires=#{_opt(opts,:expires).httpdate}" if _opt?(opts,:expires)
  cookie_text << "; Max-Age=#{_opt(opts,:max_age)}"          if _opt?(opts,:max_age)
  cookie_text << "; Domain=#{_opt(opts,:domain)}"            if _opt?(opts,:domain)
  cookie_text << "; Path=#{_opt(opts,:path)}"                if _opt?(opts,:path)
  if policy = _opt(opts,:samesite)
    cookie_text << '; SameSite=Strict' if policy.to_s.downcase == 'strict'
    cookie_text << '; SameSite=Lax'    if policy.to_s.downcase == 'lax'
    cookie_text << '; SameSite=None'   if policy.to_s.downcase == 'none'
  end
  @_cookies ||= {}
  @_cookies[name] = cookie_text
  # cookie_header = @_response.headers['Set-Cookie']
  # if cookie_header
  #   cookie_header = cookie_header << "\n" << cookie_text
  # else
  #   cookie_header = cookie_text
  # end
  @_response.headers['Set-Cookie'] = @_cookies.values.join("\n")
  value
end

#store_session_id(session_name, session_id, opts = {}) ⇒ Object

set the cookie header that stores the session_id on the browser.



314
315
316
# File 'lib/blix/rest/controller.rb', line 314

def store_session_id(session_name, session_id, opts = {})
  store_cookie(session_name, session_id, opts)
end

#url_for(path) ⇒ Object



119
120
121
# File 'lib/blix/rest/controller.rb', line 119

def url_for(path)
  req.base_url + path_for(path)
end

#verbObject



127
128
129
# File 'lib/blix/rest/controller.rb', line 127

def verb
  @_verb
end