hitnmiss 2.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 16113a952ba4f6e13da3988202b5293f7b29883e
4
+ data.tar.gz: e47681d3d3de77dc028ace05d8ca89c6c44d4cc0
5
+ SHA512:
6
+ metadata.gz: 063cac92539361cd38cbf1cf6defd69d83ab9406c5e0f65e3639b7ad708f3ecee452cd6b2313383c6e0658baaaec4cf71ffc9eeddd79d61ec2fa65c0d4cb2828
7
+ data.tar.gz: f2495525eb2431776aaf631766e61d17f0ff712a7e98b9e9ad71c216eedeb8df4f542a76c14ccb24b203af5b8d3bbd702782fc1ee0e843e0a65630570e011303
@@ -0,0 +1,9 @@
1
+ ---
2
+ engines:
3
+ fixme:
4
+ enabled: true
5
+ rubcop:
6
+ enabled: true
7
+ ratings:
8
+ paths:
9
+ - "lib/**/*"
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.11.2
@@ -0,0 +1,69 @@
1
+ # ChangeLog
2
+
3
+ The following are lists of the notable changes included with each release.
4
+ This is intended to help keep people informed about notable changes between
5
+ versions, as well as provide a rough history.
6
+
7
+ #### Next Release
8
+
9
+ #### v2.1.0
10
+
11
+ * Changed: Documentation for open sourcing
12
+
13
+ #### v2.0.0
14
+
15
+ * Added: `BackgroundRefreshRepository` module
16
+ * Changed: Extract cache management into separate module
17
+ * Added: fingerprint, last\_modified, and updated\_at
18
+ * Changed: driver interface to receive a `Hitnmiss::Entity`
19
+ * Fixed: Correct typo in UnregisteredDriver
20
+ * Changed: Extract key generation concerns into separate module
21
+ * Changed: Extract `Fetcher` interface into separate module
22
+
23
+ #### v1.0.0
24
+
25
+ * Changed version to 1.0 since the api is stable
26
+
27
+ #### v0.4.0
28
+
29
+ * Changed `InMemoryDriver` to return `Hitnmiss::Driver::Hit` and
30
+ `Hitnmiss::Driver::Miss` instances.
31
+ * Changed driver interface to require `<#driver instance>.get` method to return a
32
+ `Hitnmiss::Driver::Hit` instance or a `Hitnmiss::Driver::Miss` instance
33
+ * Added `Hitnmiss::Driver::Miss` class
34
+ * Added `Hitnmiss::Driver::Hit` class
35
+ * Changed private methods `get`, `get_all` to `fetch` & `fetch_all`
36
+ * Changed public API method `fetch(*args)` to `get(*args)`
37
+
38
+ #### v0.3.0
39
+
40
+ * Changed class style interface to object style interface
41
+ * Fixed `InMemoryDriver#all` to return values not hash of value & expiration
42
+ * Added return values to Public API Documentation examples
43
+
44
+ #### v0.2.0
45
+
46
+ * Changed `Repository.prime_cache` to `Repository.prime`
47
+ * Added `Repository.prime_all` to prime entire repository
48
+ * Changed `Repository.perform` to `Repository.get`
49
+ * Added `Repository.get_all` for `Repository.prime_all`
50
+ * Added `Repository.all` to get all cached values
51
+ * Added `Repository.delete` to delete a cached value
52
+ * Added `Repository.clear` to clear all cached values
53
+ * Added `Driver#all` interface to get all cached values
54
+ * Added `Driver#delete` interface to delete a cached value
55
+ * Added `Driver#clear` interface to clear all cached values
56
+ * Changed `InMemoryDriver` to implement `#all`, `#delete`, and `#clear`
57
+
58
+ #### v0.1.2
59
+
60
+ * Fixed fetching cached boolean `false` value
61
+
62
+ #### v0.1.1
63
+
64
+ * Changed the InMemoryDriver to be threadsafe
65
+
66
+ #### v0.1.0
67
+
68
+ * Added driver registry to centrally manage instantiated drivers
69
+ * Added initial Minimum Viable Product version of the library
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at oss@acorns.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
@@ -0,0 +1,56 @@
1
+ # Where should I start?
2
+
3
+ We recommend first starting to learn about Hitnmiss by reading the
4
+ [README](https://github.com/Acornsgrow/hitnmiss/blob/master/README.md). Beyond
5
+ that you can explore the sections below to identify the path that best fits you.
6
+ **Note:** This project follows a contribution [code of
7
+ conduct](https://github.com/Acornsgrow/hitnmiss/blob/master/CODE_OF_CONDUCT.md).
8
+
9
+ ## How can I help?
10
+
11
+ There are a number of ways you can help.
12
+
13
+ - Report Bugs
14
+ - Add Feature Requests
15
+ - Add/Update Documentation
16
+ - Triage Bugs
17
+ - Code Contributions
18
+
19
+ ### Report Bugs
20
+
21
+ As a user a relatively easy way to contribute is to report bugs that you run
22
+ into. Generally, this should be done via our
23
+ [ISSUES](https://github.com/Acornsgrow/hitnmiss/issues) page. However, if it is
24
+ a security issue please send an e-mail directly to one of the core authors found
25
+ in the
26
+ [gemspec](https://github.com/Acornsgrow/hitnmiss/blob/master/hitnmiss.gemspec#L10)
27
+ rather than creating a public issue for it. This will give us some time to
28
+ review it and resolve it before people can abuse the security flaw.
29
+
30
+ ### Add Feature Requests
31
+
32
+ One simple way to get involved after starting to use the project is to
33
+ contribute by adding feature requests for things that you see as gaps for the
34
+ project. This can be done by creating an issue or our
35
+ [ISSUES](https://github.com/Acornsgrow/hitnmiss/issues) page.
36
+
37
+ ### Add/Update Documentation
38
+
39
+ You can also contribute by correcting, updating, or adding additional
40
+ documentation. This can be done by making a pull request for documentation in
41
+ any of our top level markdown files.
42
+
43
+ ### Triage Bugs
44
+
45
+ Triaging bugs is also very important. This is the practice of the reviewing
46
+ [ISSUES](https://github.com/Acornsgrow/hitnmiss/issues), making sure that the
47
+ submitter provided necessary information to review the bugs, and classify them
48
+ appropriately.
49
+
50
+ ### Code Contributions
51
+
52
+ The most involved type of contribution you can make is to actually submit code
53
+ to implement features, fix bugs, etc. This is done by submitting a pull request.
54
+ See
55
+ [DEVELOPING](https://github.com/Acornsgrow/hitnmiss/blob/master/DEVELOPMENT.md)
56
+ for further details on getting started with a code contribution.
@@ -0,0 +1,7 @@
1
+ # Development
2
+
3
+ After checking out the repo, run `bin/setup` to install dependencies.
4
+ Then, run `rake spec` to run the tests. You can also run `bin/console`
5
+ for an interactive prompt that will allow you to experiment.
6
+
7
+ To install this gem onto your local machine, run `bundle exec rake install`.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hitnmiss.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Acorns Grow Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,397 @@
1
+ [![Build Status](https://travis-ci.com/Acornsgrow/hitnmiss.svg?token=GGEgqzL4zt7sa3zVgspU&branch=master)](https://travis-ci.com/Acornsgrow/hitnmiss)
2
+ [![Code Climate](https://codeclimate.com/repos/567a3c30bd3f3b63510017dd/badges/e979a32e79ec12d35896/gpa.svg)](https://codeclimate.com/repos/567a3c30bd3f3b63510017dd/feed)
3
+ [![Test Coverage](https://codeclimate.com/repos/567a3c30bd3f3b63510017dd/badges/e979a32e79ec12d35896/coverage.svg)](https://codeclimate.com/repos/567a3c30bd3f3b63510017dd/coverage)
4
+ [![Issue Count](https://codeclimate.com/repos/567a3c30bd3f3b63510017dd/badges/e979a32e79ec12d35896/issue_count.svg)](https://codeclimate.com/repos/567a3c30bd3f3b63510017dd/feed)
5
+
6
+ # Hitnmiss
7
+
8
+ Hitnmiss is a Ruby gem that provides support for using the Repository
9
+ pattern for read-through, write-behind caching in a thread-safe way. It
10
+ is built heavily around using POROs (Plain Old Ruby Objects). This means
11
+ it is intended to be used with all kinds of Ruby applications from plain
12
+ Ruby command line tools, to framework (Rails, Sinatra, Rack, Hanami,
13
+ etc.) based web apps, etc.
14
+
15
+ Having defined repositories for accessing your caches aids in a number of ways.
16
+ First, it centralizes cache key generation. Secondly, it centralizes &
17
+ standardizes access to the cache rather than having code spread across your app
18
+ duplicating key generation and access. Third, it provides clean separation
19
+ between the cache persistence layer and the business representation.
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ ```ruby
26
+ gem 'hitnmiss'
27
+ ```
28
+
29
+ And then execute:
30
+
31
+ $ bundle
32
+
33
+ Or install it yourself as:
34
+
35
+ $ gem install hitnmiss
36
+
37
+ ## Define/Mixin a Repository
38
+
39
+ Before you can use Hitnmiss to manage cache you first need to define a
40
+ cache repository. This is done by defining a class for your repository
41
+ and mixing the appropriate repository module in using `include`.
42
+
43
+ Below, are explanations of the **Standard Repository** and the **Background
44
+ Refresh Repository** so that you can decide which one fits your needs as well as
45
+ learn how to use them.
46
+
47
+ ### Standard Repository
48
+
49
+ The standard repository module, `Hitnmiss::Repository`, fits the most common
50
+ caching use case, the "Expiration Model". The "Expiration Model" is a caching
51
+ model where a value gets cached with an associated expiration and when that
52
+ expiration is reached that value is no longer cached. This affects the app
53
+ behavior by having it pay the caching cost when it tries to get a value and it
54
+ has expired. The following is an example of creating a `MostRecentPrice` cache
55
+ repository for the "Expiration Model".
56
+
57
+ ```ruby
58
+ class MostRecentPrice
59
+ include Hitnmiss::Repository
60
+
61
+ default_expiration 134 # expiration in seconds from when value cached
62
+ end
63
+ ```
64
+
65
+ More often than not your caching use case will have a static/known
66
+ expiration that you want to use for all values that get cached. This is handled
67
+ for you by setting the `default_expiration` as seen in the example above.
68
+ **Note:** Hitnmiss also supports being able to have different expirations for
69
+ each cached value. You can learn more about this in the "Set Cache Source based
70
+ Expiration" section.
71
+
72
+ ### Background Refresh Repository
73
+
74
+ Sometimes you don't have an expiration value and don't want cached values to
75
+ disappear. In these scenarios you want something to update the cache for you
76
+ based on some defined interval. When you use the
77
+ `Hitnmiss::BackgroundRefreshRepository` module and set the `refresh_interval`
78
+ as seen below, it prepares your repository to handle this scenario. This changes
79
+ the behavior where the app never experiences the caching cost as it is
80
+ continually managed for the app in the background based on the
81
+ `refresh_interval`.
82
+
83
+ ```ruby
84
+ class MostRecentPrice
85
+ include Hitnmiss::BackgroundRefreshRepository
86
+
87
+ refresh_interval 60*5 # refresh interval in seconds
88
+ end
89
+ ```
90
+
91
+ Once you have defined your Background Refresh Repository in order to get the
92
+ background process to update your cache you have to kick it off using the
93
+ `background_refresh(*args, swallow_exceptions: [])` method as seen in the
94
+ example below. The optional keyword argument `swallow_exceptions` defaults to
95
+ `[]`. If enabled it will prevent the specified exceptions, raised in the
96
+ `fetch(*args)` or `stale?(*args)` methods you defined, from killing the
97
+ background thread, and prevent the exceptions from making their way up to the
98
+ application. This is useful in scenarios where you want it to absorb say timeout
99
+ exceptions, etc. and continue trucking along. **Note:** Any other exceptions not
100
+ covered by the exceptions listed in the `swallow_exceptions` array will still be
101
+ raised up into the application.
102
+
103
+ ```ruby
104
+ repository = MostRecentPrice.new
105
+ repository.background_refresh(store_id)
106
+ ```
107
+
108
+ This model also has the added benefit that the priming of the cache in the
109
+ background refresh process is non-blocking. This means if you use this model the
110
+ consumer will not experience the priming of the cache like they would with the
111
+ Standard Repository's Expiration Model.
112
+
113
+ #### Staleness Checking
114
+
115
+ The Background Refresh Repository model introduces a new concept, Staleness
116
+ Checking. Staleness is checked during the background refresh process. The way it
117
+ works is if the cache is identified to be stale, then it primes the cache in the
118
+ background, if the cache is identified to NOT be stale, then it sleeps for
119
+ another `refresh_interval`.
120
+
121
+ The stale checker, `stale?(*args)`, defaults to an always stale value of `true`.
122
+ This causes the background refresh process to prime the cache every
123
+ `refresh_interval`.
124
+
125
+ If you want your cache implementation to be smarter and say validate a
126
+ fingerprint or last modified value against the source, you can do it simply by
127
+ overwriting the `stale?(*args)` method with your own staleness checking logic.
128
+ The following is an example of this.
129
+
130
+ ```ruby
131
+ class MostRecentPrice
132
+ include Hitnmiss::BackgroundRefreshRepository
133
+
134
+ refresh_interval 60*5 # refresh interval in seconds
135
+
136
+ def initialize
137
+ @client = HTTPClient.new
138
+ end
139
+
140
+ def stale?(*args)
141
+ hit_or_miss = get_from_cache(*args)
142
+ if hit_or_miss.is_a?(Hitnmiss::Driver::Miss)
143
+ return true
144
+ elsif hit_or_miss.is_a?(Hitnmiss::Driver::Hit)
145
+ url = "https://someresource.example.com/#{args[0]}/#{args[1]}/digest.json"
146
+ res = @client.head(url)
147
+ fingerprint = res.header['ETag'].first
148
+ return false if fingerprint == hit_or_miss.fingerprint
149
+ return true
150
+ else
151
+ raise Hitnmiss::Repository::UnsupportedDriverResponse.new("Driver '#{self.class.driver.inspect}' did not return an object of the support types (Hitnmiss::Driver::Hit, Hitnmiss::Driver::Miss)")
152
+ end
153
+ end
154
+ end
155
+ ```
156
+
157
+ ### Set a Repositories Cache Driver
158
+
159
+ Hitnmiss defaults to the provided `Hitnmiss::InMemoryDriver`, but if an alternate
160
+ driver is needed a new driver can be registered as seen below.
161
+
162
+ ```ruby
163
+ # config/hitnmiss.rb
164
+ Hitnmiss.register_driver(:my_driver, SomeAlternativeDriver.new)
165
+ ```
166
+
167
+ Once you have registered the new driver you can tell `Hitnmiss` what
168
+ driver to use for this particular repository. The following is an example
169
+ of how one would accomplish setting the repository driver to the driver
170
+ that was just registered.
171
+
172
+ ```ruby
173
+ class MostRecentPrice
174
+ include Hitnmiss::Repository
175
+
176
+ driver :my_driver
177
+ end
178
+ ```
179
+
180
+ This works exactly the same with the `Hitnmiss::BackgroundRefreshRepository`.
181
+
182
+ ### Define Fetcher Methods
183
+
184
+ You may be asking yourself, "How does the cache value get set?" Well,
185
+ the answer is one of two ways.
186
+
187
+ * Fetching an individual cacheable entity
188
+ * Fetching all of the repository's cacheable entities
189
+
190
+ Both of these scenarios are supported by defining the `fetch(*args)`
191
+ method or the `fetch_all(keyspace)` method respectively in your cache
192
+ repository class. See the following example.
193
+
194
+ ```ruby
195
+ class MostRecentPrice
196
+ include Hitnmiss::Repository
197
+
198
+ default_expiration 134
199
+
200
+ private
201
+
202
+ def fetch(*args)
203
+ # - do whatever to get the value you want to cache
204
+ # - construct a Hitnmiss::Entity with the value
205
+ Hitnmiss::Entity.new('some value')
206
+ end
207
+
208
+ def fetch_all(keyspace)
209
+ # - do whatever to get the values you want to cache
210
+ # - construct a collection of arguments and Hitnmiss entities
211
+ [
212
+ { args: ['a', 'b'], entity: Hitnmiss::Entity.new('some value') },
213
+ { args: ['x', 'y'], entity: Hitnmiss::Entity.new('other value') }
214
+ ]
215
+ end
216
+ end
217
+ ```
218
+
219
+ The `fetch(*args)` method is responsible for obtaining the value that you want
220
+ to cache by whatever means necessary and returning a `Hitnmiss::Entity`
221
+ containing the value, and optionally an `expiration`, `fingerprint`, and
222
+ `last_modified`. **Note:** The `*args` passed into the `fetch(*args)` method
223
+ originate as the arguments that are passed into `prime` and `get` methods when
224
+ they are called. For more context on `prime` and `get` please so [Priming an
225
+ Entity](#priming-an-entity) and [Getting a Value](#getting-a-value)
226
+ respectively. Defining the `fetch(*args)` method is **required** if you want to
227
+ be able to cache values or get cached values using the `prime` or `get` methods.
228
+
229
+ If you wish to support [priming the cache for an entire
230
+ repository](#priming-the-entire-repository) using the `prime_all` method, you
231
+ **must** define the `fetch_all(keyspace)` method on the repository class. This
232
+ method **must** return a collection of hashes describing the `args` that would
233
+ be used to get an entity and the corresponding `Hitnmiss::Entity`. See example
234
+ above.
235
+
236
+ #### Set Cache Source based Expiration
237
+
238
+ In some cases you might need to set the expiration to be a different
239
+ value for each cached value. This is generally needed when you get
240
+ information back from a remote entity in the `fetch(*args)` method and you
241
+ use it to compute the expiration. This for example could be via the `Expiration`
242
+ header in an HTTP response. Once you have the expiration for that
243
+ value you can set it by passing the expiration into the
244
+ `Hitnmiss::Entity` constructor as seen below. **Note:** The expiration
245
+ in the `Hitnmiss::Entity` will take precedence over the
246
+ `default_expiration`.
247
+
248
+ ```ruby
249
+ class MostRecentPrice
250
+ include Hitnmiss::Repository
251
+
252
+ default_expiration 134
253
+
254
+ private
255
+
256
+ def fetch(*args)
257
+ # - do whatever to get the value you want to cache
258
+ # - construct a Hitnmiss::Entity with the value and optionally a
259
+ # result based expiration. If no result based expiration is
260
+ # provided it will use the default_expiration.
261
+ Hitnmiss::Entity.new('some value', expiration: 235)
262
+ end
263
+ end
264
+ ```
265
+
266
+ #### Set Cache Source based Fingerprint
267
+
268
+ In some cases you might want to set the fingerprint for the cached value. Doing
269
+ so provides more flexibility to `Hitnmiss` in terms of determining staleness of
270
+ cache. A very common example of this would be if you are fetching something over
271
+ HTTP from the remote and the remote includes the `ETag` header. If you take the
272
+ value of the `ETag` header (a fingerprint) and you set it in the
273
+ `Hitnmiss::Entity` it can be used later on by `Hitnmiss` to aid in identifying
274
+ staleness. Once you have obtained the fingerprint by whatever means you can set
275
+ it by passing the fingerprint into the `Hitnmiss::Entity` constructor as seen
276
+ below.
277
+
278
+ ```ruby
279
+ class MostRecentPrice
280
+ include Hitnmiss::Repository
281
+
282
+ default_expiration 134
283
+
284
+ private
285
+
286
+ def fetch(*args)
287
+ # - do whatever to get the value you want to cache
288
+ # - do whatever to get the fingerprint of the value you want to cache
289
+ # - construct a Hitnmiss::Entity with the value and optionally a
290
+ # fingerprint.
291
+ Hitnmiss::Entity.new('some value',
292
+ fingerprint: "63478adce0dd0fbc82ef4bb1f1d64193")
293
+ end
294
+ end
295
+ ```
296
+
297
+ #### Set Cache Source based Last Modified
298
+
299
+ In some cases you might want to set the `last_modified` value for a cached
300
+ value. Doing this provides more felxibility to `Hitnmiss` when trying to
301
+ determine staleness of a cached item. A common example of this would be if you
302
+ are fetching somthing over HTTP from a remote server and it includes the
303
+ `Last-Modified` entity header in the response. If you took the value of the
304
+ `Last-Modified` header and you set it in the `Hitnmiss::Entity` it can be used
305
+ later on by `Hitnmiss` to aid in identifying staleness. Once you have obtained
306
+ the `Last-Modified` value by whatever means you can set it by passing the
307
+ `last_modified` option into the `Hitnmiss::Entity` constructor as seen below.
308
+
309
+ ```ruby
310
+ class MostRecentPrice
311
+ include Hitnmiss::Repository
312
+
313
+ default_expiration 134
314
+
315
+ private
316
+
317
+ def fetch(*args)
318
+ # - do whatever to get the value you want to cache
319
+ # - do whatever to get the last modified of the value you want to cache
320
+ # - construct a Hitnmiss::Entity with the value and optionally a
321
+ # last_modified value.
322
+ Hitnmiss::Entity.new('some value',
323
+ last_modified: "2016-04-15T13:00:00Z")
324
+ end
325
+ end
326
+ ```
327
+
328
+ ## Usage
329
+
330
+ The following is a light breakdown of the various pieces of Hitnmiss and
331
+ how to get going with them after defining your cache repository.
332
+
333
+ ### Priming an entity
334
+
335
+ Once you have defined the `fetch(*args)` method you can construct an
336
+ instance of your cache repository and use it. One way you might want to
337
+ use it is to force the repository to cache a value. This can be done
338
+ using the `prime` method as seen below.
339
+
340
+ ```ruby
341
+ repository = MostRecentPrice.new
342
+ repository.prime(current_user.id) # => cached_value
343
+ ```
344
+
345
+ ### Priming the entire repository
346
+
347
+ You can use the `prime_all` method to prime the entire repository. **Note:**
348
+ The repository class must define the `fetch_all(keyspace)` method for this
349
+ to work. See the "Define Fetcher Methods" section above for details.
350
+
351
+ ```ruby
352
+ repository = MostRecentPrice.new
353
+ repository.prime_all # => [cached values, ...]
354
+ ```
355
+
356
+ ### Getting a value
357
+
358
+ You can also use your cache repository to get a particular cached
359
+ value by doing the following.
360
+
361
+ ```ruby
362
+ repository = MostRecentPrice.new
363
+ repository.get(current_user.id) # => cached_value
364
+ ```
365
+
366
+ ### Deleting a cached value
367
+
368
+ You can delete a cached value by calling the `delete` method with the
369
+ same arguments used to get it.
370
+
371
+ ```ruby
372
+ repository = MostRecentPrice.new
373
+ # cache some values ex: repository.prime(current_user.id)
374
+ repository.delete(current_user.id)
375
+ ```
376
+
377
+ ### Clearing a repository
378
+
379
+ You can clear the entire repository of cached values by calling the
380
+ `clear` method.
381
+
382
+ ```ruby
383
+ repository = MostRecentPrice.new
384
+ # cache some values ex: repository.prime(current_user.id)
385
+ repository.clear
386
+ ```
387
+
388
+ ## Contributing
389
+
390
+ If you are interested in contributing to Hitnmiss. There is a guide (both code
391
+ and general help) over in
392
+ [CONTRIBUTING](https://github.com/Acornsgrow/hitnmiss/blob/master/CONTRIBUTING.md).
393
+
394
+ ## License
395
+
396
+ The gem is available as open source under the terms of the [MIT
397
+ License](http://opensource.org/licenses/MIT).