-
Notifications
You must be signed in to change notification settings - Fork 265
/
caching.rb
169 lines (143 loc) · 4.41 KB
/
caching.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
require 'dalli'
require 'forwardable'
require 'faraday_middleware/addressable_patch'
require 'digest/md5'
require 'connection_pool'
class Huboard
class Client
class ConnectionPool
def self.options
@options ||= {
namespace: "huboard_v1",
compress: true,
expires_in: 1.day,
username: HuboardApplication.cache_config[:username],
password: HuboardApplication.cache_config[:password]
}
end
def self.connection_pool
@connection_pool = ::ConnectionPool.new(:size => 20, :timeout => 10) {
Dalli::Client.new(HuboardApplication.cache_config[:servers].split(","), options)
}
end
end
class SimpleCache
def initialize(app, options)
@options = options
@app = app
end
def cache
@dalli ||= ConnectionPool.connection_pool
end
def clear(key)
cache.with do |dalli|
dalli.delete(key)
end
end
def read(key, app, env)
if cached = get(key)
cached = Marshal.load(cached)
etag = cached.headers[:etag]
env[:request_headers].merge!('If-None-Match' => etag)
response = app.call(env)
if response.status == 304
return cached
elsif response.status == 200
write(key, response)
return response
end
response
else
data = app.call(env)
if data.status == 200
write(key, data)
end
data
end
end
def get(key)
cache.with do |dalli|
dalli.get(key)
end
end
def write(key, data)
cache.with do |dalli|
dalli.set(key, Marshal.dump(data))
data
end
end
def fetch(key, app, env)
read(key, app, env)
end
end
class Caching < Faraday::Middleware
attr_reader :cache
extend Forwardable
def_delegators :'Faraday::Utils', :parse_query, :build_query
# Public: initialize the middleware.
#
# cache - An object that responds to read, write and fetch (default: nil).
# options - An options Hash (default: {}):
# :ignore_params - String name or Array names of query params
# that should be ignored when forming the cache
# key (default: []).
#
# Yields if no cache is given. The block should return a cache object.
def initialize(app, options = {:namespace => "huboard_v1", :compress => true, :expires_in => 1.day,
:username => HuboardApplication.cache_config[:username], :password => HuboardApplication.cache_config[:password]})
super(app)
@cache = SimpleCache.new app, options
@options = options
end
def call(env)
if :get == env[:method]
if env[:parallel_manager]
# callback mode
cache_on_complete(env)
else
# synchronous mode
response = cache.fetch(cache_key(env), @app, env)
finalize_response(response, env)
end
elsif :patch == env[:method] || :post == env[:method] || :delete == env[:method]
cache.clear(cache_key(env))
@app.call(env)
else
@app.call(env)
end
end
def cache_key(env)
url = env[:url].dup
if url.query && params_to_ignore.any?
params = parse_query url.query
params.reject! {|k,| params_to_ignore.include? k }
url.query = build_query params
end
url.normalize!
Digest::MD5.hexdigest(url.request_uri)
end
def params_to_ignore
@params_to_ignore ||= Array(@options[:ignore_params]).map { |p| p.to_s }
end
def cache_on_complete(env)
key = cache_key(env)
if cached_response = cache.read(key, @app, env)
finalize_response(cached_response, env)
else
response = @app.call(env)
response.on_complete { cache.write(key, response) }
end
end
def finalize_response(response, env)
response = response.dup if response.frozen?
env[:response] = response
unless env[:response_headers]
env.update response.env
# FIXME: omg hax
response.instance_variable_set('@env', env)
end
response
end
end
end
end