pbbuilder 0.14.0 → 0.15.1

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: 8d829e7e9831b4b7190db4aa3d426a68b6073156895479d243321692bd653fea
4
- data.tar.gz: 72498247f4a3ee30e403e01ed01be4cd68b17c369e5c10ca83a6be97b0397b6a
3
+ metadata.gz: dc165db83a23b9c5bf32a6921b3cd959171083d48f7a6887df8c524f9920d574
4
+ data.tar.gz: 7d3631f19dea340592918300b66e6d024889fe2c18776c729d4bebdb9424ad4b
5
5
  SHA512:
6
- metadata.gz: fbe8929f56b70421a55902c8b30248740e5bfb68b2f14f1017b384a2a5ce9202ed0bed89ae3c4e8aea011e83e2d7ef36856f1d069f3179cad5e71c5b572b1a1e
7
- data.tar.gz: 04dd8c8f46b1f1120e5d3e964cd28efd101dbf44f7f2f08381d7e3e614270b5dc042ff365d944239c9776cdd040ac7add6511c58ab738499f76f9d4e6df2f533
6
+ metadata.gz: ad453cd1f12f8a71f05b98c5d60e34837848f1c4bdbe403d41a407762b980d5f47c6dc16925b01f2927fef427cb82b5a4b23801102fa7a46d280adf1245afe2f
7
+ data.tar.gz: 1b0bb8019d473e1a433e3009ece6976d7a42992e00d207290106fef8a7de3fd16de5c49caaf6dc23a39aab5cdd6369caae06f62b34def0f431e6c16361210e03
data/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@ 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.15.1
7
+ ### Fixed
8
+ - #merge! method to handle repeated unintialized message object
9
+
10
+ ## 0.15.0
11
+ ### Changed
12
+ - #merge! method was refactored to accomodate caching for all data types (especially those that are :repeated)
13
+
6
14
  ## 0.14.0
7
15
  ### Added
8
16
  - Adding `frozen_string_literal: true` to all files.
data/README.md CHANGED
@@ -1,21 +1,26 @@
1
1
  # Pbbuilder
