garner 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -47,6 +47,22 @@ get "/me/address" do
47
47
  end
48
48
  ```
49
49
 
50
+ ETag Generation Strategies
51
+ --------------------------
52
+
53
+ The primary purpose of the ETag header is to define a short string representation of a cached object that is both (a) deterministic and (b) unique, so that Garner's `cache_or_304` method can quickly determine whether a client's cached content matches the latest server object. As such, an MD5 hash applied to *any* object serialization would suffice. However, some applications may wish to control the manner in which ETags are generated, and so Garner supports arbitrary ETag strategies.
54
+
55
+ The default strategy, `Garner::Strategies::ETags::Grape`, follows the serialization strategy used by Grape for coercing objects into JSON. Using this strategy, Garner will generate an ETag for each cache object that is identical to what `Rack::ETag` would return if that object was returned by Grape. This property could be useful for Grape applications.
56
+
57
+ Another, simpler strategy, `Garner::Strategies::ETags::Marshal`, simply applies an MD5 hash to `Marshal.dump(object)`. This strategy might be more applicable for applications not using Grape.
58
+
59
+ An ETag strategy may be defined at application startup time:
60
+
61
+ ```
62
+ ETAG_STRATEGY = Garner::Strategies::ETags::Grape
63
+ ```
64
+
65
+
50
66
  Binding Strategies
51
67
  ------------------
52
68
 
@@ -117,6 +133,15 @@ Garner::Cache::ObjectIdentity::KEY_STRATEGIES = [
117
133
 
118
134
  This method of registration does need improvement, please contribute.
119
135
 
136
+ Available Key Strategies
137
+ ------------------------
138
+
139
+ * `Garner::Strategies::Keys::Caller` inserts the calling file and line number, allowing multiple calls from the same function to generate different keys.
140
+ * `Garner::Strategies::Keys::Version` inserts the output of a `version` method, when available, primarily targeted at API implementations.
141
+ * `Garner::Strategies::Keys::Key` inserts the value of `:key` within the requested context, useful to explicitly declare an element of a cache key.
142
+ * `Garner::Strategies::Keys::RequestGet` inserts the value of HTTP request's GET parameters into the cache key when `:request` is present in the context.
143
+ * `Garner::Strategies::Keys::RequestPath` inserts the value of the HTTP request's path into the cache key when `:request` is present in the context.
144
+
120
145
  Configuration
121
146
  -------------
122
147
 
@@ -44,6 +44,8 @@ module Garner
44
44
  CACHE_STRATEGIES = [
45
45
  Garner::Strategies::Cache::Expiration
46
46
  ]
47
+
48
+ ETAG_STRATEGY = Garner::Strategies::ETags::Grape
47
49
 
48
50
  class << self
49
51
 
@@ -107,7 +109,7 @@ module Garner
107
109
  def reset_cache_metadata(key, object)
108
110
  return unless object
109
111
  metadata = {
110
- :etag => Garner::Objects::ETag.from(object),
112
+ :etag => ETAG_STRATEGY.apply(object),
111
113
  :last_modified => Time.now
112
114
  }
113
115
  meta_key = meta(key)
data/lib/garner/config.rb CHANGED
@@ -9,7 +9,7 @@ module Garner
9
9
  # config.cache = Rails.cache
10
10
  # end
11
11
  #
12
- # @return [ Config ] The configuration obejct.
12
+ # @return [ Config ] The configuration object.
13
13
  def configure
14
14
  block_given? ? yield(Garner::Config) : Garner::Config
15
15
  end
@@ -56,6 +56,7 @@ module Garner
56
56
  cache_context = {}
57
57
  cache_context.merge!(options.dup)
58
58
  cache_context[:request] = request
59
+ cache_context[:version] = version if self.respond_to?(:version) && version
59
60
  cache_context.delete(:bind)
60
61
  cache_binding = (options || {})[:bind]
61
62
  cache_binding = cache_binding ? { :bind => cache_binding } : {}
@@ -1,7 +1,7 @@
1
1
  module Garner
2
2
  module Strategies
3
3
  module Cache
4
- # Injects an expires_in value from the globl configuration.
4
+ # Injects an expires_in value from the global configuration.
5
5
  module Expiration
6
6
  class << self
7
7
  def apply(current, options = {})
@@ -0,0 +1,32 @@
1
+ module Garner
2
+ module Strategies
3
+ module ETags
4
+ module Grape
5
+ class << self
6
+ # @abstract
7
+ # Serialize in a way such that the ETag matches that which would
8
+ # be returned by Grape + Rack::ETag at response time.
9
+ def apply(object)
10
+ serialization = encode_json(object || "")
11
+ %("#{Digest::MD5.hexdigest(serialization)}")
12
+ end
13
+
14
+ # See https://github.com/intridea/grape/blob/master/lib/grape/middleware/base.rb
15
+ def encode_json(object)
16
+ return object if object.is_a?(String)
17
+
18
+ if object.respond_to? :serializable_hash
19
+ MultiJson.dump(object.serializable_hash)
20
+ elsif object.kind_of?(Array) && !object.map {|o| o.respond_to? :serializable_hash }.include?(false)
21
+ MultiJson.dump(object.map {|o| o.serializable_hash })
22
+ elsif object.respond_to? :to_json
23
+ object.to_json
24
+ else
25
+ MultiJson.dump(object)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ module Garner
2
+ module Strategies
3
+ module ETags
4
+ module Marshal
5
+ class << self
6
+ # @abstract
7
+ # Serialize using Ruby's Marshal.dump.
8
+ def apply(object)
9
+ serialization = ::Marshal.dump(object || "")
10
+ %("#{Digest::MD5.hexdigest(serialization)}")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -11,11 +11,11 @@ module Garner
11
11
 
12
12
  def apply(key, context = {})
13
13
  rc = key ? key.dup : {}
14
- clr = nil
15
14
  caller.each do |line|
16
15
  split = line.split(":")
17
16
  next unless split.length >= 2
18
17
  path = Pathname.new(split[0]).realpath.to_s
18
+ next if path.include?("lib/garner")
19
19
  next unless path.include?("/app/") || path.include?("/spec/")
20
20
  rc[field] = "#{path}:#{split[1]}"
21
21
  break
@@ -0,0 +1,24 @@
1
+ module Garner
2
+ module Strategies
3
+ module Keys
4
+ # Strips JSONP parameters from the key.
5
+ module Jsonp
6
+ class << self
7
+
8
+ def field
9
+ :params
10
+ end
11
+
12
+ def apply(key, context = {})
13
+ key = key ? key.dup : {}
14
+ return unless key[field]
15
+ key[field].delete("callback")
16
+ key[field].delete("_")
17
+ key
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ module Garner
2
+ module Strategies
3
+ module Keys
4
+ # Inject a key into the cache key.
5
+ module Key
6
+ class << self
7
+
8
+ def field
9
+ :key
10
+ end
11
+
12
+ def apply(key, context = {})
13
+ key = key ? key.dup : {}
14
+ key[field] = context[:key] if context && context.has_key?(:key)
15
+ key
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,5 +1,3 @@
1
1
  module Garner
2
- VERSION = '0.1.2'
2
+ VERSION = '0.1.3'
3
3
  end
4
-
5
-
data/lib/garner.rb CHANGED
@@ -3,8 +3,6 @@ require 'active_support'
3
3
  # garner
4
4
  require 'garner/version'
5
5
  require 'garner/config'
6
- # objects
7
- require 'garner/objects/etag'
8
6
  # middleware
9
7
  require 'garner/middleware/base'
10
8
  require 'garner/middleware/cache/bust'
@@ -13,10 +11,15 @@ require 'garner/strategies/keys/version_strategy'
13
11
  require 'garner/strategies/keys/caller_strategy'
14
12
  require 'garner/strategies/keys/request_path_strategy'
15
13
  require 'garner/strategies/keys/request_get_strategy'
14
+ require 'garner/strategies/keys/key_strategy'
15
+ require 'garner/strategies/keys/jsonp_strategy'
16
+ # etag strategies
17
+ require 'garner/strategies/etags/grape_strategy'
18
+ require 'garner/strategies/etags/marshal_strategy'
16
19
  # cache option strategies
17
20
  require 'garner/strategies/cache/expiration_strategy'
18
21
  # caches
19
22
  require 'garner/cache/object_identity'
20
23
  # mixins
21
- require 'garner/mixins/grape_cache'
22
- require 'garner/mixins/mongoid_document'
24
+ require 'garner/mixins/grape_cache' if defined?(Grape)
25
+ require 'garner/mixins/mongoid_document' if defined?(Mongoid)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: garner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-06-25 00:00:00.000000000 Z
13
+ date: 2012-07-18 00:00:00.000000000Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
17
- requirement: &70317016435700 !ruby/object:Gem::Requirement
17
+ requirement: &70261714866040 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: '0'
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70317016435700
25
+ version_requirements: *70261714866040
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: json
28
- requirement: &70317016434120 !ruby/object:Gem::Requirement
28
+ requirement: &70261714865560 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,21 +33,21 @@ dependencies:
33
33
  version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *70317016434120
36
+ version_requirements: *70261714865560
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: multi_json
39
- requirement: &70317016432160 !ruby/object:Gem::Requirement
39
+ requirement: &70261714865080 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
43
43
  - !ruby/object:Gem::Version
44
- version: '0'
44
+ version: 1.3.0
45
45
  type: :runtime
46
46
  prerelease: false
47
- version_requirements: *70317016432160
47
+ version_requirements: *70261714865080
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: activesupport
50
- requirement: &70317016430400 !ruby/object:Gem::Requirement
50
+ requirement: &70261714864600 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ! '>='
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: '0'
56
56
  type: :runtime
57
57
  prerelease: false
58
- version_requirements: *70317016430400
58
+ version_requirements: *70261714864600
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: bundler
61
- requirement: &70317016428340 !ruby/object:Gem::Requirement
61
+ requirement: &70261714864120 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ! '>='
@@ -66,10 +66,10 @@ dependencies:
66
66
  version: '0'
67
67
  type: :development
68
68
  prerelease: false
69
- version_requirements: *70317016428340
69
+ version_requirements: *70261714864120
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: grape
72
- requirement: &70317016441760 !ruby/object:Gem::Requirement
72
+ requirement: &70261714863640 !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
75
  - - =
@@ -77,10 +77,10 @@ dependencies:
77
77
  version: 0.2.0
78
78
  type: :development
79
79
  prerelease: false
80
- version_requirements: *70317016441760
80
+ version_requirements: *70261714863640
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: rack-test
83
- requirement: &70317016438060 !ruby/object:Gem::Requirement
83
+ requirement: &70261714863160 !ruby/object:Gem::Requirement
84
84
  none: false
85
85
  requirements:
86
86
  - - =
@@ -88,10 +88,10 @@ dependencies:
88
88
  version: 0.6.1
89
89
  type: :development
90
90
  prerelease: false
91
- version_requirements: *70317016438060
91
+ version_requirements: *70261714863160
92
92
  - !ruby/object:Gem::Dependency
93
93
  name: rspec
94
- requirement: &70317016452220 !ruby/object:Gem::Requirement
94
+ requirement: &70261714862680 !ruby/object:Gem::Requirement
95
95
  none: false
96
96
  requirements:
97
97
  - - ~>
@@ -99,10 +99,10 @@ dependencies:
99
99
  version: '2.10'
100
100
  type: :development
101
101
  prerelease: false
102
- version_requirements: *70317016452220
102
+ version_requirements: *70261714862680
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: jeweler
105
- requirement: &70317016480360 !ruby/object:Gem::Requirement
105
+ requirement: &70261714878560 !ruby/object:Gem::Requirement
106
106
  none: false
107
107
  requirements:
108
108
  - - =
@@ -110,10 +110,10 @@ dependencies:
110
110
  version: 1.8.3
111
111
  type: :development
112
112
  prerelease: false
113
- version_requirements: *70317016480360
113
+ version_requirements: *70261714878560
114
114
  - !ruby/object:Gem::Dependency
115
115
  name: mongoid
116
- requirement: &70317016476340 !ruby/object:Gem::Requirement
116
+ requirement: &70261714878080 !ruby/object:Gem::Requirement
117
117
  none: false
118
118
  requirements:
119
119
  - - ~>
@@ -121,10 +121,10 @@ dependencies:
121
121
  version: '2.4'
122
122
  type: :development
123
123
  prerelease: false
124
- version_requirements: *70317016476340
124
+ version_requirements: *70261714878080
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: bson_ext
127
- requirement: &70317020028420 !ruby/object:Gem::Requirement
127
+ requirement: &70261714877600 !ruby/object:Gem::Requirement
128
128
  none: false
129
129
  requirements:
130
130
  - - ~>
@@ -132,10 +132,10 @@ dependencies:
132
132
  version: '1.6'
133
133
  type: :development
134
134
  prerelease: false
135
- version_requirements: *70317020028420
135
+ version_requirements: *70261714877600
136
136
  - !ruby/object:Gem::Dependency
137
137
  name: yard
138
- requirement: &70317020022760 !ruby/object:Gem::Requirement
138
+ requirement: &70261714877120 !ruby/object:Gem::Requirement
139
139
  none: false
140
140
  requirements:
141
141
  - - ! '>='
@@ -143,10 +143,10 @@ dependencies:
143
143
  version: '0'
144
144
  type: :development
145
145
  prerelease: false
146
- version_requirements: *70317020022760
146
+ version_requirements: *70261714877120
147
147
  - !ruby/object:Gem::Dependency
148
148
  name: redcarpet
149
- requirement: &70317020102160 !ruby/object:Gem::Requirement
149
+ requirement: &70261714876640 !ruby/object:Gem::Requirement
150
150
  none: false
151
151
  requirements:
152
152
  - - ! '>='
@@ -154,10 +154,10 @@ dependencies:
154
154
  version: '0'
155
155
  type: :development
156
156
  prerelease: false
157
- version_requirements: *70317020102160
157
+ version_requirements: *70261714876640
158
158
  - !ruby/object:Gem::Dependency
159
159
  name: github-markup
160
- requirement: &70317020288700 !ruby/object:Gem::Requirement
160
+ requirement: &70261714876160 !ruby/object:Gem::Requirement
161
161
  none: false
162
162
  requirements:
163
163
  - - ! '>='
@@ -165,7 +165,7 @@ dependencies:
165
165
  version: '0'
166
166
  type: :development
167
167
  prerelease: false
168
- version_requirements: *70317020288700
168
+ version_requirements: *70261714876160
169
169
  description: Garner is a set of Rack middleware and cache helpers that implement various
170
170
  strategies.
171
171
  email: dblock@dblock.org
@@ -182,16 +182,19 @@ files:
182
182
  - lib/garner/middleware/cache/bust.rb
183
183
  - lib/garner/mixins/grape_cache.rb
184
184
  - lib/garner/mixins/mongoid_document.rb
185
- - lib/garner/objects/etag.rb
186
185
  - lib/garner/strategies/cache/expiration_strategy.rb
186
+ - lib/garner/strategies/etags/grape_strategy.rb
187
+ - lib/garner/strategies/etags/marshal_strategy.rb
187
188
  - lib/garner/strategies/keys/caller_strategy.rb
189
+ - lib/garner/strategies/keys/jsonp_strategy.rb
190
+ - lib/garner/strategies/keys/key_strategy.rb
188
191
  - lib/garner/strategies/keys/request_get_strategy.rb
189
192
  - lib/garner/strategies/keys/request_path_strategy.rb
190
193
  - lib/garner/strategies/keys/version_strategy.rb
191
194
  - lib/garner/version.rb
192
195
  - LICENSE.md
193
196
  - README.md
194
- homepage: http://github.com/dblock/garner
197
+ homepage: http://github.com/artsy/garner
195
198
  licenses:
196
199
  - MIT
197
200
  post_install_message:
@@ -206,7 +209,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
206
209
  version: '0'
207
210
  segments:
208
211
  - 0
209
- hash: 1217542249029147985
212
+ hash: -3554335930431109647
210
213
  required_rubygems_version: !ruby/object:Gem::Requirement
211
214
  none: false
212
215
  requirements:
@@ -1,20 +0,0 @@
1
- module Garner
2
- module Objects
3
- module ETag
4
- class << self
5
- # @abstract
6
- # Serialize in a way such that the ETag matches that which would
7
- # be returned by Rack::ETag at response time.
8
- def from(object)
9
- serialization = case object
10
- when nil then ""
11
- when String then object
12
- when Hash then object.respond_to?(:to_json) ? object.to_json : MultiJson.dump(object)
13
- else Marshal.dump(object)
14
- end
15
- %("#{Digest::MD5.hexdigest(serialization)}")
16
- end
17
- end
18
- end
19
- end
20
- end