hitnmiss 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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).