pbbuilder 0.6.0 → 0.7.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/lib/pbbuilder.rb +8 -7
- data/lib/pbbuilder/handler.rb +4 -2
- data/lib/pbbuilder/template.rb +93 -0
- data/pbbuilder.gemspec +1 -1
- data/test/pbbuilder_template_test.rb +97 -0
- data/test/test_helper.rb +16 -0
- metadata +10 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83ca08d74d2463d2d14d5d57127e482a0ea9fde4a0cdcaac3f237c3a8f48ff23
|
4
|
+
data.tar.gz: 828fe17a1b1be0feab98d7cc68940350df6093d0995751e0873c46d9a363f487
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c4285699991af59101b07e3e04dd987620065362febdc8623d62390b15a17fd2660031f9e3a4cc3eb602609504ab70cde82848012fc061759124a706809fd3a
|
7
|
+
data.tar.gz: 214263e610fc46ab2bf978f17dd3dc8633ae757af8859906c4c588827c00e383b542ac749b5f9e089ede8401153a54629b82e83a29d5c212043965c70035fc58
|
data/lib/pbbuilder.rb
CHANGED
@@ -28,8 +28,8 @@ class Pbbuilder < BasicObject
|
|
28
28
|
yield self if ::Kernel.block_given?
|
29
29
|
end
|
30
30
|
|
31
|
-
def method_missing(
|
32
|
-
set!(
|
31
|
+
def method_missing(...)
|
32
|
+
set!(...)
|
33
33
|
end
|
34
34
|
|
35
35
|
def respond_to_missing?(field)
|
@@ -39,19 +39,20 @@ class Pbbuilder < BasicObject
|
|
39
39
|
def set!(field, *args, &block)
|
40
40
|
name = field.to_s
|
41
41
|
descriptor = @message.class.descriptor.lookup(name)
|
42
|
+
::Kernel.raise ::ArgumentError, "Unknown field #{name}" if descriptor.nil?
|
42
43
|
|
43
44
|
if block
|
44
|
-
raise ::ArgumentError, "can't pass block to non-message field" unless descriptor.type == :message
|
45
|
+
::Kernel.raise ::ArgumentError, "can't pass block to non-message field" unless descriptor.type == :message
|
45
46
|
|
46
47
|
if descriptor.label == :repeated
|
47
48
|
# pb.field @array { |element| pb.name element.name }
|
48
|
-
raise ::ArgumentError, "wrong number of arguments (expected 1)" unless args.length == 1
|
49
|
+
::Kernel.raise ::ArgumentError, "wrong number of arguments #{args.length} (expected 1)" unless args.length == 1
|
49
50
|
collection = args.first
|
50
51
|
_append_repeated(name, descriptor, collection, &block)
|
51
52
|
return
|
52
53
|
end
|
53
54
|
|
54
|
-
raise ::ArgumentError, "wrong number of arguments (expected 0)" unless args.empty?
|
55
|
+
::Kernel.raise ::ArgumentError, "wrong number of arguments (expected 0)" unless args.empty?
|
55
56
|
# pb.field { pb.name "hello" }
|
56
57
|
message = (@message[name] ||= _new_message_from_descriptor(descriptor))
|
57
58
|
_scope(message, &block)
|
@@ -107,7 +108,7 @@ class Pbbuilder < BasicObject
|
|
107
108
|
private
|
108
109
|
|
109
110
|
def _append_repeated(name, descriptor, collection, &block)
|
110
|
-
raise ::ArgumentError, "expected Enumerable" unless collection.respond_to?(:map)
|
111
|
+
::Kernel.raise ::ArgumentError, "expected Enumerable" unless collection.respond_to?(:map)
|
111
112
|
elements = collection.map do |element|
|
112
113
|
message = _new_message_from_descriptor(descriptor)
|
113
114
|
_scope(message) { block.call(element) }
|
@@ -126,7 +127,7 @@ class Pbbuilder < BasicObject
|
|
126
127
|
end
|
127
128
|
|
128
129
|
def _new_message_from_descriptor(descriptor)
|
129
|
-
raise ::ArgumentError, "can't pass block to non-message field" unless descriptor.type == :message
|
130
|
+
::Kernel.raise ::ArgumentError, "can't pass block to non-message field" unless descriptor.type == :message
|
130
131
|
|
131
132
|
# Here we're using Protobuf reflection to create an instance of the message class
|
132
133
|
message_descriptor = descriptor.subtype
|
data/lib/pbbuilder/handler.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "pbbuilder/template"
|
2
|
+
|
1
3
|
# Basically copied and pasted from JbuilderHandler, except it uses Pbbuilder
|
2
4
|
|
3
5
|
class PbbuilderHandler
|
@@ -6,7 +8,7 @@ class PbbuilderHandler
|
|
6
8
|
def self.call(template, source = nil)
|
7
9
|
source ||= template.source
|
8
10
|
# We need to keep `source` on the first line, so line numbers are correct if there's an error
|
9
|
-
%{pb
|
10
|
-
pb.target!}
|
11
|
+
%{__already_defined = defined?(pb); pb ||= PbbuilderTemplate.new(self, @_response_class.new); #{source}
|
12
|
+
pb.target! unless (__already_defined && __already_defined != "method")}
|
11
13
|
end
|
12
14
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# PbbuilderTemplate is an extension of Pbbuilder to be used as a Rails template
|
2
|
+
# It adds support for partials.
|
3
|
+
class PbbuilderTemplate < Pbbuilder
|
4
|
+
class << self
|
5
|
+
attr_accessor :template_lookup_options
|
6
|
+
end
|
7
|
+
|
8
|
+
self.template_lookup_options = {handlers: [:pbbuilder]}
|
9
|
+
|
10
|
+
def initialize(context, message)
|
11
|
+
@context = context
|
12
|
+
super(message)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Render a partial. Can be called as:
|
16
|
+
# pb.partial! "name/of_partial", argument: 123
|
17
|
+
# pb.partial! "name/of_partial", locals: {argument: 123}
|
18
|
+
# pb.partial! partial: "name/of_partial", argument: 123
|
19
|
+
# pb.partial! partial: "name/of_partial", locals: {argument: 123}
|
20
|
+
# pb.partial! @model # @model is an ActiveModel value, it will use the name to look up a partial
|
21
|
+
def partial!(*args)
|
22
|
+
if args.one? && _is_active_model?(args.first)
|
23
|
+
_render_active_model_partial args.first
|
24
|
+
else
|
25
|
+
_render_explicit_partial(*args)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def set!(field, *args, **kwargs, &block)
|
30
|
+
# If partial options are being passed, we assume a collection is being rendered with a partial for every element
|
31
|
+
# pb.friends @friends, partial: "friend", as: :friend
|
32
|
+
if args.one? && _partial_options?(kwargs)
|
33
|
+
# Call set! on the super class, passing in a block that renders a partial for every element
|
34
|
+
super(field, *args) do |element|
|
35
|
+
_set_inline_partial(element, kwargs)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def _partial_options?(options)
|
45
|
+
::Hash === options && options.key?(:as) && options.key?(:partial)
|
46
|
+
end
|
47
|
+
|
48
|
+
def _is_active_model?(object)
|
49
|
+
object.class.respond_to?(:model_name) && object.respond_to?(:to_partial_path)
|
50
|
+
end
|
51
|
+
|
52
|
+
def _render_explicit_partial(name_or_options, locals = {})
|
53
|
+
case name_or_options
|
54
|
+
when ::Hash
|
55
|
+
# partial! partial: 'name', foo: 'bar'
|
56
|
+
options = name_or_options
|
57
|
+
else
|
58
|
+
# partial! 'name', locals: {foo: 'bar'}
|
59
|
+
options = if locals.one? && (locals.keys.first == :locals)
|
60
|
+
locals.merge(partial: name_or_options)
|
61
|
+
else
|
62
|
+
{partial: name_or_options, locals: locals}
|
63
|
+
end
|
64
|
+
# partial! 'name', foo: 'bar'
|
65
|
+
as = locals.delete(:as)
|
66
|
+
options[:as] = as if as.present?
|
67
|
+
options[:collection] = locals[:collection] if locals.key?(:collection)
|
68
|
+
end
|
69
|
+
|
70
|
+
_render_partial_with_options options
|
71
|
+
end
|
72
|
+
|
73
|
+
def _render_active_model_partial(object)
|
74
|
+
@context.render object, pb: self
|
75
|
+
end
|
76
|
+
|
77
|
+
def _set_inline_partial(object, options)
|
78
|
+
locals = ::Hash[options[:as], object]
|
79
|
+
_render_partial_with_options options.merge(locals: locals)
|
80
|
+
end
|
81
|
+
|
82
|
+
def _render_partial_with_options(options)
|
83
|
+
options.reverse_merge! locals: options.except(:partial, :as, :collection)
|
84
|
+
options.reverse_merge! ::PbbuilderTemplate.template_lookup_options
|
85
|
+
|
86
|
+
_render_partial options
|
87
|
+
end
|
88
|
+
|
89
|
+
def _render_partial(options)
|
90
|
+
options[:locals][:pb] = self
|
91
|
+
@context.render options
|
92
|
+
end
|
93
|
+
end
|
data/pbbuilder.gemspec
CHANGED
@@ -0,0 +1,97 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "action_view/testing/resolvers"
|
3
|
+
|
4
|
+
class PbbuilderTemplateTest < ActiveSupport::TestCase
|
5
|
+
PERSON_PARTIAL = <<-PBBUILDER
|
6
|
+
pb.extract! person, :name
|
7
|
+
PBBUILDER
|
8
|
+
|
9
|
+
RACER_PARTIAL = <<-PBBUILDER
|
10
|
+
pb.extract! racer, :name
|
11
|
+
pb.friends racer.friends, partial: "racers/racer", as: :racer
|
12
|
+
pb.best_friend do
|
13
|
+
pb.partial! "racers/racer", racer: racer.best_friend
|
14
|
+
end if racer.best_friend.present?
|
15
|
+
PBBUILDER
|
16
|
+
|
17
|
+
PARTIALS = {
|
18
|
+
"_partial.pb.pbbuilder" => "pb.name name",
|
19
|
+
"_person.pb.pbbuilder" => PERSON_PARTIAL,
|
20
|
+
"racers/_racer.pb.pbbuilder" => RACER_PARTIAL,
|
21
|
+
|
22
|
+
# Ensure we find only Pbbuilder partials from within Pbbuilder templates.
|
23
|
+
"_person.html.erb" => "Hello world!"
|
24
|
+
}
|
25
|
+
|
26
|
+
test "basic template" do
|
27
|
+
result = render('pb.name "hello"')
|
28
|
+
assert_equal "hello", result.name
|
29
|
+
end
|
30
|
+
|
31
|
+
test "partial by name with top-level locals" do
|
32
|
+
result = render('pb.partial! "partial", name: "hello"')
|
33
|
+
assert_equal "hello", result.name
|
34
|
+
end
|
35
|
+
|
36
|
+
test "partial by name with nested locals" do
|
37
|
+
result = render('pb.partial! "partial", locals: { name: "hello" }')
|
38
|
+
assert_equal "hello", result.name
|
39
|
+
end
|
40
|
+
|
41
|
+
test "partial by options containing nested locals" do
|
42
|
+
result = render('pb.partial! partial: "partial", locals: { name: "hello" }')
|
43
|
+
assert_equal "hello", result.name
|
44
|
+
end
|
45
|
+
|
46
|
+
test "partial by options containing top-level locals" do
|
47
|
+
result = render('pb.partial! partial: "partial", name: "hello"')
|
48
|
+
assert_equal "hello", result.name
|
49
|
+
end
|
50
|
+
|
51
|
+
test "partial for Active Model" do
|
52
|
+
result = render("pb.partial! @racer", racer: Racer.new(123, "Chris Harris", []))
|
53
|
+
assert_equal "Chris Harris", result.name
|
54
|
+
end
|
55
|
+
|
56
|
+
test "collection partial" do
|
57
|
+
friends = [Racer.new(1, "Johnny Test", []), Racer.new(2, "Max Verstappen", [])]
|
58
|
+
result = render("pb.partial! @racer", racer: Racer.new(123, "Chris Harris", friends))
|
59
|
+
assert_equal 2, result.friends.size
|
60
|
+
assert_equal "Johnny Test", result.friends[0].name
|
61
|
+
assert_equal "Max Verstappen", result.friends[1].name
|
62
|
+
end
|
63
|
+
|
64
|
+
test "nested message partial" do
|
65
|
+
other_racer = Racer.new(2, "Max Verstappen", [])
|
66
|
+
result = render("pb.partial! @racer", racer: Racer.new(123, "Chris Harris", [], other_racer))
|
67
|
+
assert_equal "Max Verstappen", result.best_friend.name
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def render(*args)
|
73
|
+
render_without_parsing(*args)
|
74
|
+
end
|
75
|
+
|
76
|
+
def render_without_parsing(source, assigns = {})
|
77
|
+
view = build_view(fixtures: PARTIALS.merge("source.pb.pbbuilder" => source), assigns: assigns)
|
78
|
+
view.render(template: "source", handlers: [:pbbuilder], formats: [:pb])
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_view(options = {})
|
82
|
+
resolver = ActionView::FixtureResolver.new(options.fetch(:fixtures))
|
83
|
+
lookup_context = ActionView::LookupContext.new([resolver], {}, [""])
|
84
|
+
controller = ActionView::TestCase::TestController.new
|
85
|
+
|
86
|
+
assigns = options.fetch(:assigns, {})
|
87
|
+
assigns.reverse_merge! _response_class: API::Person
|
88
|
+
|
89
|
+
view = ActionView::Base.with_empty_template_cache.new(lookup_context, assigns, controller)
|
90
|
+
|
91
|
+
def view.view_cache_dependencies
|
92
|
+
[]
|
93
|
+
end
|
94
|
+
|
95
|
+
view
|
96
|
+
end
|
97
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -4,6 +4,15 @@ ENV["RAILS_ENV"] = "test"
|
|
4
4
|
require "rails"
|
5
5
|
require "rails/test_help"
|
6
6
|
require "rails/test_unit/reporter"
|
7
|
+
|
8
|
+
require "active_support"
|
9
|
+
require "active_support/core_ext/array/access"
|
10
|
+
require "active_support/cache/memory_store"
|
11
|
+
require "active_support/json"
|
12
|
+
require "active_model"
|
13
|
+
require "action_view"
|
14
|
+
require "rails/version"
|
15
|
+
|
7
16
|
require "pbbuilder"
|
8
17
|
|
9
18
|
Rails::TestUnitReporter.executable = "bin/test"
|
@@ -27,3 +36,10 @@ end
|
|
27
36
|
module API
|
28
37
|
Person = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("pbbuildertest.Person").msgclass
|
29
38
|
end
|
39
|
+
|
40
|
+
class Racer < Struct.new(:id, :name, :friends, :best_friend)
|
41
|
+
extend ActiveModel::Naming
|
42
|
+
include ActiveModel::Conversion
|
43
|
+
end
|
44
|
+
|
45
|
+
ActionView::Template.register_template_handler :pbbuilder, PbbuilderHandler
|
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.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bouke van der Bijl
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-05-
|
11
|
+
date: 2021-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: google-protobuf
|
@@ -24,7 +24,7 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 3.15.5
|
27
|
-
description:
|
27
|
+
description:
|
28
28
|
email:
|
29
29
|
- bouke@cheddar.me
|
30
30
|
executables: []
|
@@ -42,8 +42,10 @@ files:
|
|
42
42
|
- lib/pbbuilder/handler.rb
|
43
43
|
- lib/pbbuilder/protobuf_extension.rb
|
44
44
|
- lib/pbbuilder/railtie.rb
|
45
|
+
- lib/pbbuilder/template.rb
|
45
46
|
- lib/tasks/pbbuilder_tasks.rake
|
46
47
|
- pbbuilder.gemspec
|
48
|
+
- test/pbbuilder_template_test.rb
|
47
49
|
- test/pbbuilder_test.rb
|
48
50
|
- test/protobuf_extension_test.rb
|
49
51
|
- test/test_helper.rb
|
@@ -51,7 +53,7 @@ homepage: https://github.com/cheddar-me/pbbuilder
|
|
51
53
|
licenses:
|
52
54
|
- MIT
|
53
55
|
metadata: {}
|
54
|
-
post_install_message:
|
56
|
+
post_install_message:
|
55
57
|
rdoc_options: []
|
56
58
|
require_paths:
|
57
59
|
- lib
|
@@ -66,11 +68,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
68
|
- !ruby/object:Gem::Version
|
67
69
|
version: '0'
|
68
70
|
requirements: []
|
69
|
-
rubygems_version: 3.
|
70
|
-
signing_key:
|
71
|
+
rubygems_version: 3.2.3
|
72
|
+
signing_key:
|
71
73
|
specification_version: 4
|
72
74
|
summary: Generate Protobuf messages via a Builder-style DSL
|
73
75
|
test_files:
|
76
|
+
- test/pbbuilder_template_test.rb
|
74
77
|
- test/pbbuilder_test.rb
|
75
78
|
- test/protobuf_extension_test.rb
|
76
79
|
- test/test_helper.rb
|