pbbuilder 0.16.1 → 0.16.2
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 +9 -0
- data/README.md +10 -2
- data/lib/pbbuilder/template.rb +55 -35
- data/lib/pbbuilder.rb +14 -8
- data/pbbuilder.gemspec +1 -1
- data/test/pbbuilder_template_test.rb +16 -3
- data/test/test_helper.rb +5 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f61e783abb61817e05f29772b6614314646c0fda6f24a5f73f556ebc19d0f97
|
4
|
+
data.tar.gz: 34cf6626e2f60aa50a7ee2e7278a0149557263d023dee6a758e4adf2d8e0d263
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15f60555cb89a70e0b28490db9bc00e4b9ca7ed34f179a5df0576d6a70ef737c9d7acc66e686b0b0216a003e043c967162d377087a443f3057470e4396889e4e
|
7
|
+
data.tar.gz: fd6e5587bd3d8bafc4988f089901afe2848c577243b5d9b105e629b92c3f7cbc522fbe218f9be59ca29605af27c034c423f244c8df0208dd72d9b67bf4831143
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,15 @@ All notable changes to this project will be documented in this file.
|
|
3
3
|
|
4
4
|
This format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
5
5
|
|
6
|
+
## 0.16.2
|
7
|
+
### Added
|
8
|
+
- Add support for partial as a first argument , e.g.`pb.friends "racers/racer", as: :racer, collection: @racers`
|
9
|
+
- Add tests to verify that fragment caching is operational
|
10
|
+
|
11
|
+
## 0.16.1
|
12
|
+
### Changed
|
13
|
+
- Deal properly with recursive protobuf messages while using ActiveView::CollectionRenderer
|
14
|
+
|
6
15
|
## 0.16.0
|
7
16
|
### Added
|
8
17
|
- Added support for new collection rendering, that is backed by ActiveView::CollectionRenderer.
|
data/README.md
CHANGED
@@ -13,7 +13,7 @@ We don't aim to have 100% compitability and coverage with jbuilder gem, but we c
|
|
13
13
|
| cache! | ✅ | ✅ |
|
14
14
|
| cache_if! | ✅ | ✅ |
|
15
15
|
| cache_root! | ✅| |
|
16
|
-
|
|
16
|
+
| fragment cache | ✅| ✅ |
|
17
17
|
| extract! | ✅ | ✅ |
|
18
18
|
| merge! | ✅ | ✅ |
|
19
19
|
| child! | ✅ | |
|
@@ -116,7 +116,15 @@ pb.cache_if! !admin?, "cache-key", expires_in: 10.minutes do
|
|
116
116
|
end
|
117
117
|
```
|
118
118
|
|
119
|
-
Fragment caching
|
119
|
+
Fragment caching currently works through ActionView::CollectionRenderer and can be used only with the following syntax:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
pb.friends partial: "racers/racer", as: :racer, collection: @racers, cached: true
|
123
|
+
```
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
pb.friends "racers/racer", as: :racer, collection: @racers, cached: true
|
127
|
+
```
|
120
128
|
|
121
129
|
## Installation
|
122
130
|
Add this line to your application's Gemfile:
|
data/lib/pbbuilder/template.rb
CHANGED
@@ -30,57 +30,45 @@ class PbbuilderTemplate < Pbbuilder
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
# Set the value in the message field.
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# pb.friends @friends, partial: "friend", as: :friend
|
37
|
+
# pb.friends partial: "racers/racer", as: :racer, collection: [Racer.new(1, "Johnny Test", []), Racer.new(2, "Max Verstappen", [])]
|
38
|
+
# pb.best_friend partial: "person", person: @best_friend
|
39
|
+
# pb.friends "racers/racer", as: :racer, collection: [Racer.new(1, "Johnny Test", []), Racer.new(2, "Max Verstappen", [])]
|
40
|
+
|
33
41
|
def set!(field, *args, **kwargs, &block)
|
34
42
|
# If partial options are being passed, we render a submessage with a partial
|
35
43
|
if kwargs.has_key?(:partial)
|
36
44
|
if args.one? && kwargs.has_key?(:as)
|
37
|
-
#
|
45
|
+
# example syntax that should end up here:
|
46
|
+
# pb.friends @friends, partial: "friend", as: :friend
|
38
47
|
# Call set! on the super class, passing in a block that renders a partial for every element
|
39
48
|
super(field, *args) do |element|
|
40
49
|
_set_inline_partial(element, kwargs)
|
41
50
|
end
|
42
51
|
elsif kwargs.has_key?(:collection) && kwargs.has_key?(:as)
|
43
|
-
#
|
44
|
-
# collection
|
45
|
-
options = kwargs.deep_dup
|
46
|
-
|
47
|
-
options.reverse_merge! locals: options.except(:partial, :as, :collection, :cached)
|
48
|
-
options.reverse_merge! ::PbbuilderTemplate.template_lookup_options
|
49
|
-
|
50
|
-
collection = options[:collection] || []
|
51
|
-
partial = options[:partial]
|
52
|
-
|
53
|
-
# The way recursive rendering works is that CollectionRenderer needs to be aware of node its currently rendering and parent node,
|
54
|
-
# these is no need to know entire "stack" of nodes. CollectionRenderer would traverse to bottom node render that first and then go up in stack.
|
55
|
-
|
56
|
-
# CollectionRenderer uses locals[:pb] to render the partial as a protobuf message,
|
57
|
-
# but also needs locals[:pb_parent] to apply rendered partial to top level protobuf message.
|
58
|
-
|
59
|
-
# This logic could be found in CollectionRenderer#build_rendered_collection method that we over wrote.
|
60
|
-
options[:locals].merge!(pb: ::PbbuilderTemplate.new(@context, new_message_for(field)))
|
61
|
-
options[:locals].merge!(pb_parent: self)
|
62
|
-
options[:locals].merge!(field: field)
|
52
|
+
# example syntax that should end up here:
|
53
|
+
# pb.friends partial: "racers/racer", as: :racer, collection: [Racer.new(1, "Johnny Test", []), Racer.new(2, "Max Verstappen", [])]
|
63
54
|
|
64
|
-
|
65
|
-
raise ::NotImplementedError, "The `:layout' option is not supported in collection rendering."
|
66
|
-
end
|
67
|
-
|
68
|
-
if options.has_key?(:spacer_template)
|
69
|
-
raise ::NotImplementedError, "The `:spacer_template' option is not supported in collection rendering."
|
70
|
-
end
|
71
|
-
|
72
|
-
CollectionRenderer
|
73
|
-
.new(@context.lookup_context, options) { |&block| _scope(message[field.to_s],&block) }
|
74
|
-
.render_collection_with_partial(collection, partial, @context, nil)
|
55
|
+
_render_collection_with_options(field, kwargs[:collection], kwargs)
|
75
56
|
else
|
57
|
+
# # example syntax that should end up here:
|
76
58
|
# pb.best_friend partial: "person", person: @best_friend
|
77
|
-
|
59
|
+
|
78
60
|
super(field, *args) do
|
79
61
|
_render_partial_with_options(kwargs)
|
80
62
|
end
|
81
63
|
end
|
82
64
|
else
|
83
|
-
|
65
|
+
if args.one? && kwargs.has_key?(:collection) && kwargs.has_key?(:as)
|
66
|
+
# example syntax that should end up here:
|
67
|
+
# pb.friends "racers/racer", as: :racer, collection: [Racer.new(1, "Johnny Test", []), Racer.new(2, "Max Verstappen", [])]
|
68
|
+
_render_collection_with_options(field, kwargs[:collection], kwargs.merge(partial: args.first))
|
69
|
+
else
|
70
|
+
super
|
71
|
+
end
|
84
72
|
end
|
85
73
|
end
|
86
74
|
|
@@ -119,10 +107,42 @@ class PbbuilderTemplate < Pbbuilder
|
|
119
107
|
|
120
108
|
private
|
121
109
|
|
110
|
+
# Uses ActionView::CollectionRenderer to render collection effectively and to use rails built in fragment caching support.
|
111
|
+
#
|
112
|
+
# The way recursive rendering works is that the CollectionRenderer needs to be aware of the node its currently rendering and parent node.
|
113
|
+
# There is no need to know the entire "stack" of nodes. ActionView::CollectionRenderer would traverse to bottom node render it first and then go one leve up in stack,
|
114
|
+
# rince and repeat until entire stack is rendered.
|
115
|
+
|
116
|
+
# CollectionRenderer uses locals[:pb] to render the partial as a protobuf message,
|
117
|
+
# but also needs locals[:pb_parent] to apply the rendered partial to the top level protobuf message.
|
118
|
+
|
119
|
+
# This logic can be found in CollectionRenderer#build_rendered_collection method that we overwrote.
|
120
|
+
def _render_collection_with_options(field, collection, options)
|
121
|
+
partial = options[:partial]
|
122
|
+
|
123
|
+
options.reverse_merge! locals: options.except(:partial, :as, :collection, :cached)
|
124
|
+
options.reverse_merge! ::PbbuilderTemplate.template_lookup_options
|
125
|
+
|
126
|
+
options[:locals].merge!(pb: ::PbbuilderTemplate.new(@context, new_message_for(field)))
|
127
|
+
options[:locals].merge!(pb_parent: self)
|
128
|
+
options[:locals].merge!(field: field)
|
129
|
+
|
130
|
+
if options.has_key?(:layout)
|
131
|
+
raise ::NotImplementedError, "The `:layout' option is not supported in collection rendering."
|
132
|
+
end
|
133
|
+
|
134
|
+
if options.has_key?(:spacer_template)
|
135
|
+
raise ::NotImplementedError, "The `:spacer_template' option is not supported in collection rendering."
|
136
|
+
end
|
137
|
+
|
138
|
+
CollectionRenderer
|
139
|
+
.new(@context.lookup_context, options) { |&block| _scope(message[field.to_s],&block) }
|
140
|
+
.render_collection_with_partial(collection, partial, @context, nil)
|
141
|
+
end
|
142
|
+
|
122
143
|
# Writes to cache, if cache with keys is missing.
|
123
144
|
#
|
124
145
|
# @return fragment value
|
125
|
-
|
126
146
|
def _cache_fragment_for(key, options, &block)
|
127
147
|
key = _cache_key(key, options)
|
128
148
|
_read_fragment_cache(key, options) || _write_fragment_cache(key, options, &block)
|
data/lib/pbbuilder.rb
CHANGED
@@ -57,12 +57,14 @@ class Pbbuilder
|
|
57
57
|
::Kernel.raise ::ArgumentError, "can't pass block to non-message field" unless descriptor.type == :message
|
58
58
|
|
59
59
|
if descriptor.label == :repeated
|
60
|
-
#
|
60
|
+
# example syntax that should end up here:
|
61
|
+
# pb.field @array { |element| pb.name element.name }
|
61
62
|
::Kernel.raise ::ArgumentError, "wrong number of arguments #{args.length} (expected 1)" unless args.length == 1
|
62
63
|
collection = args.first
|
63
64
|
_append_repeated(name, descriptor, collection, &block)
|
64
65
|
else
|
65
|
-
#
|
66
|
+
# example syntax that should end up here:
|
67
|
+
# pb.field { pb.name "hello" }
|
66
68
|
::Kernel.raise ::ArgumentError, "wrong number of arguments (expected 0)" unless args.empty?
|
67
69
|
message = (@message[name] ||= _new_message_from_descriptor(descriptor))
|
68
70
|
_scope(message, &block)
|
@@ -71,7 +73,8 @@ class Pbbuilder
|
|
71
73
|
arg = args.first
|
72
74
|
if descriptor.label == :repeated
|
73
75
|
if arg.respond_to?(:to_hash)
|
74
|
-
#
|
76
|
+
# example syntax that should end up here:
|
77
|
+
# pb.fields {"one" => "two"}
|
75
78
|
arg.to_hash.each { |k, v| @message[name][k] = v }
|
76
79
|
elsif arg.respond_to?(:to_ary) && !descriptor.type.eql?(:message)
|
77
80
|
# pb.fields ["one", "two"]
|
@@ -79,20 +82,23 @@ class Pbbuilder
|
|
79
82
|
|
80
83
|
@message[name].concat arg.to_ary
|
81
84
|
elsif arg.respond_to?(:to_ary) && descriptor.type.eql?(:message)
|
82
|
-
#
|
83
|
-
#
|
85
|
+
# example syntax that should end up here:
|
86
|
+
# pb.friends [Person.new(name: "Johnny Test"), Person.new(name: "Max Verstappen")]
|
84
87
|
|
85
88
|
args.flatten.each {|obj| @message[name].push descriptor.subtype.msgclass.new(obj)}
|
86
89
|
else
|
87
|
-
#
|
90
|
+
# example syntax that should end up here:
|
91
|
+
# pb.fields "one"
|
88
92
|
@message[name].push arg
|
89
93
|
end
|
90
94
|
else
|
91
|
-
#
|
95
|
+
# example syntax that should end up here:
|
96
|
+
# pb.field "value"
|
92
97
|
@message[name] = arg
|
93
98
|
end
|
94
99
|
else
|
95
|
-
#
|
100
|
+
# example syntax that should end up here:
|
101
|
+
# pb.field @value, :id, :name, :url
|
96
102
|
element = args.shift
|
97
103
|
if descriptor.label == :repeated
|
98
104
|
# If the message field that's being assigned is a repeated field, then we assume that `element` is enumerable.
|
data/pbbuilder.gemspec
CHANGED
@@ -54,6 +54,18 @@ class PbbuilderTemplateTest < ActiveSupport::TestCase
|
|
54
54
|
assert_equal "https://google.com/test3.svg", result.friends.first.friends.first.friends.first.logo.url
|
55
55
|
end
|
56
56
|
|
57
|
+
test "collection partial with fragment caching enabled" do
|
58
|
+
template = <<-PBBUILDER
|
59
|
+
racers = [Racer.new(1, "Johnny Test", [], nil, API::Asset.new(url: "https://google.com/test1.svg")), Racer.new(2, "Max Verstappen", [])]
|
60
|
+
pb.friends partial: "racers/racer", as: :racer, collection: racers, cached: true
|
61
|
+
PBBUILDER
|
62
|
+
result = render(template)
|
63
|
+
|
64
|
+
assert_equal 2, result.friends.count
|
65
|
+
assert_nil result.logo
|
66
|
+
assert_equal "https://google.com/test1.svg", result.friends.first.logo.url
|
67
|
+
end
|
68
|
+
|
57
69
|
test "CollectionRenderer: raises an error on a render with :layout option" do
|
58
70
|
error = assert_raises NotImplementedError do
|
59
71
|
render('pb.friends partial: "racers/racer", as: :racer, layout: "layout", collection: [Racer.new(1, "Johnny Test", []), Racer.new(2, "Max Verstappen", [])]')
|
@@ -356,9 +368,10 @@ class PbbuilderTemplateTest < ActiveSupport::TestCase
|
|
356
368
|
|
357
369
|
view = ActionView::Base.with_empty_template_cache.new(lookup_context, assigns, controller)
|
358
370
|
|
359
|
-
def view.view_cache_dependencies
|
360
|
-
|
361
|
-
end
|
371
|
+
def view.view_cache_dependencies; [] end
|
372
|
+
def view.combined_fragment_cache_key(key) [ key ] end
|
373
|
+
def view.cache_fragment_name(key, *) key end
|
374
|
+
def view.fragment_name_with_digest(key) key end
|
362
375
|
|
363
376
|
view
|
364
377
|
end
|
data/test/test_helper.rb
CHANGED
@@ -58,6 +58,11 @@ end
|
|
58
58
|
class Racer < Struct.new(:id, :name, :friends, :best_friend, :logo)
|
59
59
|
extend ActiveModel::Naming
|
60
60
|
include ActiveModel::Conversion
|
61
|
+
|
62
|
+
# Fragment caching needs to know, if record could be persisted. We set it to false, this is a default in ActiveModel::API.
|
63
|
+
def persisted?
|
64
|
+
false
|
65
|
+
end
|
61
66
|
end
|
62
67
|
|
63
68
|
Mime::Type.register "application/vnd.google.protobuf", :pb, [], %w(pb)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pbbuilder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.16.
|
4
|
+
version: 0.16.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bouke van der Bijl
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-03-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: google-protobuf
|
@@ -123,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
requirements: []
|
126
|
-
rubygems_version: 3.
|
126
|
+
rubygems_version: 3.5.3
|
127
127
|
signing_key:
|
128
128
|
specification_version: 4
|
129
129
|
summary: Generate Protobuf Messages with a simple DSL similar to JBuilder
|