pbbuilder 0.16.1 → 0.16.2

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