pbbuilder 0.1.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 +7 -0
- data/.gitignore +12 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +13 -0
- data/bin/test +5 -0
- data/lib/pbbuilder.rb +118 -0
- data/lib/pbbuilder/handler.rb +12 -0
- data/lib/pbbuilder/railtie.rb +11 -0
- data/lib/tasks/pbbuilder_tasks.rake +4 -0
- data/pbbuilder.gemspec +15 -0
- data/test/pbbuilder_test.rb +29 -0
- data/test/test_helper.rb +23 -0
- metadata +86 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 95c1a8225cbe27f9523fe58f6e0a706108ae12839d1b091b6a2a70d02d5363cc
|
4
|
+
data.tar.gz: b133c221d7f27e76626632c488309c4c1d8c929e1725d34298847c42ad5f7350
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 033030a337a7b3a7e334719d564dfe098c7e1f7b54e8cea18ace761d51adb091c7fbe0b6b42d527ebc1e3b622e8d3332f4400a1444e263b55c2a95cf9a8e92fd
|
7
|
+
data.tar.gz: 76f971940ae26fcd1360552cec8e5e9bb3160f21a39e36184d2bc700524f480c1b5261781dd760be5d33d702c4e2a95739e5f32d890d348f6f698dc0ee1a315f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2021 Bouke van der Bijl
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Pbbuilder
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
How to use my plugin.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'pbbuilder'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install pbbuilder
|
22
|
+
```
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
Contribution directions go here.
|
26
|
+
|
27
|
+
## License
|
28
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/test
ADDED
data/lib/pbbuilder.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
# Pbbuilder makes it easy to create a protobuf message using the builder pattern
|
2
|
+
# It is heavily inspired by jbuilder
|
3
|
+
#
|
4
|
+
# Given this example message definition:
|
5
|
+
# message Person {
|
6
|
+
# string name = 1;
|
7
|
+
# repeated Person friends = 2;
|
8
|
+
# }
|
9
|
+
#
|
10
|
+
# You could use Pbbuilder as follows:
|
11
|
+
# person = RPC::Person.new
|
12
|
+
# Pbbuilder.new(person) do |pb|
|
13
|
+
# pb.name "Hello"
|
14
|
+
# pb.friends [1, 2, 3] do |number|
|
15
|
+
# pb.name "Friend ##{number}"
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# message.name => "Hello"
|
20
|
+
#
|
21
|
+
# It basically works exactly like jbuilder. The main difference is that it can use introspection to figure out what kind
|
22
|
+
# of protobuf message it needs to create.
|
23
|
+
|
24
|
+
class Pbbuilder
|
25
|
+
def initialize(message)
|
26
|
+
@message = message
|
27
|
+
|
28
|
+
yield self if ::Kernel.block_given?
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing(field, *args, &block)
|
32
|
+
set!(field, *args, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def respond_to_missing?(field)
|
36
|
+
!!@message.class.descriptor.lookup(field.to_s)
|
37
|
+
end
|
38
|
+
|
39
|
+
def set!(field, *args, &block)
|
40
|
+
name = field.to_s
|
41
|
+
descriptor = @message.class.descriptor.lookup(name)
|
42
|
+
|
43
|
+
if block
|
44
|
+
raise ::ArgumentError, "can't pass block to non-message field" unless descriptor.type == :message
|
45
|
+
|
46
|
+
if descriptor.label == :repeated
|
47
|
+
# pb.field @array { |element| pb.name = element.name }
|
48
|
+
raise ::ArgumentError, "wrong number of arguments (expected 1)" unless args.length == 1
|
49
|
+
collection = args.first
|
50
|
+
_append_repeated(name, descriptor, collection, &block)
|
51
|
+
return
|
52
|
+
end
|
53
|
+
|
54
|
+
raise ::ArgumentError, "wrong number of arguments (expected 0)" unless args.empty?
|
55
|
+
# pb.field { pb.name = "hello" }
|
56
|
+
message = (@message[name] ||= _new_message_from_descriptor(descriptor))
|
57
|
+
_scope(message, &block)
|
58
|
+
elsif args.length == 1
|
59
|
+
@message[name] = args.first
|
60
|
+
else
|
61
|
+
# pb.field @value, :id, :name, :url
|
62
|
+
element = args.shift
|
63
|
+
if descriptor.label == :repeated
|
64
|
+
# If the message field that's being assigned is a repeated field, then we assume that `element` is enumerable.
|
65
|
+
# This way you can do something like pb.repeated_field @array, :id, :name
|
66
|
+
# This will create a message out of every object in @array, copying over the :id and :name values.
|
67
|
+
set!(name, element) do |item|
|
68
|
+
extract!(item, *args)
|
69
|
+
end
|
70
|
+
else
|
71
|
+
extract!(element, *args)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def extract!(element, *args)
|
77
|
+
args.each do |arg|
|
78
|
+
value = element.send(arg)
|
79
|
+
@message[arg.to_s] = value
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def target!
|
84
|
+
@message
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def _append_repeated(name, descriptor, collection, &block)
|
90
|
+
raise ::ArgumentError, "expected Enumerable" unless collection.respond_to?(:map)
|
91
|
+
elements = collection.map do |element|
|
92
|
+
message = _new_message_from_descriptor(descriptor)
|
93
|
+
_scope(message) { block.call(element) }
|
94
|
+
end
|
95
|
+
|
96
|
+
@message[name].push(*elements)
|
97
|
+
end
|
98
|
+
|
99
|
+
def _scope(message)
|
100
|
+
old_message = @message
|
101
|
+
@message = message
|
102
|
+
yield
|
103
|
+
message
|
104
|
+
ensure
|
105
|
+
@message = old_message
|
106
|
+
end
|
107
|
+
|
108
|
+
def _new_message_from_descriptor(descriptor)
|
109
|
+
raise ::ArgumentError, "can't pass block to non-message field" unless descriptor.type == :message
|
110
|
+
|
111
|
+
# Here we're using Protobuf reflection to create an instance of the message class
|
112
|
+
message_descriptor = descriptor.subtype
|
113
|
+
message_class = message_descriptor.msgclass
|
114
|
+
message_class.new
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
require "pbbuilder/railtie" if defined?(Rails)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Basically copied and pasted from JbuilderHandler, except it uses Pbbuilder
|
2
|
+
|
3
|
+
class PbbuilderHandler
|
4
|
+
# This builds up a Ruby string, that Rails' templating system `eval`s to create the view result.
|
5
|
+
# In our case the view result is a Protobuf message.
|
6
|
+
def self.call(template, source = nil)
|
7
|
+
source ||= template.source
|
8
|
+
# We need to keep `source` on the first line, so line numbers are correct if there's an error
|
9
|
+
%{pb=Pbbuilder.new(response_class.new); #{source}
|
10
|
+
pb.target!}
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "pbbuilder/handler"
|
2
|
+
|
3
|
+
class Pbbuilder
|
4
|
+
class Railtie < ::Rails::Railtie
|
5
|
+
initializer :register_handler do
|
6
|
+
ActiveSupport.on_load :action_view do
|
7
|
+
ActionView::Template.register_template_handler :pbbuilder, PbbuilderHandler
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/pbbuilder.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = "pbbuilder"
|
3
|
+
spec.version = "0.1.0"
|
4
|
+
spec.authors = ["Bouke van der Bijl"]
|
5
|
+
spec.email = ["bouke@cheddar.me"]
|
6
|
+
spec.homepage = "https://github.com/cheddar-me/pbbuilder"
|
7
|
+
spec.summary = "Generate Protobuf messages via a Builder-style DSL"
|
8
|
+
spec.license = "MIT"
|
9
|
+
|
10
|
+
spec.files = `git ls-files`.split("\n")
|
11
|
+
spec.test_files = `git ls-files -- test/*`.split("\n")
|
12
|
+
|
13
|
+
spec.add_dependency "rails", "~> 6.1.3"
|
14
|
+
spec.add_dependency "google-protobuf", "~> 3.15.5"
|
15
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "pbbuilder"
|
3
|
+
|
4
|
+
class PbbuilderTest < ActiveSupport::TestCase
|
5
|
+
test "it makes it possible to create a person" do
|
6
|
+
person = Pbbuilder.new(API::Person.new) do |pb|
|
7
|
+
pb.name "Hello world"
|
8
|
+
pb.friends 1..3 do |number|
|
9
|
+
pb.name "Friend ##{number}"
|
10
|
+
end
|
11
|
+
end.target!
|
12
|
+
assert_equal person.name, "Hello world"
|
13
|
+
assert_equal person.friends.first.name, "Friend #1"
|
14
|
+
end
|
15
|
+
|
16
|
+
test "it can extract fields in a nice way" do
|
17
|
+
klass = Struct.new(:name)
|
18
|
+
friends = [klass.new("Friend 1"), klass.new("Friend 2")]
|
19
|
+
person = Pbbuilder.new(API::Person.new) do |pb|
|
20
|
+
pb.name "Hello world"
|
21
|
+
pb.friends friends, :name
|
22
|
+
end.target!
|
23
|
+
|
24
|
+
assert_equal person.name, "Hello world"
|
25
|
+
assert_equal person.friends.size, 2
|
26
|
+
assert_equal person.friends.first.name, "Friend 1"
|
27
|
+
assert_equal person.friends.last.name, "Friend 2"
|
28
|
+
end
|
29
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# Configure Rails Environment
|
2
|
+
ENV["RAILS_ENV"] = "test"
|
3
|
+
|
4
|
+
require "rails"
|
5
|
+
require "rails/test_help"
|
6
|
+
require "rails/test_unit/reporter"
|
7
|
+
|
8
|
+
Rails::TestUnitReporter.executable = "bin/test"
|
9
|
+
|
10
|
+
require "google/protobuf"
|
11
|
+
|
12
|
+
Google::Protobuf::DescriptorPool.generated_pool.build do
|
13
|
+
add_file("pbbuilder.proto", syntax: :proto3) do
|
14
|
+
add_message "pbbuildertest.Person" do
|
15
|
+
optional :name, :string, 1
|
16
|
+
repeated :friends, :message, 2, "pbbuildertest.Person"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module API
|
22
|
+
Person = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("pbbuildertest.Person").msgclass
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pbbuilder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bouke van der Bijl
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-03-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 6.1.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 6.1.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: google-protobuf
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.15.5
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.15.5
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- bouke@cheddar.me
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- Gemfile
|
50
|
+
- MIT-LICENSE
|
51
|
+
- README.md
|
52
|
+
- Rakefile
|
53
|
+
- bin/test
|
54
|
+
- lib/pbbuilder.rb
|
55
|
+
- lib/pbbuilder/handler.rb
|
56
|
+
- lib/pbbuilder/railtie.rb
|
57
|
+
- lib/tasks/pbbuilder_tasks.rake
|
58
|
+
- pbbuilder.gemspec
|
59
|
+
- test/pbbuilder_test.rb
|
60
|
+
- test/test_helper.rb
|
61
|
+
homepage: https://github.com/cheddar-me/pbbuilder
|
62
|
+
licenses:
|
63
|
+
- MIT
|
64
|
+
metadata: {}
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubygems_version: 3.2.3
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: Generate Protobuf messages via a Builder-style DSL
|
84
|
+
test_files:
|
85
|
+
- test/pbbuilder_test.rb
|
86
|
+
- test/test_helper.rb
|