knuckles 0.4.0 → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +168 -0
- data/lib/knuckles.rb +5 -1
- data/lib/knuckles/active/hydrator.rb +1 -1
- data/lib/knuckles/pipeline.rb +1 -1
- data/lib/knuckles/stages/combiner.rb +40 -1
- data/lib/knuckles/stages/dumper.rb +14 -0
- data/lib/knuckles/stages/enhancer.rb +29 -0
- data/lib/knuckles/stages/fetcher.rb +40 -4
- data/lib/knuckles/stages/hydrator.rb +24 -0
- data/lib/knuckles/stages/renderer.rb +25 -0
- data/lib/knuckles/stages/writer.rb +14 -1
- data/lib/knuckles/version.rb +2 -1
- data/lib/knuckles/view.rb +2 -2
- data/spec/knuckles/stages/fetcher_spec.rb +9 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96d10e0c441bc44adc48654b19878d2a7283cd7b
|
4
|
+
data.tar.gz: 4a9df62148abaec7d7fb161b37bf1bab7b511327
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 828e4d98d6f655aee4ed20fb0792af61920d9304775b23693b0f3cd87b7a6c4d4d68ceddc20a3895408eca23f88df1220239b74f705d415cf0b3f8b4525862fe
|
7
|
+
data.tar.gz: 4cb0da0daf47140f5ff953214f739d79de15de96f59134dadb803c02887a70c2cc6bf35b0719f5caf1493614e2685794ac58d236917866010cd0b32d54e674a0
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## v0.5.0 - 2016-07-08
|
2
|
+
|
3
|
+
* Added: Accept a `proc` or any callable object as the `keygen`. This simplifies
|
4
|
+
overriding the cache key on a per-instance basis.
|
5
|
+
* Added: Lots of documentation! All code has inline documentation now.
|
6
|
+
|
1
7
|
## v0.4.0 - 2016-05-11
|
2
8
|
|
3
9
|
* Added: `Knuckles::Active::Hydrator`, a hydrator specifically designed to work
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
[](https://travis-ci.org/sorentwo/knuckles)
|
2
2
|
[](https://coveralls.io/github/sorentwo/knuckles?branch=master)
|
3
3
|
[](https://codeclimate.com/github/sorentwo/knuckles)
|
4
|
+
[](http://inch-ci.org/github/sorentwo/knuckles)
|
4
5
|
|
5
6
|
# Knuckles (Because Sonic was Taken)
|
6
7
|
|
@@ -69,6 +70,144 @@ end
|
|
69
70
|
With the top level module configured it is simple to jump right into rendering,
|
70
71
|
but we'll look at configuring the pipeline first.
|
71
72
|
|
73
|
+
## Understanding and Using Pipelines
|
74
|
+
|
75
|
+
Knuckles renders and serializes data through a series of stages composed into a
|
76
|
+
pipeline. Stages can easily be added or removed to control how data is
|
77
|
+
transformed. Here is a breakdown of the default stages and what their role is
|
78
|
+
within the pipeline.
|
79
|
+
|
80
|
+
#### Fetcher
|
81
|
+
|
82
|
+
The fetcher is responsible for bulk retrieval of data from the cache. Fetching
|
83
|
+
is done using a single `read_multi` operation, which is multiplexed in caches
|
84
|
+
like Redis or MemCached.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
pipeline = Knuckles::Pipeline.new
|
88
|
+
|
89
|
+
pipeline.call(posts)
|
90
|
+
```
|
91
|
+
|
92
|
+
#### Hydrator
|
93
|
+
|
94
|
+
Models that couldn't be retrieved from the cache will then be hydrated, a
|
95
|
+
process where the stripped down model that was given for fetching is replaced
|
96
|
+
with a full model with preloaded associations. The behavior of the hydrator
|
97
|
+
stage is entirely controlled by passing a Proc as the `hydrate` option. If the
|
98
|
+
`hydrate` proc is omitted hydration will be skipped. Skipping hydration is
|
99
|
+
useful if you want a simplified pipeline where full models and their
|
100
|
+
associations are preloaded before starting serialization.
|
101
|
+
|
102
|
+
See `Knuckles::Active::Hydrator` for an alternative `ActiveRecord` specific
|
103
|
+
hydrator. If you are using Knuckles within a Rais app, this is probably the
|
104
|
+
hydration stage you want to use.
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
# Using the standard hydrator
|
108
|
+
pipeline.call(posts, hydrator: -> (model) { model.fetch })
|
109
|
+
|
110
|
+
# Using active hydrator with a relation that has a `prepared` scope
|
111
|
+
pipeline.call(posts, relation: posts.prepared)
|
112
|
+
```
|
113
|
+
|
114
|
+
#### Renderer
|
115
|
+
|
116
|
+
After un-cached models have been hydrated they can be rendered. Rendering is
|
117
|
+
synonymous with converting a model to a hash, like calling `as_json` on an
|
118
|
+
`ActiveRecord` model. Knuckles provides a minimal (but fast) view module that
|
119
|
+
can be used with the rendering step. Alternatively, if you're migrating from
|
120
|
+
`ActiveModelSerializers` you can pass in an AMS class instead.
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
# Using Knuckles::View
|
124
|
+
pipeline.call(models, view: PostView)
|
125
|
+
|
126
|
+
# Using ActiveModelSerializer
|
127
|
+
pipeline.call(models, view: PostSerializer)
|
128
|
+
```
|
129
|
+
|
130
|
+
#### Writer
|
131
|
+
|
132
|
+
After un-cached models have been serialized they are ready to be cached for
|
133
|
+
future retrieval. Each fully serialized model is written to the cache in a
|
134
|
+
single `write_multi` operation if available (using Readthis, for example). Only
|
135
|
+
previously un-cached data will be written to the cache, making the writer a
|
136
|
+
no-op when all of the data was cached initially.
|
137
|
+
|
138
|
+
#### Enhancer
|
139
|
+
|
140
|
+
The enhancer modifies rendered data using proc passed through options. The
|
141
|
+
enhancer stage is critical to customizing the final output. For example, if
|
142
|
+
staff should have confidential data that regular users can't see you can enhance
|
143
|
+
the final values. Another use of enhancers is personalizing an otherwise generic
|
144
|
+
response.
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
# Removing staff only content from the rendered data
|
148
|
+
pipeline.call(posts,
|
149
|
+
scope: current_user,
|
150
|
+
enhancer: lambda do |result, options|
|
151
|
+
scope = options[:scope]
|
152
|
+
|
153
|
+
unless scope.staff?
|
154
|
+
result.delete_if { |key, _| key == "confidential" }
|
155
|
+
end
|
156
|
+
|
157
|
+
result
|
158
|
+
end
|
159
|
+
)
|
160
|
+
```
|
161
|
+
|
162
|
+
#### Combiner
|
163
|
+
|
164
|
+
The combiner stage merges all of the individually rendered results into a single
|
165
|
+
hash. The output of this stage is a single object, ready to be serialized.
|
166
|
+
|
167
|
+
#### Dumper
|
168
|
+
|
169
|
+
The dumping process combines de-duplication and actual serialization. For every
|
170
|
+
top level key that is an array all of the children will have uniqueness
|
171
|
+
enforced. For example, if you had rendered a collection of posts that shared the
|
172
|
+
same author, you will only have a single author object serialized. Be aware that
|
173
|
+
the uniqueness check relies on the presence of an `id` key rather than full
|
174
|
+
object comparisons.
|
175
|
+
|
176
|
+
Dumping is the final stage of the pipeline. At this point you have a single
|
177
|
+
serialized payload in the format of your choice (JSON by default), ready to send
|
178
|
+
back as a response.
|
179
|
+
|
180
|
+
## Customizing Pipelines
|
181
|
+
|
182
|
+
Pipelines stages can be removed, swapped out or otherwise tuned. An array of
|
183
|
+
stages can be passed when building a new pipeline. Here is an example of
|
184
|
+
creating a customized pipeline without any caching, hydration, or enhancing:
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
Knuckles::Pipeline.new(stages: [
|
188
|
+
Knuckles::Stages::Renderer,
|
189
|
+
Knuckles::Stages::Combiner,
|
190
|
+
Knuckles::Stages::Dumper
|
191
|
+
])
|
192
|
+
```
|
193
|
+
|
194
|
+
Or, perhaps you want to use the active hydrator instead:
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
Knuckles::Pipeline.new(stages: [
|
198
|
+
Knuckles::Stages::Fetcher,
|
199
|
+
Knuckles::Active::Hydrator,
|
200
|
+
Knuckles::Stages::Renderer,
|
201
|
+
Knuckles::Stages::Writer,
|
202
|
+
Knuckles::Stages::Enhancer,
|
203
|
+
Knuckles::Stages::Combiner,
|
204
|
+
Knuckles::Stages::Dumper
|
205
|
+
])
|
206
|
+
```
|
207
|
+
|
208
|
+
Note that once the pipeline is initialized the stages are frozen to prevent
|
209
|
+
modification.
|
210
|
+
|
72
211
|
## Defining Views for Rendering
|
73
212
|
|
74
213
|
While you can use Knuckles with other serializers, you can also use the provided
|
@@ -95,6 +234,35 @@ end
|
|
95
234
|
|
96
235
|
See `Knuckles::View` for more usage details.
|
97
236
|
|
237
|
+
## Rendering in Rails
|
238
|
+
|
239
|
+
One driving factor of Knuckles is that code should be explicit. As a result
|
240
|
+
there isn't a default Railtie that will integrate Knuckles into the
|
241
|
+
`ActiveController` rendering process for you. Luckily there isn't much to
|
242
|
+
setting up a new pipeline for rendering. Add this to your
|
243
|
+
`ApplicationController` or an API specific controller:
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
def knuckles_render(relation, options)
|
247
|
+
Knuckles::Pipeline.new.call(relation, options)
|
248
|
+
end
|
249
|
+
```
|
250
|
+
|
251
|
+
Now you can easily render responses:
|
252
|
+
|
253
|
+
```ruby
|
254
|
+
def index
|
255
|
+
posts = posts.published.paginate(pagination_params)
|
256
|
+
|
257
|
+
render json: knuckles_render(
|
258
|
+
posts.select(:id, :updated_at),
|
259
|
+
relation: posts.prepared,
|
260
|
+
view: PostView,
|
261
|
+
scope: current_user,
|
262
|
+
)
|
263
|
+
end
|
264
|
+
```
|
265
|
+
|
98
266
|
## Contributing
|
99
267
|
|
100
268
|
1. Fork it ( https://github.com/sorentwo/knuckles/fork )
|
data/lib/knuckles.rb
CHANGED
@@ -33,10 +33,13 @@ module Knuckles
|
|
33
33
|
autoload :Pipeline, "knuckles/pipeline"
|
34
34
|
autoload :View, "knuckles/view"
|
35
35
|
|
36
|
+
# Top level wrapper for stages that are expected to interact with `Active*`
|
37
|
+
# libraries, such as `ActiveModel`.
|
36
38
|
module Active
|
37
39
|
autoload :Hydrator, "knuckles/active/hydrator"
|
38
40
|
end
|
39
41
|
|
42
|
+
# Top level wrapper for standard pipleline stages.
|
40
43
|
module Stages
|
41
44
|
autoload :Combiner, "knuckles/stages/combiner"
|
42
45
|
autoload :Dumper, "knuckles/stages/dumper"
|
@@ -104,7 +107,8 @@ module Knuckles
|
|
104
107
|
yield self
|
105
108
|
end
|
106
109
|
|
107
|
-
#
|
110
|
+
# Reset all configuration values back to `nil`, restoring them to the
|
111
|
+
# defaults. This is useful for testing because configuration is global.
|
108
112
|
def reset!
|
109
113
|
@cache = nil
|
110
114
|
@keygen = nil
|
@@ -24,8 +24,8 @@ module Knuckles
|
|
24
24
|
#
|
25
25
|
# @example Hydrating missing objects
|
26
26
|
#
|
27
|
-
# prepared = [Post.new(1), Post.new(2)]
|
28
27
|
# relation = Post.all.preload(:author, :comments)
|
28
|
+
# prepared = relation.select(:id, :updated_at)
|
29
29
|
#
|
30
30
|
# Knuckles::Active::Hydrator.call(prepared, relation: relation) #=>
|
31
31
|
# # [{object: #Post<1>, cached?: false, ...
|
data/lib/knuckles/pipeline.rb
CHANGED
@@ -2,10 +2,47 @@
|
|
2
2
|
|
3
3
|
module Knuckles
|
4
4
|
module Stages
|
5
|
+
# The combiner stage merges all of the individually rendered results into a
|
6
|
+
# single hash. The output of this stage is a single object with string keys
|
7
|
+
# and array values, ready to be serialized.
|
5
8
|
module Combiner
|
6
9
|
extend self
|
7
10
|
|
8
|
-
|
11
|
+
# Merge all of the rendered data into a single hash. Each
|
12
|
+
# resulting value will be an array, even if there was only one
|
13
|
+
# value in the original rendered results.
|
14
|
+
#
|
15
|
+
# @param [Enumerable] prepared The prepared collection to be combined
|
16
|
+
# @param [Hash] _options Options aren't used, but are accepted
|
17
|
+
# to maintain a consistent interface
|
18
|
+
#
|
19
|
+
# @example Combining rendered data
|
20
|
+
#
|
21
|
+
# prepared = [
|
22
|
+
# {
|
23
|
+
# result: {
|
24
|
+
# author: {id: 1, name: "Michael"},
|
25
|
+
# posts: [{id: 1, title: "hello"}],
|
26
|
+
# }
|
27
|
+
# }, {
|
28
|
+
# result: {
|
29
|
+
# author: {id: 1, name: "Michael"},
|
30
|
+
# posts: [{id: 2, title: "there"}],
|
31
|
+
# }
|
32
|
+
# }
|
33
|
+
# ]
|
34
|
+
#
|
35
|
+
# Knuckles::Stage::Combiner.call(prepared, {}) #=> {
|
36
|
+
# "author" => [
|
37
|
+
# {id: 1, name: "Michael"}
|
38
|
+
# ],
|
39
|
+
# "posts" => [
|
40
|
+
# {id: 1, title: "hello"},
|
41
|
+
# {id: 2, title: "there"}
|
42
|
+
# ]
|
43
|
+
# }
|
44
|
+
#
|
45
|
+
def call(prepared, _options)
|
9
46
|
prepared.each_with_object(array_backed_hash) do |hash, memo|
|
10
47
|
hash[:result].each do |root, values|
|
11
48
|
case values
|
@@ -16,6 +53,8 @@ module Knuckles
|
|
16
53
|
end
|
17
54
|
end
|
18
55
|
|
56
|
+
private
|
57
|
+
|
19
58
|
def array_backed_hash
|
20
59
|
Hash.new { |hash, key| hash[key] = [] }
|
21
60
|
end
|
@@ -2,9 +2,23 @@
|
|
2
2
|
|
3
3
|
module Knuckles
|
4
4
|
module Stages
|
5
|
+
# The dumping process combines de-duplication and actual serialization. For
|
6
|
+
# every top level key that is an array all of the children will have
|
7
|
+
# uniqueness enforced. For example, if you had rendered a collection of
|
8
|
+
# posts that shared the same author, you will only have a single author
|
9
|
+
# object serialized. Be aware that the uniqueness check relies on the
|
10
|
+
# presence of an `id` key rather than full object comparisons.
|
5
11
|
module Dumper
|
6
12
|
extend self
|
7
13
|
|
14
|
+
# De-duplicate values in all keys and merge them into a single hash.
|
15
|
+
# Afterwards the complete hash is serialized using the serializer
|
16
|
+
# configured at `Knuckles.serializer`.
|
17
|
+
#
|
18
|
+
# @param [Enumerable<Hash>] objects A collection of hashes to be dumped
|
19
|
+
# @param [Hash] _options Options aren't used, but are accepted
|
20
|
+
# to maintain a consistent interface
|
21
|
+
#
|
8
22
|
def call(objects, _options)
|
9
23
|
Knuckles.serializer.dump(keys_to_arrays(objects))
|
10
24
|
end
|
@@ -2,9 +2,38 @@
|
|
2
2
|
|
3
3
|
module Knuckles
|
4
4
|
module Stages
|
5
|
+
# The enhancer modifies rendered data using proc passed through options.
|
6
|
+
# The enhancer stage is critical to customizing the final output. For
|
7
|
+
# example, if staff should have confidential data that regular users can't
|
8
|
+
# see you can enhance the final values. Another use of enhancers is
|
9
|
+
# personalizing an otherwise generic response.
|
5
10
|
module Enhancer
|
6
11
|
extend self
|
7
12
|
|
13
|
+
# Modify all results using an `enhancer` proc.
|
14
|
+
#
|
15
|
+
# @param [Enumerable] prepared The prepared collection to be enhanced
|
16
|
+
# @option [Proc] :enhancer A `proc`, `lambda`, or any object that responds
|
17
|
+
# to `call`. Every complete `result` in the prepared collection will be
|
18
|
+
# passed to the enhancer.
|
19
|
+
#
|
20
|
+
# @example Removing tags unless the scope is staff
|
21
|
+
#
|
22
|
+
# enhancer = lambda do |result, options|
|
23
|
+
# scope = options[:scope]
|
24
|
+
#
|
25
|
+
# unless scope.staff?
|
26
|
+
# result.delete_if { |key, _| key == "tags" }
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# result
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# prepared = [{result: {"posts" => [], "tags" => []}}]
|
33
|
+
#
|
34
|
+
# Knuckles::Stages::Enhancer.call(prepared, enhancer: enhancer) #=>
|
35
|
+
# # [{result: {"posts" => []}}]
|
36
|
+
#
|
8
37
|
def call(prepared, options)
|
9
38
|
enhancer = options[:enhancer]
|
10
39
|
|
@@ -2,16 +2,44 @@
|
|
2
2
|
|
3
3
|
module Knuckles
|
4
4
|
module Stages
|
5
|
+
# The fetcher is responsible for bulk retrieval of data from the cache.
|
6
|
+
# Fetching is done using a single `read_multi` operation, which is
|
7
|
+
# multiplexed in caches like Redis or MemCached.
|
8
|
+
#
|
9
|
+
# The underlying cache *must* support `read_multi` for the stage to work.
|
5
10
|
module Fetcher
|
6
11
|
extend self
|
7
12
|
|
13
|
+
# Fetch all previously cached objects from the configured store.
|
14
|
+
#
|
15
|
+
# @param [Enumerable] prepared The prepared collection to fetch
|
16
|
+
# @option [Module] :keygen (Knuckles.keygen) The cache key generator used
|
17
|
+
# to construct an entries cache_key. It can be any object that responds
|
18
|
+
# to `expand_key`
|
19
|
+
#
|
20
|
+
# @example Provide a custom keygen
|
21
|
+
#
|
22
|
+
# keygen = Module.new do
|
23
|
+
# def self.expand_key(object)
|
24
|
+
# object.name
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# Knuckles::Stages::Fetcher.call(prepared, keygen: keygen)
|
29
|
+
#
|
30
|
+
# @example Use a lambda as a keygen
|
31
|
+
#
|
32
|
+
# Knuckles::Stages::Fetcher.call(
|
33
|
+
# prepared,
|
34
|
+
# keygen: -> (object) { object.name }
|
35
|
+
# )
|
36
|
+
#
|
8
37
|
def call(prepared, options)
|
9
38
|
results = get_cached(prepared, options)
|
10
39
|
|
11
40
|
prepared.each do |hash|
|
12
|
-
result = results[hash[:key]]
|
13
|
-
hash[:cached?] = !result.nil?
|
14
|
-
hash[:result] = result
|
41
|
+
hash[:result] = results[hash[:key]]
|
42
|
+
hash[:cached?] = !hash[:result].nil?
|
15
43
|
end
|
16
44
|
end
|
17
45
|
|
@@ -20,11 +48,19 @@ module Knuckles
|
|
20
48
|
def get_cached(prepared, options)
|
21
49
|
kgen = options.fetch(:keygen, Knuckles.keygen)
|
22
50
|
keys = prepared.map do |hash|
|
23
|
-
hash[:key] =
|
51
|
+
hash[:key] = expand_key(kgen, hash[:object])
|
24
52
|
end
|
25
53
|
|
26
54
|
Knuckles.cache.read_multi(*keys)
|
27
55
|
end
|
56
|
+
|
57
|
+
def expand_key(keygen, object)
|
58
|
+
if keygen.respond_to?(:call)
|
59
|
+
keygen.call(object)
|
60
|
+
else
|
61
|
+
keygen.expand_key(object)
|
62
|
+
end
|
63
|
+
end
|
28
64
|
end
|
29
65
|
end
|
30
66
|
end
|
@@ -2,9 +2,33 @@
|
|
2
2
|
|
3
3
|
module Knuckles
|
4
4
|
module Stages
|
5
|
+
# The hydrator converts minimal objects in a prepared collection into fully
|
6
|
+
# "hydrated" versions of the same record. For example, the initial `model`
|
7
|
+
# may only have the `id` and `updated_at` timestamp selected, which is
|
8
|
+
# ideal for fetching from the cache. If the object wasn't in the cache then
|
9
|
+
# all of the fields are needed for a complete rendering, so the hydration
|
10
|
+
# call will use the passed relation to fetch the full model and any
|
11
|
+
# associations.
|
12
|
+
#
|
13
|
+
# This is a generic hydrator suitable for any type of collection. If you
|
14
|
+
# are working with `ActiveRecord` you'll want to use the
|
15
|
+
# `Knuckles::Active::Hydrator` module instead.
|
5
16
|
module Hydrator
|
6
17
|
extend self
|
7
18
|
|
19
|
+
# Convert all uncached objects into their full representation.
|
20
|
+
#
|
21
|
+
# @param [Enumerable] prepared The prepared collection for processing
|
22
|
+
# @option [Proc, #call] :hydrate A proc used to load missing data for
|
23
|
+
# uncached objects
|
24
|
+
#
|
25
|
+
# @example Hydrating missing objects
|
26
|
+
#
|
27
|
+
# Knuckles::Hydrator.call(
|
28
|
+
# prepared,
|
29
|
+
# hydrate: -> (objects) { objects.each(&:fetch!) }
|
30
|
+
# )
|
31
|
+
#
|
8
32
|
def call(prepared, options)
|
9
33
|
hydrate = options[:hydrate]
|
10
34
|
|
@@ -2,9 +2,34 @@
|
|
2
2
|
|
3
3
|
module Knuckles
|
4
4
|
module Stages
|
5
|
+
# After un-cached models have been hydrated they can be rendered. Rendering
|
6
|
+
# is synonymous with converting a model to a hash, like calling `as_json`
|
7
|
+
# on an `ActiveRecord` model. Knuckles provides a minimal (but fast) view
|
8
|
+
# module that can be used with the rendering step. Alternatively, if you're
|
9
|
+
# migrating from `ActiveModelSerializers` you can pass in an AMS class
|
10
|
+
# instead.
|
5
11
|
module Renderer
|
6
12
|
extend self
|
7
13
|
|
14
|
+
# Serialize all un-cached objects into hashes.
|
15
|
+
#
|
16
|
+
# @param [Enumerable] objects The prepared collection to be rendered
|
17
|
+
# @option [Module] :view A `Knuckles::View` compliant module,
|
18
|
+
# it will be passed the object and any options. Alternately,
|
19
|
+
# a class compatible with the `ActiveModelSerializers` API.
|
20
|
+
#
|
21
|
+
# @example Using a Knuckles::View
|
22
|
+
#
|
23
|
+
# module PostView
|
24
|
+
# extend Knuckles::View
|
25
|
+
#
|
26
|
+
# def self.data(post, _options)
|
27
|
+
# {id: post.id, name: post.name}
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# pipeline.call(models, view: PostView)
|
32
|
+
#
|
8
33
|
def call(objects, options)
|
9
34
|
view = options.fetch(:view)
|
10
35
|
|
@@ -2,10 +2,23 @@
|
|
2
2
|
|
3
3
|
module Knuckles
|
4
4
|
module Stages
|
5
|
+
# After un-cached models have been serialized they are ready to be cached
|
6
|
+
# for future retrieval. Each fully serialized model is written to the cache
|
7
|
+
# in a single `write_multi` operation if available (using Readthis, for
|
8
|
+
# example). Only previously un-cached data will be written to the cache,
|
9
|
+
# making the writer a no-op when all of the data was cached initially.
|
5
10
|
module Writer
|
6
11
|
extend self
|
7
12
|
|
8
|
-
|
13
|
+
# Write all serialized, but previously un-cached, data to the cache.
|
14
|
+
#
|
15
|
+
# @param [Enumerable] objects A collection of hashes to be serialized,
|
16
|
+
# each hash must have they keys `:key`, `:result`, and `:cached?`.
|
17
|
+
# @param [Hash] _options Options aren't used, but are accepted
|
18
|
+
# to maintain a consistent interface
|
19
|
+
# @return The original enumerable is returned unchanged
|
20
|
+
#
|
21
|
+
def call(objects, _options)
|
9
22
|
if cache.respond_to?(:write_multi)
|
10
23
|
write_multi(objects)
|
11
24
|
else
|
data/lib/knuckles/version.rb
CHANGED
data/lib/knuckles/view.rb
CHANGED
@@ -46,8 +46,8 @@ module Knuckles
|
|
46
46
|
# Convenience for combining the results of data and relations
|
47
47
|
# into a single object.
|
48
48
|
#
|
49
|
-
# @param [Object]
|
50
|
-
# @param [Hash]
|
49
|
+
# @param [Object] object The object for serializing.
|
50
|
+
# @param [Hash] options The options to be used during serialization, i.e.
|
51
51
|
# `:scope`
|
52
52
|
#
|
53
53
|
# @return [Hash] A hash representing the serialized object and relations.
|
@@ -24,6 +24,15 @@ RSpec.describe Knuckles::Stages::Fetcher do
|
|
24
24
|
|
25
25
|
expect(pluck(results, :key)).to eq(["alpha"])
|
26
26
|
end
|
27
|
+
|
28
|
+
it "allows using a lambda as a keygen" do
|
29
|
+
results = Knuckles::Stages::Fetcher.call(
|
30
|
+
prepare([Tag.new(1, "alpha")]),
|
31
|
+
keygen: -> (object) { object.name }
|
32
|
+
)
|
33
|
+
|
34
|
+
expect(pluck(results, :key)).to eq(["alpha"])
|
35
|
+
end
|
27
36
|
end
|
28
37
|
|
29
38
|
def pluck(enum, key)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knuckles
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Parker Selbert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|