blix-rest 0.1.30 → 0.8.2
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 +4 -4
- data/LICENSE +2 -2
- data/README.md +232 -30
- data/lib/blix/rest/cache.rb +79 -0
- data/lib/blix/rest/controller.rb +202 -29
- data/lib/blix/rest/cucumber/world.rb +13 -12
- data/lib/blix/rest/format_parser.rb +18 -11
- data/lib/blix/rest/redis_cache.rb +127 -0
- data/lib/blix/rest/request_mapper.rb +22 -6
- data/lib/blix/rest/response.rb +2 -2
- data/lib/blix/rest/route.rb +33 -0
- data/lib/blix/rest/server.rb +60 -20
- data/lib/blix/rest/session.rb +138 -0
- data/lib/blix/rest/version.rb +1 -1
- data/lib/blix/rest.rb +31 -10
- data/lib/blix/utils/redis_store.rb +2 -2
- metadata +23 -5
data/lib/blix/rest/controller.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'base64'
|
4
4
|
require 'erb'
|
5
5
|
require 'securerandom'
|
6
|
+
require 'digest'
|
6
7
|
|
7
8
|
module Blix::Rest
|
8
9
|
# base class for controllers. within your block handling a particular route you
|
@@ -21,8 +22,19 @@ module Blix::Rest
|
|
21
22
|
# to accept requests other thatn json then set :accept=>[:json,:html] as options in the route
|
22
23
|
# eg post '/myform' :accept=>[:html] # this will only accept html requests.
|
23
24
|
|
25
|
+
Context = Struct.new(
|
26
|
+
:path_params,
|
27
|
+
:params,
|
28
|
+
:req,
|
29
|
+
:format,
|
30
|
+
:response,
|
31
|
+
:method,
|
32
|
+
:server
|
33
|
+
)
|
34
|
+
|
24
35
|
class Controller
|
25
36
|
|
37
|
+
|
26
38
|
#--------------------------------------------------------------------------------------------------------
|
27
39
|
# convenience methods
|
28
40
|
#--------------------------------------------------------------------------------------------------------
|
@@ -35,6 +47,14 @@ module Blix::Rest
|
|
35
47
|
@_server_options
|
36
48
|
end
|
37
49
|
|
50
|
+
def server_cache
|
51
|
+
@_server_cache
|
52
|
+
end
|
53
|
+
|
54
|
+
def server_cache_get(key)
|
55
|
+
server_cache[key] ||= yield if block_given?
|
56
|
+
end
|
57
|
+
|
38
58
|
def logger
|
39
59
|
Blix::Rest.logger
|
40
60
|
end
|
@@ -60,10 +80,21 @@ module Blix::Rest
|
|
60
80
|
# env['rack.input'].rewindreq.POST #env["body"]
|
61
81
|
end
|
62
82
|
|
83
|
+
# ovverride the path method to return the internal path.
|
63
84
|
def path
|
64
|
-
req.path
|
85
|
+
p = req.path
|
86
|
+
p = '/' + p if p[0, 1] != '/' # ensure a leading slash on path
|
87
|
+
idx = RequestMapper.path_root_length
|
88
|
+
if idx > 0
|
89
|
+
p = p[idx..-1] || '/'
|
90
|
+
p = '/' + p if p[0, 1] != '/' # ensure a leading slash on path
|
91
|
+
p
|
92
|
+
else
|
93
|
+
p
|
94
|
+
end
|
65
95
|
end
|
66
96
|
|
97
|
+
|
67
98
|
def form_hash
|
68
99
|
StringHash.new(req.POST)
|
69
100
|
end
|
@@ -113,7 +144,7 @@ module Blix::Rest
|
|
113
144
|
end
|
114
145
|
|
115
146
|
def path_for(path)
|
116
|
-
File.join(RequestMapper.path_root, path)
|
147
|
+
File.join(RequestMapper.path_root, path || '')
|
117
148
|
end
|
118
149
|
|
119
150
|
def url_for(path)
|
@@ -128,6 +159,26 @@ module Blix::Rest
|
|
128
159
|
@_verb
|
129
160
|
end
|
130
161
|
|
162
|
+
def method
|
163
|
+
@_method
|
164
|
+
end
|
165
|
+
|
166
|
+
def route_parameters
|
167
|
+
@_parameters
|
168
|
+
end
|
169
|
+
|
170
|
+
def route_params
|
171
|
+
@_parameters
|
172
|
+
end
|
173
|
+
|
174
|
+
def route_options
|
175
|
+
@_parameters
|
176
|
+
end
|
177
|
+
|
178
|
+
def response
|
179
|
+
@_response
|
180
|
+
end
|
181
|
+
|
131
182
|
def method
|
132
183
|
env['REQUEST_METHOD'].downcase
|
133
184
|
end
|
@@ -147,7 +198,7 @@ module Blix::Rest
|
|
147
198
|
end
|
148
199
|
|
149
200
|
def redirect(path, status = 302)
|
150
|
-
raise ServiceError.new(nil, status, '
|
201
|
+
raise ServiceError.new(nil, status, 'location' => RequestMapper.ensure_full_path(path))
|
151
202
|
end
|
152
203
|
|
153
204
|
alias redirect_to redirect
|
@@ -190,12 +241,36 @@ module Blix::Rest
|
|
190
241
|
[login, password]
|
191
242
|
end
|
192
243
|
|
244
|
+
# set the cors headers
|
245
|
+
def set_accept_cors(opts={})
|
246
|
+
origin = opts[:origin] || env['HTTP_ORIGIN'] || '*'
|
247
|
+
origin = origin.to_s
|
248
|
+
if method=='options'
|
249
|
+
methods = [opts[:methods] || []].to_a.flatten
|
250
|
+
max_age = opts[:max_age] || 86400
|
251
|
+
headers = [opts[:headers] || []].to_a.flatten
|
252
|
+
credentials = opts.key?(:credentials) ? !!opts[:credentials] : true
|
253
|
+
methods = [:get] if methods.empty?
|
254
|
+
methods = methods.map{|m| m.to_s.upcase}
|
255
|
+
headers = ['Content-Type'] if headers.empty?
|
256
|
+
max_age = max_age.to_i
|
257
|
+
|
258
|
+
add_headers 'Access-Control-Allow-Origin' => origin,
|
259
|
+
'Access-Control-Allow-Methods'=>methods.join(', '),
|
260
|
+
'Access-Control-Allow-Headers'=>headers,
|
261
|
+
'Access-Control-Max-Age'=>max_age, #86400,
|
262
|
+
'Access-Control-Allow-Credentials'=>'true'
|
263
|
+
else
|
264
|
+
add_headers 'Access-Control-Allow-Origin' => origin
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
193
268
|
def set_status(value)
|
194
|
-
@_response.status = value
|
269
|
+
@_response.status = value.to_i
|
195
270
|
end
|
196
271
|
|
197
272
|
def add_headers(headers)
|
198
|
-
@_response.headers.merge!(headers)
|
273
|
+
@_response.headers.merge!(headers.map{|k,v| [k.to_s.downcase,v]}.to_h)
|
199
274
|
end
|
200
275
|
|
201
276
|
# the following is copied from Rack::Utils
|
@@ -231,6 +306,19 @@ module Blix::Rest
|
|
231
306
|
raise ServiceError.new(message, status, headers)
|
232
307
|
end
|
233
308
|
|
309
|
+
# send data to browser as attachment
|
310
|
+
def send_data(data, opts = {})
|
311
|
+
add_headers 'content-type'=> opts[:type] || 'application/octet-stream'
|
312
|
+
if opts[:filename]
|
313
|
+
add_headers 'content-disposition'=>'attachment;filename='+ opts[:filename]
|
314
|
+
elsif opts[:disposition] == 'attachment'
|
315
|
+
add_headers 'content-disposition'=>'attachment'
|
316
|
+
elsif opts[:disposition] == 'inline'
|
317
|
+
add_headers 'content-disposition'=>'inline'
|
318
|
+
end
|
319
|
+
raise RawResponse.new(data, opts[:status] || 200)
|
320
|
+
end
|
321
|
+
|
234
322
|
def auth_error(*params)
|
235
323
|
if params[0].kind_of?(String)
|
236
324
|
message = params[0]
|
@@ -279,7 +367,7 @@ module Blix::Rest
|
|
279
367
|
# else
|
280
368
|
# cookie_header = cookie_text
|
281
369
|
# end
|
282
|
-
@_response.headers['
|
370
|
+
@_response.headers['set-cookie'] = @_cookies.values.join("\n")
|
283
371
|
value
|
284
372
|
end
|
285
373
|
|
@@ -315,6 +403,16 @@ module Blix::Rest
|
|
315
403
|
store_cookie(session_name, session_id, opts)
|
316
404
|
end
|
317
405
|
|
406
|
+
# perform the before hooks.
|
407
|
+
def __before(*a)
|
408
|
+
self.class._do_before(self, *a)
|
409
|
+
end
|
410
|
+
|
411
|
+
# perform the after hooks
|
412
|
+
def __after(*a)
|
413
|
+
self.class._do_after(self, *a)
|
414
|
+
end
|
415
|
+
|
318
416
|
#----------------------------------------------------------------------------------------------------------
|
319
417
|
# template methods that can be overwritten
|
320
418
|
|
@@ -327,17 +425,32 @@ module Blix::Rest
|
|
327
425
|
response
|
328
426
|
end
|
329
427
|
|
428
|
+
def session_before(opts); end # empty session before hooh
|
429
|
+
def session_after; end # empty session after hook
|
430
|
+
|
330
431
|
#----------------------------------------------------------------------------------------------------------
|
331
432
|
|
332
|
-
def
|
333
|
-
@
|
334
|
-
@
|
433
|
+
def _setup(context, _verb, _path, _parameters)
|
434
|
+
@_context = context
|
435
|
+
@_req = context.req
|
436
|
+
@_env = req.env
|
335
437
|
@_query_params = StringHash.new(req.GET)
|
336
|
-
@_path_params = StringHash.new(path_params)
|
337
|
-
@_format
|
338
|
-
@_verb
|
339
|
-
@_response
|
340
|
-
@_server_options =
|
438
|
+
@_path_params = StringHash.new(context.path_params)
|
439
|
+
@_format = context.format
|
440
|
+
@_verb = _verb
|
441
|
+
@_response = context.response
|
442
|
+
@_server_options = context.server._options
|
443
|
+
@_parameters = _parameters
|
444
|
+
@_server_cache = context.server._cache
|
445
|
+
@_method = context.method
|
446
|
+
end
|
447
|
+
|
448
|
+
def to_s
|
449
|
+
"<#{self.class.to_s}:#{object_id}>"
|
450
|
+
end
|
451
|
+
|
452
|
+
def inspect
|
453
|
+
to_s
|
341
454
|
end
|
342
455
|
|
343
456
|
# do not cache templates in development mode
|
@@ -376,9 +489,9 @@ module Blix::Rest
|
|
376
489
|
path = opts[:path] || __erb_path || Controller.erb_root
|
377
490
|
|
378
491
|
layout = layout_name && if no_template_cache
|
379
|
-
ERB.new(File.read(File.join(path, layout_name + '.html.erb'))
|
492
|
+
ERB.new(File.read(File.join(path, layout_name + '.html.erb')),:trim_mode=>'-')
|
380
493
|
else
|
381
|
-
erb_templates[layout_name] ||= ERB.new(File.read(File.join(path, layout_name + '.html.erb'))
|
494
|
+
erb_templates[layout_name] ||= ERB.new(File.read(File.join(path, layout_name + '.html.erb')),:trim_mode=>'-')
|
382
495
|
end
|
383
496
|
|
384
497
|
begin
|
@@ -388,8 +501,8 @@ module Blix::Rest
|
|
388
501
|
text
|
389
502
|
end
|
390
503
|
rescue Exception
|
391
|
-
|
392
|
-
|
504
|
+
::Blix::Rest.logger << $!
|
505
|
+
::Blix::Rest.logger << $@
|
393
506
|
'*** TEMPLATE ERROR ***'
|
394
507
|
end
|
395
508
|
end
|
@@ -401,15 +514,15 @@ module Blix::Rest
|
|
401
514
|
path = opts[:erb_dir] || __erb_path || Controller.erb_root
|
402
515
|
|
403
516
|
layout = layout_name && if no_template_cache
|
404
|
-
ERB.new(File.read(File.join(path, layout_name + '.html.erb'))
|
517
|
+
ERB.new(File.read(File.join(path, layout_name + '.html.erb')),:trim_mode=>'-')
|
405
518
|
else
|
406
|
-
erb_templates[layout_name] ||= ERB.new(File.read(File.join(path, layout_name + '.html.erb'))
|
519
|
+
erb_templates[layout_name] ||= ERB.new(File.read(File.join(path, layout_name + '.html.erb')),:trim_mode=>'-')
|
407
520
|
end
|
408
521
|
|
409
522
|
erb = if no_template_cache
|
410
|
-
ERB.new(File.read(File.join(path, name + '.html.erb'))
|
523
|
+
ERB.new(File.read(File.join(path, name + '.html.erb')),:trim_mode=>'-')
|
411
524
|
else
|
412
|
-
erb_templates[name] ||= ERB.new(File.read(File.join(path, name + '.html.erb'))
|
525
|
+
erb_templates[name] ||= ERB.new(File.read(File.join(path, name + '.html.erb')),:trim_mode=>'-')
|
413
526
|
end
|
414
527
|
|
415
528
|
begin
|
@@ -421,8 +534,8 @@ module Blix::Rest
|
|
421
534
|
erb.result(bind)
|
422
535
|
end
|
423
536
|
rescue Exception
|
424
|
-
|
425
|
-
|
537
|
+
::Blix::Rest.logger << $!
|
538
|
+
::Blix::Rest.logger << $@
|
426
539
|
'*** TEMPLATE ERROR ***'
|
427
540
|
end
|
428
541
|
end
|
@@ -447,19 +560,36 @@ module Blix::Rest
|
|
447
560
|
raise ServiceError, 'invalid format for this request' unless accept.index format
|
448
561
|
end
|
449
562
|
|
563
|
+
def _do_route_hook(route)
|
564
|
+
if @_route_hook
|
565
|
+
superclass._do_route_hook(route) if superclass.respond_to? :_do_route_hook
|
566
|
+
@_route_hook.call(route)
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
450
570
|
def route(verb, path, opts = {}, &blk)
|
451
|
-
|
571
|
+
path = '/' + path unless path[0] == '/'
|
572
|
+
path = String.new(path) # in case frozen.
|
573
|
+
route = Route.new(verb, path, opts)
|
574
|
+
_do_route_hook(route)
|
575
|
+
proc = lambda do |context|
|
452
576
|
unless opts[:force] && (opts[:accept] == :*)
|
453
|
-
check_format(opts[:accept],
|
577
|
+
check_format(opts[:accept], context.format)
|
454
578
|
end
|
455
|
-
app = new
|
579
|
+
app = new
|
580
|
+
app._setup(context, verb, path, opts)
|
456
581
|
begin
|
582
|
+
app.session_before(opts)
|
457
583
|
app.before(opts)
|
458
|
-
|
584
|
+
app.__before
|
585
|
+
context.response = app.instance_eval( &blk )
|
459
586
|
rescue
|
460
587
|
raise
|
461
588
|
ensure
|
462
|
-
app.
|
589
|
+
app.__after
|
590
|
+
app.after(opts, context.response)
|
591
|
+
app.session_after
|
592
|
+
context.response
|
463
593
|
end
|
464
594
|
end
|
465
595
|
|
@@ -498,6 +628,49 @@ module Blix::Rest
|
|
498
628
|
route 'OPTIONS', *a, &b
|
499
629
|
end
|
500
630
|
|
631
|
+
def before_route(&b)
|
632
|
+
@_route_hook = b if b
|
633
|
+
end
|
634
|
+
|
635
|
+
|
636
|
+
def _before_hooks
|
637
|
+
@_before_hooks ||= {}
|
638
|
+
end
|
639
|
+
|
640
|
+
def _after_hooks
|
641
|
+
@_after_hooks ||= {}
|
642
|
+
end
|
643
|
+
|
644
|
+
def _do_before(ctx, *a)
|
645
|
+
superclass._do_before(ctx, *a) if superclass.respond_to? :_do_before
|
646
|
+
_before_hooks.each_value{ |h| ctx.instance_eval(&h) }
|
647
|
+
end
|
648
|
+
|
649
|
+
def _do_after(ctx, *a)
|
650
|
+
_after_hooks.each_value{ |h| ctx.instance_eval(&h) }
|
651
|
+
superclass._do_after(ctx, *a) if superclass.respond_to? :_do_after
|
652
|
+
end
|
653
|
+
|
654
|
+
# define a before hook for a controller. only one hook can be defined per
|
655
|
+
# controller in a single source file.
|
656
|
+
def before(&block)
|
657
|
+
if block
|
658
|
+
file = block.source_location[0]
|
659
|
+
warn("warning: before hook already defined in #{file}") if _before_hooks[file]
|
660
|
+
_before_hooks[file] = block
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
# define an after hook for a controller. only one hook can be defined per
|
665
|
+
# controller in a single source file.
|
666
|
+
def after(&block)
|
667
|
+
if block
|
668
|
+
file = block.source_location[0]
|
669
|
+
warn("warning: after hook already defined in #{file}") if _after_hooks[file]
|
670
|
+
_after_hooks[file] = block
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
501
674
|
end
|
502
675
|
|
503
676
|
end
|
@@ -4,7 +4,7 @@
|
|
4
4
|
class RestWorld
|
5
5
|
# the entry point to the rack application to be tested
|
6
6
|
def self.app
|
7
|
-
@_app ||= Rack::Builder.parse_file('config.ru')
|
7
|
+
@_app ||= Rack::Builder.parse_file('config.ru')
|
8
8
|
end
|
9
9
|
|
10
10
|
# a dummy request to sent to the server
|
@@ -16,15 +16,16 @@ class RestWorld
|
|
16
16
|
class Response
|
17
17
|
def initialize(resp)
|
18
18
|
@resp = resp
|
19
|
-
|
19
|
+
content_type = @resp.headers['Content-Type'] || @resp.headers['content-type']
|
20
|
+
if content_type == 'application/json'
|
20
21
|
begin
|
21
|
-
@h = MultiJson.load(
|
22
|
+
@h = MultiJson.load(body) || {}
|
22
23
|
rescue Exception => e
|
23
|
-
|
24
|
+
log 'INVALID RESPONSE BODY=>' + body
|
24
25
|
raise
|
25
26
|
end
|
26
27
|
else
|
27
|
-
@h = { 'html' =>
|
28
|
+
@h = { 'html' => body }
|
28
29
|
end
|
29
30
|
|
30
31
|
# get_ids_from_hash
|
@@ -35,7 +36,7 @@ class RestWorld
|
|
35
36
|
end
|
36
37
|
|
37
38
|
def body
|
38
|
-
@resp.body
|
39
|
+
[@resp.body].flatten.join('')
|
39
40
|
end
|
40
41
|
|
41
42
|
def data
|
@@ -51,7 +52,7 @@ class RestWorld
|
|
51
52
|
end
|
52
53
|
|
53
54
|
def header
|
54
|
-
@resp.
|
55
|
+
@resp.headers || {}
|
55
56
|
end
|
56
57
|
|
57
58
|
def content_type
|
@@ -93,10 +94,10 @@ class RestWorld
|
|
93
94
|
end
|
94
95
|
|
95
96
|
def explain
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
97
|
+
log "request ==> #{@_verb} #{@_request}"
|
98
|
+
log "cookies ==> #{cookies.join('; ')}" if cookies.length > 0
|
99
|
+
log "body ==> #{@_body}" if @_body
|
100
|
+
log "response ==> #{@_response.inspect}"
|
100
101
|
end
|
101
102
|
|
102
103
|
def before_parse_path(path); end
|
@@ -217,7 +218,7 @@ class RestWorld
|
|
217
218
|
@_response = Response.new(raw_response)
|
218
219
|
# add cookies to the cookie jar.
|
219
220
|
#unless @_current_user=="guest"
|
220
|
-
if cookie = @_response.header["Set-Cookie"]
|
221
|
+
if cookie = @_response.header["Set-Cookie"] || @_response.header["set-cookie"]
|
221
222
|
parts = cookie.split(';')
|
222
223
|
cookies << parts[0].strip
|
223
224
|
end
|
@@ -72,16 +72,16 @@ module Blix::Rest
|
|
72
72
|
def format_response(value, response)
|
73
73
|
if value.is_a?(RawJsonString)
|
74
74
|
response.content = if _options[:nodata]
|
75
|
-
value.to_s
|
75
|
+
[value.to_s]
|
76
76
|
else
|
77
|
-
"{\"data\":#{value}}"
|
77
|
+
["{\"data\":#{value}}"]
|
78
78
|
end
|
79
79
|
else
|
80
80
|
begin
|
81
81
|
response.content = if _options[:nodata]
|
82
|
-
MultiJson.dump(value)
|
82
|
+
[MultiJson.dump(value)]
|
83
83
|
else
|
84
|
-
MultiJson.dump('data' => value)
|
84
|
+
[MultiJson.dump('data' => value)]
|
85
85
|
end
|
86
86
|
rescue Exception => e
|
87
87
|
::Blix::Rest.logger << e.to_s
|
@@ -107,7 +107,8 @@ module Blix::Rest
|
|
107
107
|
end
|
108
108
|
|
109
109
|
def format_response(value, response)
|
110
|
-
|
110
|
+
value = [value.to_s] unless value.respond_to?(:each) || value.respond_to?(:call)
|
111
|
+
response.content = value
|
111
112
|
end
|
112
113
|
|
113
114
|
end
|
@@ -121,8 +122,8 @@ module Blix::Rest
|
|
121
122
|
|
122
123
|
def set_default_headers(headers)
|
123
124
|
headers[CACHE_CONTROL] = CACHE_NO_STORE
|
124
|
-
headers[PRAGMA]
|
125
|
-
headers[CONTENT_TYPE]
|
125
|
+
headers[PRAGMA] = NO_CACHE
|
126
|
+
headers[CONTENT_TYPE] = CONTENT_TYPE_XML
|
126
127
|
end
|
127
128
|
|
128
129
|
def format_error(message)
|
@@ -130,7 +131,7 @@ module Blix::Rest
|
|
130
131
|
end
|
131
132
|
|
132
133
|
def format_response(value, response)
|
133
|
-
response.content = value.to_s
|
134
|
+
response.content = [value.to_s]
|
134
135
|
end
|
135
136
|
|
136
137
|
end
|
@@ -144,8 +145,14 @@ module Blix::Rest
|
|
144
145
|
|
145
146
|
def set_default_headers(headers)
|
146
147
|
headers[CACHE_CONTROL] = CACHE_NO_STORE
|
147
|
-
headers[PRAGMA]
|
148
|
-
headers[CONTENT_TYPE]
|
148
|
+
headers[PRAGMA] = NO_CACHE
|
149
|
+
headers[CONTENT_TYPE] = CONTENT_TYPE_HTML
|
150
|
+
# headers['X-Frame-Options'] = 'SAMEORIGIN'
|
151
|
+
# headers['X-XSS-Protection'] = '1; mode=block'
|
152
|
+
# headers['X-Content-Type-Options'] = 'nosniff'
|
153
|
+
# headers['X-Download-Options'] = 'noopen'
|
154
|
+
# headers['X-Permitted-Cross-Domain-Policies'] = 'none'
|
155
|
+
# headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'
|
149
156
|
end
|
150
157
|
|
151
158
|
def format_error(message)
|
@@ -160,7 +167,7 @@ module Blix::Rest
|
|
160
167
|
end
|
161
168
|
|
162
169
|
def format_response(value, response)
|
163
|
-
response.content = value.to_s
|
170
|
+
response.content = [value.to_s]
|
164
171
|
end
|
165
172
|
|
166
173
|
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'redis'
|
4
|
+
require_relative 'cache'
|
5
|
+
require_relative 'string_hash'
|
6
|
+
#
|
7
|
+
# options:
|
8
|
+
# :expire_secs - how long store should save data.
|
9
|
+
# :reset_expire_on_get - start the expire timer again on read.
|
10
|
+
|
11
|
+
module Blix
|
12
|
+
module Rest
|
13
|
+
class RedisCache < Cache
|
14
|
+
|
15
|
+
STORE_PREFIX = 'blixcache'
|
16
|
+
|
17
|
+
#---------------------------------------------------------------------------
|
18
|
+
|
19
|
+
# clear all data from the cache
|
20
|
+
def clear
|
21
|
+
reset
|
22
|
+
end
|
23
|
+
|
24
|
+
# retrieve data from the cache
|
25
|
+
def get(id)
|
26
|
+
key = _key(id)
|
27
|
+
str = redis.get(key)
|
28
|
+
data = str && begin
|
29
|
+
_decode(str)
|
30
|
+
rescue StandardError
|
31
|
+
redis.del( key)
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
redis.expire(key, _opts[:expire_secs]) if data && _opts[:reset_expire_on_get] && _opts.key?(:expire_secs)
|
35
|
+
data
|
36
|
+
end
|
37
|
+
|
38
|
+
# set data in the cache
|
39
|
+
def set(id, data)
|
40
|
+
params = {}
|
41
|
+
params[:ex] = _opts[:expire_secs] if _opts.key?(:expire_secs)
|
42
|
+
redis.set(_key(id), _encode(data), **params)
|
43
|
+
data
|
44
|
+
end
|
45
|
+
|
46
|
+
# is key present in the cache
|
47
|
+
def key?(id)
|
48
|
+
redis.get(_key(id)) != nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def delete(id)
|
52
|
+
redis.del(_key(id)) > 0
|
53
|
+
end
|
54
|
+
|
55
|
+
#---------------------------------------------------------------------------
|
56
|
+
|
57
|
+
def _key(name)
|
58
|
+
_prefix + name
|
59
|
+
end
|
60
|
+
|
61
|
+
def _opts
|
62
|
+
@_opts ||= begin
|
63
|
+
o = ::Blix::Rest::StringHash.new
|
64
|
+
o[:prefix] = STORE_PREFIX
|
65
|
+
o.merge!(params)
|
66
|
+
o
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def _prefix
|
71
|
+
@_prefix ||= _opts[:prefix]
|
72
|
+
end
|
73
|
+
|
74
|
+
# remove all sessions from the store
|
75
|
+
def reset(name = nil)
|
76
|
+
keys = _all_keys(name)
|
77
|
+
redis.del(*keys) unless keys.empty?
|
78
|
+
end
|
79
|
+
|
80
|
+
def _all_keys(name = nil)
|
81
|
+
redis.keys("#{_prefix}#{name}*") || []
|
82
|
+
end
|
83
|
+
|
84
|
+
# the redis session store
|
85
|
+
def redis
|
86
|
+
@redis ||= begin
|
87
|
+
r = Redis.new
|
88
|
+
begin
|
89
|
+
r.ping
|
90
|
+
rescue Exception => e
|
91
|
+
Blix::Rest.logger.error "cannot reach redis server:#{e}"
|
92
|
+
raise
|
93
|
+
end
|
94
|
+
r
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# the number of sessions in the store
|
99
|
+
def length
|
100
|
+
_all_keys.length
|
101
|
+
end
|
102
|
+
|
103
|
+
# delete expired sessions from the store. this should be handled
|
104
|
+
# automatically by redis if the ttl is set on save correctly
|
105
|
+
def cleanup(opts = nil); end
|
106
|
+
|
107
|
+
def _encode(data)
|
108
|
+
Marshal.dump(data)
|
109
|
+
end
|
110
|
+
|
111
|
+
def _decode(msg)
|
112
|
+
Marshal.load(msg)
|
113
|
+
end
|
114
|
+
|
115
|
+
def get_expiry_time
|
116
|
+
if expire = _opts[:expire_secs] || _opts['expire_secs']
|
117
|
+
Time.now - expire
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def get_expiry_secs
|
122
|
+
_opts[:expire_secs] || _opts['expire_secs']
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|