knuckles 0.3.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ac7f83df75c4e52ee59dc85ff6d6d98c863f5963
4
- data.tar.gz: e1ff17c2903f39eac251ee3bbaaa56ab1adfc1e5
3
+ metadata.gz: 69ff93727664c8cb7f6c22d52542521b400f7421
4
+ data.tar.gz: 88f1ad40c4d32591dfcc6b9d3e8c093d1c14a80a
5
5
  SHA512:
6
- metadata.gz: d49c80cd7f0e4b2cefc1861188b84bf9ccd752975d6714d068c8a97798a8e95e615a6192762afe721a6461f9ec23105d66d46651b1cd809fa7c3d6abb577eb8d
7
- data.tar.gz: 9e7bd47dd8b17edba6e3e88fa367bca797654b876fc8a4fb2f87f25189bbb503a3c72e8cca288c301ec6c0876e94a5a693ded4ca8a51031bb90e1649b5e80ec3
6
+ metadata.gz: 5bff88dba2e59d125dfa1c61ee050f191f690e1f1a12f704729e1f8e29b76bfe7a444e4bc550cd4a09d3777156114a10b8dc319f6594e92e83ca013ea17255aa
7
+ data.tar.gz: e51e57e0df18ccaef4304ee9f78a47ee0472063be046592b883cf2e6f732ba68cc9803f2f9636ff09ad9e9b48006603fa1c36fb25203405e6d3f2b8144e50844
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## v0.4.0 - 2016-05-11
2
+
3
+ * Added: `Knuckles::Active::Hydrator`, a hydrator specifically designed to work
4
+ with `ActiveRecord`. It converts a relation of minimally loaded `ActiveRecord`
5
+ models into fully loaded models ready for serialization.
6
+ * Added: A compatibility layer within the `Renderer` stage to ease in the
7
+ transition from `ActiveModelSerializers`. Both `Knuckles::View` or `AMS`
8
+ instances now work when passed as the `view` option.
9
+
1
10
  ## v0.3.0 - 2016-04-07
2
11
 
3
12
  * Added: Tons of documentation.
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Knuckles
4
+ module Active
5
+ # The active hydrator converts minimal objects in a prepared collection
6
+ # into fully "hydrated" versions of the same record. For example, the
7
+ # initial `model` may only have the `id` and `updated_at` timestamp
8
+ # selected, which is ideal for fetching from the cache. If the object
9
+ # wasn't in the cache then all of the fields are needed for a complete
10
+ # rendering, so the hydration call will use the passed relation to fetch
11
+ # the full model and any associations.
12
+ #
13
+ # This `Hydrator` module is specifically designed to work with
14
+ # `ActiveRecord` relations. The initial objects can be anything that
15
+ # responds to `id`, but the relation should be an `ActiveRecord` relation.
16
+ module Hydrator
17
+ extend self
18
+
19
+ # Convert all uncached objects into their full representation.
20
+ #
21
+ # @param [Enumerable] prepared The prepared collection for processing
22
+ # @option [#Relation] :relation An ActiveRecord::Relation, used to
23
+ # hydrate uncached objects
24
+ #
25
+ # @example Hydrating missing objects
26
+ #
27
+ # prepared = [Post.new(1), Post.new(2)]
28
+ # relation = Post.all.preload(:author, :comments)
29
+ #
30
+ # Knuckles::Active::Hydrator.call(prepared, relation: relation) #=>
31
+ # # [{object: #Post<1>, cached?: false, ...
32
+ #
33
+ def call(prepared, options)
34
+ mapping = id_object_mapping(prepared)
35
+
36
+ if mapping.any?
37
+ relation = relation_without_pagination(options)
38
+
39
+ relation.where(id: mapping.keys).each do |hydrated|
40
+ mapping[hydrated.id][:object] = hydrated
41
+ end
42
+ end
43
+
44
+ prepared
45
+ end
46
+
47
+ private
48
+
49
+ def relation_without_pagination(options)
50
+ options.fetch(:relation).offset(false).limit(false)
51
+ end
52
+
53
+ def id_object_mapping(objects)
54
+ objects.each_with_object({}) do |hash, memo|
55
+ next if hash[:cached?]
56
+
57
+ memo[hash[:object].id] = hash
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -18,9 +18,10 @@ module Knuckles
18
18
  private
