pbbuilder 0.13.3 → 0.15.0
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/Gemfile +2 -0
- data/README.md +76 -16
- data/Rakefile +2 -0
- data/lib/pbbuilder/errors.rb +2 -0
- data/lib/pbbuilder/handler.rb +2 -0
- data/lib/pbbuilder/pbbuilder.rb +2 -0
- data/lib/pbbuilder/protobuf_extension.rb +2 -0
- data/lib/pbbuilder/railtie.rb +2 -0
- data/lib/pbbuilder/template.rb +2 -0
- data/lib/pbbuilder.rb +47 -20
- data/pbbuilder.gemspec +3 -1
- data/test/pbbuilder_template_test.rb +37 -2
- data/test/pbbuilder_test.rb +2 -0
- data/test/protobuf_extension_test.rb +2 -0
- data/test/test_helper.rb +3 -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: e89f023f27d95d8d11cdf454148ef955b0234b6d7e59f40e30859e09c02761cb
|
4
|
+
data.tar.gz: 108693d16d963bc46ad96d3f79ed4b8078f8efc9c3a7f798c32b7bdd5cc98e73
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa478fc7fad5954801385b731b7fc77296d732e8f13a6bc5688417dfdeb247a7135f3ecbce14f88abc441a87cbc6ad669d3f5547b917f80bfddfbac7e0b2c9a8
|
7
|
+
data.tar.gz: 2fe79ab3cfde6b908907d55eaf37054cb0b530f4e98dbf44d732bef82c46054f0efd4c6ba3a05827915cc855f9cc70a671adc2316c691edd5147ababec573ec6
|
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.0
|
7
|
+
### Changed
|
8
|
+
- #merge! method was refactored to accomodate caching for all data types (especially those that are :repeated)
|
9
|
+
|
10
|
+
## 0.14.0
|
11
|
+
### Added
|
12
|
+
- Adding `frozen_string_literal: true` to all files.
|
13
|
+
|
6
14
|
## 0.13.2 2023.02.3
|
7
15
|
### Fixed
|
8
16
|
- In case ActiveSupport::Cache::FileStore in Rails is used as a cache, File.atomic_write can have a race condition and fail to rename temporary file. We're attempting to recover from that, by catching this specific error and returning a value.
|
data/Gemfile
CHANGED
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/Rakefile
CHANGED
data/lib/pbbuilder/errors.rb
CHANGED
data/lib/pbbuilder/handler.rb
CHANGED
data/lib/pbbuilder/pbbuilder.rb
CHANGED
data/lib/pbbuilder/railtie.rb
CHANGED
data/lib/pbbuilder/template.rb
CHANGED
data/lib/pbbuilder.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "pbbuilder/pbbuilder"
|
2
4
|
require 'pbbuilder/errors'
|
3
5
|
require "pbbuilder/protobuf_extension"
|
@@ -114,33 +116,58 @@ class Pbbuilder
|
|
114
116
|
::Kernel.raise Pbbuilder::MergeError.build(target!, object) unless object.class == ::Hash
|
115
117
|
|
116
118
|
object.each_key do |key|
|
117
|
-
if object[key].respond_to?(:empty?) && object[key].empty?
|
118
|
-
::Kernel.raise Pbbuilder::MergeError.build(target!, object)
|
119
|
-
end
|
119
|
+
next if object[key].respond_to?(:empty?) && object[key].empty?
|
120
120
|
|
121
|
-
|
122
|
-
|
123
|
-
@message[key.to_s] = object[key]
|
124
|
-
elsif object[key].class == ::Array
|
125
|
-
# pb.tags ['test', 'ok']
|
126
|
-
@message[key.to_s].replace object[key]
|
127
|
-
elsif object[key].class == ::TrueClass || object[key].class == ::FalseClass
|
128
|
-
# pb.boolean true || false
|
129
|
-
@message[key.to_s] = object[key]
|
130
|
-
elsif ( obj = object[key]).class == ::Hash
|
131
|
-
# pb.field_name do
|
132
|
-
# pb.tags ["ok", "cool"]
|
133
|
-
# end
|
134
|
-
#
|
121
|
+
descriptor = @message.class.descriptor.lookup(key.to_s)
|
122
|
+
::Kernel.raise ::ArgumentError, "Unknown field #{name}" if descriptor.nil?
|
135
123
|
|
124
|
+
if descriptor.label == :repeated
|
136
125
|
# optional empty fields don't show up in @message object,
|
137
126
|
# we recreate empty message, so we can fill it with values
|
138
127
|
if @message[key.to_s].nil?
|
139
|
-
|
140
|
-
|
128
|
+
@message[key.to_s] = _new_message_from_descriptor(descriptor)
|
129
|
+
end
|
130
|
+
|
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)
|
141
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
|
142
157
|
|
143
|
-
|
158
|
+
object[key].each do |k, v|
|
159
|
+
if object[key][k].respond_to?(:to_hash)
|
160
|
+
_scope(@message[key.to_s][k.to_s]) { self.merge!(object[key][k]) }
|
161
|
+
elsif object[key][k].respond_to?(:to_ary)
|
162
|
+
@message[key.to_s][k.to_s].replace object[key][k]
|
163
|
+
else
|
164
|
+
# Throws an error, if we try to merge nil object into empty value.
|
165
|
+
next if object[key][k].nil? && @message[key.to_s][k.to_s].nil?
|
166
|
+
|
167
|
+
@message[key.to_s][k.to_s] = object[key][k]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
144
171
|
end
|
145
172
|
end
|
146
173
|
end
|
data/pbbuilder.gemspec
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
Gem::Specification.new do |spec|
|
2
4
|
spec.name = "pbbuilder"
|
3
|
-
spec.version = "0.
|
5
|
+
spec.version = "0.15.0"
|
4
6
|
spec.authors = ["Bouke van der Bijl"]
|
5
7
|
spec.email = ["bouke@cheddar.me"]
|
6
8
|
spec.homepage = "https://github.com/cheddar-me/pbbuilder"
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "test_helper"
|
2
4
|
require "action_view/testing/resolvers"
|
3
5
|
|
@@ -101,13 +103,13 @@ class PbbuilderTemplateTest < ActiveSupport::TestCase
|
|
101
103
|
end
|
102
104
|
|
103
105
|
test "should raise Error in merge! an empty hash" do
|
104
|
-
|
106
|
+
assert_nothing_raised {
|
105
107
|
render(<<-PBBUILDER)
|
106
108
|
pb.merge! "name" => {}
|
107
109
|
PBBUILDER
|
108
110
|
}
|
109
111
|
|
110
|
-
|
112
|
+
assert_nothing_raised {
|
111
113
|
render(<<-PBBUILDER)
|
112
114
|
pb.merge! "" => {}
|
113
115
|
PBBUILDER
|
@@ -129,6 +131,39 @@ class PbbuilderTemplateTest < ActiveSupport::TestCase
|
|
129
131
|
assert_equal "suslik", result["name"]
|
130
132
|
end
|
131
133
|
|
134
|
+
test "caching repeated partial" do
|
135
|
+
template = <<-PBBUILDER
|
136
|
+
pb.cache! "some-random-key" do
|
137
|
+
pb.friends @friends, partial: "racers/racer", as: :racer
|
138
|
+
end
|
139
|
+
PBBUILDER
|
140
|
+
friends = [Racer.new(1, "Johnny Test", []), Racer.new(2, "Max Verstappen", [])]
|
141
|
+
|
142
|
+
result = assert_nothing_raised { render(template, friends: friends) }
|
143
|
+
assert_equal("Johnny Test", result.friends[0].name)
|
144
|
+
assert_equal("Max Verstappen", result.friends[1].name)
|
145
|
+
|
146
|
+
result = render('pb.cache! "some-random-key" do; end ')
|
147
|
+
assert_equal("Johnny Test", result.friends[0].name)
|
148
|
+
assert_equal("Max Verstappen", result.friends[1].name)
|
149
|
+
end
|
150
|
+
|
151
|
+
test "caching map values" do
|
152
|
+
template = <<-PBBUILDER
|
153
|
+
pb.cache! "some-random-cache-key" do
|
154
|
+
pb.favourite_foods @foods
|
155
|
+
end
|
156
|
+
PBBUILDER
|
157
|
+
|
158
|
+
r = assert_nothing_raised { render( template, foods: {'pizza' => 'yes', 'borsh' => 'false'})}
|
159
|
+
assert_equal('false', r.favourite_foods['borsh'])
|
160
|
+
assert_equal('yes', r.favourite_foods['pizza'])
|
161
|
+
|
162
|
+
result = render('pb.cache! "some-random-cache-key" do; end ')
|
163
|
+
assert_equal('false', result.favourite_foods['borsh'])
|
164
|
+
assert_equal('yes', result.favourite_foods['pizza'])
|
165
|
+
end
|
166
|
+
|
132
167
|
test "object fragment caching" do
|
133
168
|
render(<<-PBBUILDER)
|
134
169
|
pb.cache! "cache-key" do
|
data/test/pbbuilder_test.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "bundler/setup"
|
2
4
|
|
3
5
|
require "rails"
|
@@ -15,6 +17,7 @@ require "google/protobuf"
|
|
15
17
|
require "google/protobuf/field_mask_pb"
|
16
18
|
|
17
19
|
require "active_support/testing/autorun"
|
20
|
+
require "pry"
|
18
21
|
|
19
22
|
ActiveSupport.test_order = :random
|
20
23
|
|
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.0
|
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-05-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: google-protobuf
|