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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f4c9ff9f8ec71c8e3fee410430e9c29da1907e6e110ec6869531b52b8ec54ce5
4
- data.tar.gz: 0f75e4c2fbdde5b6c33acc05dd62173b2b399d03c1030c7fd132c460d229b12b
3
+ metadata.gz: 0f61e783abb61817e05f29772b6614314646c0fda6f24a5f73f556ebc19d0f97
4
+ data.tar.gz: 34cf6626e2f60aa50a7ee2e7278a0149557263d023dee6a758e4adf2d8e0d263
5
5
  SHA512:
6
- metadata.gz: '0191dcf320aa97316c483230dda497cc1d83c20f1aac6caa5e4ce4a8883188a949a8217344f04445ab4375b1a4bf952c49eef115af357c87c8127f1c85db3ac7'
7
- data.tar.gz: 287458b4fc6db49f90a0d76e4da273861b58bfd84d49edd86f966198a16cb694319cd47fca69200d5cad5767307fbbe83b2fa8c866519056313b3d435c5e917e
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
- | collection cache | ✅| |
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 support is currently in the works.
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:
@@ -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
- # pb.friends @friends, partial: "friend", as: :friend
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
- # pb.friends partial: "racers/racer", as: :racer, collection: [Racer.new(1, "Johnny Test", []), Racer.new(2, "Max Verstappen", [])]
44
- # collection renderer
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
- if options.has_key?(:layout)
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
- # Call set! as a submessage, passing in the kwargs as partial options
59
+
78
60
  super(field, *args) do
79
61
  _render_partial_with_options(kwargs)
80
62
  end
81
63
  end
82
64
  else
83
- super
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
- # pb.field @array { |element| pb.name element.name }
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
- # pb.field { pb.name "hello" }
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
- # pb.fields {"one" => "two"}
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
- # pb.friends [Person.new(name: "Johnny Test"), Person.new(name: "Max Verstappen")]
83
- # Concat another Protobuf message into parent Protobuf message (not ary of strings)
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
- # pb.fields "one"
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
- # pb.field "value"
95
+ # example syntax that should end up here:
96
+ # pb.field "value"
92
97
  @message[name] = arg
93
98
  end
94
99
  else
95
- # pb.field @value, :id, :name, :url
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "pbbuilder"
5
- spec.version = "0.16.1"
5
+ spec.version = "0.16.2"
6
6
  spec.authors = ["Bouke van der Bijl"]
7
7
  spec.email = ["bouke@cheddar.me"]
8
8
  spec.homepage = "https://github.com/cheddar-me/pbbuilder"
@@ -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.1
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: 2023-11-02 00:00:00.000000000 Z
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.4.20
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