19
19
 
20
20
  def do_render(object, view, options)
21
- view.relations(object, options).merge!(
22
- view.root => [view.data(object, options)]
23
- )
21
+ case view
22
+ when Knuckles::View then view.render(object, options)
23
+ else view.new(object, options).as_json
24
+ end
24
25
  end
25
26
  end
26
27
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Knuckles
4
- VERSION = "0.3.0".freeze
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/knuckles/view.rb CHANGED
@@ -43,6 +43,23 @@ module Knuckles
43
43
  def root
44
44
  end
45
45
 
46
+ # Convenience for combining the results of data and relations
47
+ # into a single object.
48
+ #
49
+ # @param [Object] _object The object for serializing.
50
+ # @param [Hash] _options The options to be used during serialization, i.e.
51
+ # `:scope`
52
+ #
53
+ # @return [Hash] A hash representing the serialized object and relations.
54
+ #
55
+ # @example Rendering an object
56
+ #
57
+ # TagView.render(tag) #=> {tags: [{id: 1, name: "Alpha"}]}
58
+ #
59
+ def render(object, options = {})
60
+ relations(object, options).merge!(root => [data(object, options)])
61
+ end
62
+
46
63
  # Serialize an object into a hash. This simply returns an empty hash by
47
64
  # default, it must be overridden by submodules.
48
65
  #
data/lib/knuckles.rb CHANGED
@@ -31,9 +31,22 @@ require "json"
31
31
  module Knuckles
32
32
  autoload :Keygen, "knuckles/keygen"
33
33
  autoload :Pipeline, "knuckles/pipeline"
34
- autoload :Stages, "knuckles/stages"
35
34
  autoload :View, "knuckles/view"
36
35
 
36
+ module Active
37
+ autoload :Hydrator, "knuckles/active/hydrator"
38
+ end
39
+
40
+ module Stages
41
+ autoload :Combiner, "knuckles/stages/combiner"
42
+ autoload :Dumper, "knuckles/stages/dumper"
43
+ autoload :Enhancer, "knuckles/stages/enhancer"
44
+ autoload :Fetcher, "knuckles/stages/fetcher"
45
+ autoload :Hydrator, "knuckles/stages/hydrator"
46
+ autoload :Renderer, "knuckles/stages/renderer"
47
+ autoload :Writer, "knuckles/stages/writer"
48
+ end
49
+
37
50
  extend self
38
51
 
39
52
  attr_writer :cache, :keygen, :notifications, :serializer
@@ -0,0 +1,43 @@
1
+ RSpec.describe Knuckles::Active::Hydrator do
2
+ class FakeRelation
3
+ attr_reader :objects
4
+
5
+ def initialize(objects)
6
+ @objects = objects
7
+ end
8
+
9
+ def offset(_bool)
10
+ self
11
+ end
12
+
13
+ def limit(_bool)
14
+ self
15
+ end
16
+
17
+ def where(id:)
18
+ objects.select { |object| id.include?(object.id) }
19
+ end
20
+ end
21
+
22
+ describe ".call" do
23
+ it "is a no-op if all objects are cached" do
24
+ prepared = prepare([Tag.new(1, "alpha")])
25
+
26
+ prepared.each { |prep| prep[:cached?] = true }
27
+
28
+ expect(Knuckles::Active::Hydrator.call(prepared, {})).to eq(prepared)
29
+ end
30
+
31
+ it "replaces all objects using the results of a query" do
32
+ prepared = prepare([Tag.new(1, nil), Tag.new(2, nil)])
33
+ relation = FakeRelation.new([Tag.new(1, "alpha"), Tag.new(2, "beta")])
34
+
35
+ results = Knuckles::Active::Hydrator.call(prepared, relation: relation)
36
+
37
+ expect(results.map { |hash| hash[:object] }).to eq([
38
+ Tag.new(1, "alpha"),
39
+ Tag.new(2, "beta")
40
+ ])
41
+ end
42
+ end
43
+ end
@@ -28,5 +28,25 @@ RSpec.describe Knuckles::Stages::Renderer do
28
28
  ]
