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 +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +76 -16
- data/lib/pbbuilder.rb +50 -20
- data/pbbuilder.gemspec +1 -1
- data/test/pbbuilder_template_test.rb +67 -2
- data/test/test_helper.rb +8 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc165db83a23b9c5bf32a6921b3cd959171083d48f7a6887df8c524f9920d574
|
4
|
+
data.tar.gz: 7d3631f19dea340592918300b66e6d024889fe2c18776c729d4bebdb9424ad4b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
##
|
5
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
124
|
-
|
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
|
-
|
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
|
-
|
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
@@ -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
|
-
|
106
|
+
assert_nothing_raised {
|
107
107
|
render(<<-PBBUILDER)
|
108
108
|
pb.merge! "name" => {}
|
109
109
|
PBBUILDER
|
110
110
|
}
|
111
111
|
|
112
|
-
|
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.
|
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-
|
11
|
+
date: 2023-06-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: google-protobuf
|