garner 0.1.2 → 0.1.3

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.
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