blix-rest 0.1.30 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|