2
2
  PBBuilder generates [Protobuf](https://developers.google.com/protocol-buffers) Messages with a simple DSL similar to [JBuilder](https://rubygems.org/gems/jbuilder) gem.
3
3
 
4
- ## Usage
5
- It basically works exactly like jbuilder. The main difference is that it can use introspection to figure out what kind of protobuf message it needs to create.
4
+ ## Compatibility
5
+ We don't aim to have 100% compatibility with jbuilder gem, but we closely follow jbuilder's API design.
6
+
7
+ | | Jbuilder | Pbbuilder |
8
+ |---|---|---|
9
+ | set! | ✅ | ✅ |
10
+ | cache! | ✅ | ✅ |
11
+ | cache_if! | ✅ | ✅ |
12
+ | cache_root! | ✅| |
13
+ | extract! | ✅ | ✅ |
14
+ | merge! | ✅ | ✅ |
15
+ | deep_format_keys! | ✅ | |
16
+ | child! | ✅ | |
17
+ | array! | ✅ | |
18
+ | ignore_nil! | ✅ | |
6
19
 
20
+ ## Usage
21
+ The main difference is that it can use introspection to figure out what kind of protobuf message it needs to create.
7
22
 
8
- Following Pbbuilder code
9
- ```
10
- person = RPC::Person.new
11
- Pbbuilder.new(person) do |pb|
12
- pb.name "Hello"
13
- pb.friends [1, 2, 3] do |number|
14
- pb.name "Friend ##{number}"
15
- end
16
- end
17
- ```
18
- Would produce this message:
23
+ This is an example `.proto` message.
19
24
 
20
25
  ```
21
26
  message Person {
@@ -24,6 +29,52 @@ message Person {
24
29
  }
25
30
  ```
26
31
 
32
+ The following `.pb` file would generate a message of valid Person type.
33
+ ```
34
+ person = RPC::Person.new
35
+
36
+ Pbbuilder.new(person) do |pb|
37
+ pb.name "Hello"
38
+ pb.friends [1, 2, 3] do |number|
39
+ pb.name "Friend ##{number}"
40
+ end
41
+ end
42
+ ```
43
+
44
+ Under the hood, this DSL is using `method_missing` and `set!` methods. But there are other methods and features to use.
45
+
46
+ ### extract!
47
+ The following `_account.pb.pbbuilder` partial:
48
+ ```
49
+ pb.id account.id
50
+ pb.phone_number account.phone_number
51
+ pb.tag account.tag
52
+ ```
53
+
54
+ could be rewritten to a shorter version with a use of `extract!`.
55
+ ```
56
+ pb.extract! account, :id, :phone_number, :tag
57
+ ```
58
+
59
+ ### Partials
60
+ Given partial `_account.pb.pbuilder`:
61
+
62
+ ```
63
+ pb.name account.name
64
+ pb.registration_date account.created_at
65
+ ```
66
+
67
+ Using partial while passing a variable to it
68
+
69
+ ```
70
+ pb.account partial: "account", account: @account
71
+ ```
72
+
73
+ Here is way to use partials with collection while passing a variable to it
74
+
75
+ ```
76
+ pb.accounts @accounts, partial: "account", as: account
77
+ ```
27
78
 
28
79
  ### Caching
29
80
  Fragment caching is supported, it uses Rails.cache and works like caching in HTML templates:
@@ -42,7 +93,6 @@ pb.cache_if! !admin?, "cache-key", expires_in: 10.minutes do
42
93
  end
43
94
  ```
44
95
 
45
-
46
96
  ## Installation
47
97
  Add this line to your application's Gemfile:
48
98
 
@@ -59,10 +109,20 @@ Or install it yourself as:
59
109
  ```bash
60
110
  $ gem install pbbuilder
61
111
  ```
112
+ ## Development
62
113
 
63
- ## Contributing
114
+ When debugging, make sure to prepend `::Kernel` to any calls such as `puts` as otherwise the code will think you're trying to add another attribute into protobuf object.
64
115
 
65
- When debugging, make sure you're prepending `::Kernel` to any calls such as `puts` as otherwise the code will think you're trying to add another attribute onto the protobuf.
116
+ In case, you're looking to use breakpoints for debugging purposes - it's better to use `pry`. Just make sure to [change pbbuilder superclass from `ProxyObject/BasicObject` to `Object`](lib/pbbuilder/pbbuilder.rb).
117
+
118
+ ## Testing
119
+ Running `bundle exec appraisal rake test` locally will run entire testsuit with all version of rails. To run tests only for certain rails version do the following `bundle exec appraisal rails-7-0 rake test`
120
+
121
+ To run only one tests from file - use `m` utility. Like this:
122
+ `bundle exec appraisal rails-7-0 m test/pbbuilder_template_test.rb:182`
123
+
124
+ ## Contributing
125
+ Everyone is welcome to contribute.
66
126
 
67
127
  ## License
68
128
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/lib/pbbuilder.rb CHANGED
@@ -116,33 +116,63 @@ class Pbbuilder
116
116
  ::Kernel.raise Pbbuilder::MergeError.build(target!, object) unless object.class == ::Hash
117
117
 
118
118
  object.each_key do |key|
119
- if object[key].respond_to?(:empty?) && object[key].empty?
120
- ::Kernel.raise Pbbuilder::MergeError.build(target!, object)
121
- end
119
+ next if object[key].respond_to?(:empty?) && object[key].empty?
122
120
 
123
- if object[key].class == ::String
124
- # pb.fields {"one" => "two"}
125
- @message[key.to_s] = object[key]
126
- elsif object[key].class == ::Array
127
- # pb.tags ['test', 'ok']
128
- @message[key.to_s].replace object[key]
129
- elsif object[key].class == ::TrueClass || object[key].class == ::FalseClass
130
- # pb.boolean true || false
131
- @message[key.to_s] = object[key]
132
- elsif ( obj = object[key]).class == ::Hash
133
- # pb.field_name do
134
- # pb.tags ["ok", "cool"]
135
- # end
136
- #
121
+ descriptor = @message.class.descriptor.lookup(key.to_s)
122
+ ::Kernel.raise ::ArgumentError, "Unknown field #{name}" if descriptor.nil?
137
123
 
124
+ if descriptor.label == :repeated
138
125
  # optional empty fields don't show up in @message object,
139
126
  # we recreate empty message, so we can fill it with values
140
127
  if @message[key.to_s].nil?
141
- field_descriptor = @message.class.descriptor.lookup(key.to_s)
142
- @message[key.to_s] = _new_message_from_descriptor(field_descriptor)
128
+ @message[key.to_s] = _new_message_from_descriptor(descriptor)
143
129
  end
144
130
 
145
- @message[key.to_s] = _scope(@message[key.to_s]) { self.merge!(obj) }
131
+ if object[key].respond_to?(:to_hash)
132
+ object[key].to_hash.each {|k, v| @message[key.to_s][k] = v}
133
+ elsif object[key].respond_to?(:to_ary)
134
+ elements = object[key].map do |obj|
135
+ descriptor.subtype ? descriptor.subtype.msgclass.new(obj) : obj
136
+ end
137
+
138
+ @message[key.to_s].replace(elements)
139
+ end
140
+ else
141
+ if object[key].class == ::String
142
+ # pb.fields {"one" => "two"}
143
+ @message[key.to_s] = object[key]
144
+ elsif object[key].class == ::TrueClass || object[key].class == ::FalseClass
145
+ # pb.boolean true || false
146
+ @message[key.to_s] = object[key]
147
+ elsif object[key].class == ::Array
148
+ # pb.field_name do
149
+ # pb.tags ["ok", "cool"]
150
+ # end
151
+
152
+ @message[key.to_s] = object[key]
153
+ elsif object[key].class == ::Hash
154
+ if @message[key.to_s].nil?
155
+ @message[key.to_s] = _new_message_from_descriptor(descriptor)
156
+ end
157
+
158
+ object[key].each do |k, v|
159
+ if object[key][k].respond_to?(:to_hash)
160
+ if @message[key.to_s][k.to_s].nil?
161
+ descriptor = @message[key.to_s].class.descriptor.lookup(k.to_s)
162
+ @message[key.to_s][k.to_s] = _new_message_from_descriptor(descriptor)
163
+ end
164
+
165
+ _scope(@message[key.to_s][k.to_s]) { self.merge!(object[key][k]) }
166
+ elsif object[key][k].respond_to?(:to_ary)
167
+ @message[key.to_s][k.to_s].replace object[key][k]
168
+ else
169
+ # Throws an error, if we try to merge nil object into empty value.
170
+ next if object[key][k].nil? && @message[key.to_s][k.to_s].nil?
171
+
172
+ @message[key.to_s][k.to_s] = object[key][k]
173
+ end
174
+ end
175
+ end
146
176
  end
147
177
  end
148
178
  end
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.14.0"
5
+ spec.version = "0.15.1"
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"
@@ -103,19 +103,51 @@ class PbbuilderTemplateTest < ActiveSupport::TestCase
103
103
  end
104
104
 
105
105
  test "should raise Error in merge! an empty hash" do
106
- assert_raise(ActionView::Template::Error) {
106
+ assert_nothing_raised {
107
107
  render(<<-PBBUILDER)
108
108
  pb.merge! "name" => {}
109
109
  PBBUILDER
110
110
  }
111
111
 
112
- assert_raise(ActionView::Template::Error) {
112
+ assert_nothing_raised {
113
113
  render(<<-PBBUILDER)
114
114
  pb.merge! "" => {}
115
115
  PBBUILDER
116
116
  }
117
117
  end
118
118
 
119
+ test "caching a message object" do
120
+ template = <<-PBBUILDER
121
+ pb.cache! "some-random-key-again" do
122
+ pb.best_friend do
123
+ pb.name "Max Verstappen"
124
+ pb.logo do
125
+ pb.url('https://google.com/image.jpg')
126
+ pb.url_2x('https://google.com/image.jpg')
127
+ pb.url_3x('https://google.com/image.jpg')
128
+ end
129
+ end
130
+ pb.logo do
131
+ pb.url('https://google.com/image.jpg')
132
+ pb.url_2x('https://google.com/image.jpg')
133
+ pb.url_3x('https://google.com/image.jpg')
134
+ end
135
+ end
136
+ PBBUILDER
137
+
138
+ assert_nothing_raised { render(template) }
139
+
140
+ result = render('pb.cache! "some-random-key-again" do; end ')
141
+
142
+ assert_equal('https://google.com/image.jpg', result.logo.url)
143
+ assert_equal('https://google.com/image.jpg', result.logo.url_2x)
144
+ assert_equal('https://google.com/image.jpg', result.logo.url_3x)
145
+
146
+ assert_equal('https://google.com/image.jpg', result.best_friend.logo.url)
147
+ assert_equal('https://google.com/image.jpg', result.best_friend.logo.url_2x)
148
+ assert_equal('https://google.com/image.jpg', result.best_friend.logo.url_3x)
149
+ end
150
+
119
151
  test "empty fragment caching" do
120
152
  render 'pb.cache! "nothing" do; end'
121
153
 
@@ -131,6 +163,39 @@ class PbbuilderTemplateTest < ActiveSupport::TestCase
131
163
  assert_equal "suslik", result["name"]
132
164
  end
133
165
 
166
+ test "caching repeated partial" do
167
+ template = <<-PBBUILDER
168
+ pb.cache! "some-random-key" do
169
+ pb.friends @friends, partial: "racers/racer", as: :racer
170
+ end
171
+ PBBUILDER
172
+ friends = [Racer.new(1, "Johnny Test", []), Racer.new(2, "Max Verstappen", [])]
173
+
174
+ result = assert_nothing_raised { render(template, friends: friends) }
175
+ assert_equal("Johnny Test", result.friends[0].name)
176
+ assert_equal("Max Verstappen", result.friends[1].name)
177
+
178
+ result = render('pb.cache! "some-random-key" do; end ')
179
+ assert_equal("Johnny Test", result.friends[0].name)
180
+ assert_equal("Max Verstappen", result.friends[1].name)
181
+ end
182
+
183
+ test "caching map values" do
184
+ template = <<-PBBUILDER
185
+ pb.cache! "some-random-cache-key" do
186
+ pb.favourite_foods @foods
187
+ end
188
+ PBBUILDER
189
+
190
+ r = assert_nothing_raised { render( template, foods: {'pizza' => 'yes', 'borsh' => 'false'})}
191
+ assert_equal('false', r.favourite_foods['borsh'])
192
+ assert_equal('yes', r.favourite_foods['pizza'])
193
+
194
+ result = render('pb.cache! "some-random-cache-key" do; end ')
195
+ assert_equal('false', result.favourite_foods['borsh'])
196
+ assert_equal('yes', result.favourite_foods['pizza'])
197
+ end
198
+
134
199
  test "object fragment caching" do
135
200
  render(<<-PBBUILDER)
136
201
  pb.cache! "cache-key" do
data/test/test_helper.rb CHANGED
@@ -17,6 +17,7 @@ require "google/protobuf"
17
17
  require "google/protobuf/field_mask_pb"
18
18
 
19
19
  require "active_support/testing/autorun"
20
+ require "pry"
20
21
 
21
22
  ActiveSupport.test_order = :random
22
23
 
@@ -32,6 +33,13 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
32
33
  repeated :tags, :string, 7
33
34
  optional :last_name, :string, 8
34
35
  optional :boolean_me, :bool, 9
36
+ optional :logo, :message, 10, "pbbuildertest.Asset"
37
+ end
38
+
39
+ add_message "pbbuildertest.Asset" do
40
+ optional :url, :string, 1
41
+ optional :url_2x, :string, 2
42
+ optional :url_3x, :string, 3
35
43
  end
36
44
  end
37
45
  end
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.14.0
4
+ version: 0.15.1
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-03-16 00:00:00.000000000 Z
11
+ date: 2023-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: google-protobuf