29
29
  )
30
30
  end
31
+
32
+ it "serializes using a class based renderer" do
33
+ tag = Tag.new(1, "alpha")
34
+
35
+ ar_view = Class.new do
36
+ def initialize(object, _options)
37
+ @object = object
38
+ end
39
+
40
+ def as_json
41
+ {tags: [{id: @object.id, name: @object.name}]}
42
+ end
43
+ end
44
+
45
+ results = Knuckles::Stages::Renderer.call(prepare([tag]), view: ar_view)
46
+
47
+ expect(results[0][:result]).to eq(
48
+ tags: [{id: 1, name: "alpha"}]
49
+ )
50
+ end
31
51
  end
32
52
  end
@@ -35,4 +35,14 @@ RSpec.describe Knuckles::View do
35
35
  )
36
36
  end
37
37
  end
38
+
39
+ describe ".render" do
40
+ it "performs combined rendering of relations and data" do
41
+ tag = Tag.new(1, "Alpha")
42
+
43
+ expect(TagView.render(tag)).to eq(
44
+ tags: [{id: 1, name: "Alpha"}]
45
+ )
46
+ end
47
+ end
38
48
  end
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.3.0
4
+ version: 0.4.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-04-07 00:00:00.000000000 Z
11
+ date: 2016-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -77,9 +77,9 @@ files:
77
77
  - LICENSE.txt
78
78
  - README.md
79
79
  - lib/knuckles.rb
80
+ - lib/knuckles/active/hydrator.rb
80
81
  - lib/knuckles/keygen.rb
81
82
  - lib/knuckles/pipeline.rb
82
- - lib/knuckles/stages.rb
83
83
  - lib/knuckles/stages/combiner.rb
84
84
  - lib/knuckles/stages/dumper.rb
85
85
  - lib/knuckles/stages/enhancer.rb
@@ -89,6 +89,7 @@ files:
89
89
  - lib/knuckles/stages/writer.rb
90
90
  - lib/knuckles/version.rb
91
91
  - lib/knuckles/view.rb
92
+ - spec/knuckles/active/hydrator_spec.rb
92
93
  - spec/knuckles/keygen_spec.rb
93
94
  - spec/knuckles/pipeline_spec.rb
94
95
  - spec/knuckles/stages/combiner_spec.rb
@@ -128,6 +129,7 @@ signing_key:
128
129
  specification_version: 4
129
130
  summary: Simple performance aware data serialization
130
131
  test_files:
132
+ - spec/knuckles/active/hydrator_spec.rb
131
133
  - spec/knuckles/keygen_spec.rb
132
134
  - spec/knuckles/pipeline_spec.rb
133
135
  - spec/knuckles/stages/combiner_spec.rb
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Knuckles
4
- module Stages
5
- autoload :Combiner, "knuckles/stages/combiner"
6
- autoload :Dumper, "knuckles/stages/dumper"
7
- autoload :Enhancer, "knuckles/stages/enhancer"
8
- autoload :Fetcher, "knuckles/stages/fetcher"
9
- autoload :Hydrator, "knuckles/stages/hydrator"
10
- autoload :Renderer, "knuckles/stages/renderer"
11
- autoload :Writer, "knuckles/stages/writer"
12
- end
13
- end