pb-serializer 0.5.1 → 0.5.2
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/.yardopts +8 -0
- data/CHANGELOG.md +4 -0
- data/README.ja.md +66 -0
- data/README.md +46 -46
- data/docs/examples.md +236 -0
- data/lib/pb/{serializer → serializable}/computed_model_support.rb +2 -1
- data/lib/pb/serializable/dsl/attribute.rb +94 -0
- data/lib/pb/serializable/dsl/oneof.rb +18 -0
- data/lib/pb/serializable/dsl.rb +75 -0
- data/lib/pb/serializable.rb +42 -11
- data/lib/pb/serializer/base.rb +3 -0
- data/lib/pb/serializer/version.rb +1 -1
- data/lib/pb/serializer.rb +11 -11
- data/pb-serializer.gemspec +2 -2
- metadata +16 -13
- data/lib/pb/serializer/attribute.rb +0 -91
- data/lib/pb/serializer/dsl.rb +0 -69
- data/lib/pb/serializer/oneof.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b06e7d595d2212e0d8c6d36bc652acccb3020094b6370c9494476eb0de483487
|
4
|
+
data.tar.gz: 13efc77cdd5a595a0763b16989f7064a5c1f79ebd3bdff7330964eadd7569db7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 936b879ffebc21afb812bb8763cfc57132c19db685138ba0b69c74ab156c70909a2f35482464c0c35ed776e5b2ff02546b1f55699bc48b5f44568890771da0b8
|
7
|
+
data.tar.gz: 6da97182764357672107dbacf6184e723f55ad203078dc9c660e53741d861ae39e210cf5d7ecb6a9b4155e618273e0963a97bc322bba7e579faa8f066b36a5bb
|
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
data/README.ja.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
<!--
|
2
|
+
# @title 日本語版 README
|
3
|
+
-->
|
4
|
+
|
5
|
+
# Pb::Serializer
|
6
|
+
|
7
|
+
`Pb::Serializer` はRuby オブジェクトの Protocol Buffers シリアライザです。
|
8
|
+
|
9
|
+
[English version](./README.md)
|
10
|
+
|
11
|
+
## Features
|
12
|
+
|
13
|
+
- [ActiveModelSerializers](https://github.com/rails-api/active_model_serializers) のような宣言的な API
|
14
|
+
- [Well-Known Types](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf) への自動変換(例 `google.protobuf.Uint64Value`)
|
15
|
+
- [`google.protobuf.FieldMask`](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#fieldmask) を利用した、GraphQL のような選択的フィールド取得のサポート
|
16
|
+
- [ComputedModel](https://github.com/wantedly/computed_model) と組み合わせることで、複雑なロジックと依存関係を持つ API も宣言的に実装できます
|
17
|
+
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
以下のような Protocol Buffers のメッセージ定義および ActiveRecord モデルを例にします。
|
22
|
+
|
23
|
+
```proto
|
24
|
+
syntax = "proto3";
|
25
|
+
|
26
|
+
package example;
|
27
|
+
|
28
|
+
option ruby_package = "ExamplesPb";
|
29
|
+
|
30
|
+
message User {
|
31
|
+
uint64 id = 1;
|
32
|
+
string name = 2;
|
33
|
+
}
|
34
|
+
```
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# Schema: [id(integer), name(string)]
|
38
|
+
class User < ActiveRecord::Base
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
`.proto` で定義された `User` メッセージに対応する PbSerializer を実装します。
|
43
|
+
生成されたクラスと定義されているフィールドすべてを PbSerializer に宣言する必要があります。
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
class UserPbSerializer < Pb::Serializer::Base
|
47
|
+
message ExamplesPb::User
|
48
|
+
|
49
|
+
attribute :id
|
50
|
+
attribute :name
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
実装した PbSerializer で、Ruby オブジェクトを protobuf message object にシリアライズできます。
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
user = User.find(123)
|
58
|
+
UserPbSerializer.new(user).to_pb
|
59
|
+
# => <ExamplesPb::User: id: 123, name: "someuser">
|
60
|
+
```
|
61
|
+
|
62
|
+
各 attribute の値は、PbSerializer インスタンス、もしくはコンストラクタに渡されたオブジェクト から決定されます。
|
63
|
+
|
64
|
+
## Next read
|
65
|
+
|
66
|
+
- [Examples](./docs/examples.md)
|
data/README.md
CHANGED
@@ -4,62 +4,66 @@
|
|
4
4
|
[](https://badge.fury.io/rb/pb-serializer)
|
5
5
|
[](./LICENSE)
|
6
6
|
|
7
|
-
|
8
|
-
class UserSerializer < Pb::Serializer::Base
|
9
|
-
message YourApp::User
|
7
|
+
`Pb::Serializer` is Protocol Buffers serializer for Ruby objects.
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
[日本語版 README](./README.ja.md)
|
10
|
+
|
11
|
+
## Features
|
12
|
+
|
13
|
+
- Declarative APIs such as [ActiveModelSerializers](https://github.com/rails-api/active_model_serializers)
|
14
|
+
- Automatic conversion to [Well-Known Types](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf) (e.g. `google.protobuf.Uint64Value`)
|
15
|
+
- Support for GraphQL-like selective field fetching using [`google.protobuf.FieldMask`](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#fieldmask).
|
16
|
+
- When combined with [ComputedModel](https://github.com/wantedly/computed_model), APIs with complex logic and dependencies can be implemented declaratively.
|
17
|
+
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
The following is an example of a message definition and ActiveRecord model for Protocol Buffers.
|
22
|
+
|
23
|
+
```proto
|
24
|
+
syntax = "proto3";
|
14
25
|
|
15
|
-
|
16
|
-
User.where(id: ids).preload(subdeps).map { |u| new(u) }
|
17
|
-
end
|
26
|
+
package example;
|
18
27
|
|
19
|
-
|
20
|
-
PostSerializer.bulk_load(user_id: user_ids, with: subdeps).group_by { |s| s.post.user_id }
|
21
|
-
end
|
28
|
+
option ruby_package = "ExamplesPb";
|
22
29
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
30
|
+
message User {
|
31
|
+
uint64 id = 1;
|
32
|
+
string name = 2;
|
33
|
+
}
|
34
|
+
```
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# Schema: [id(integer), name(string)]
|
38
|
+
class User < ActiveRecord::Base
|
27
39
|
end
|
40
|
+
```
|
28
41
|
|
29
|
-
|
30
|
-
|
42
|
+
Implements a PbSerializer for the `User` message defined in `.proto`.
|
43
|
+
You need to declare the generated class and all defined fields in the PbSerializer.
|
31
44
|
|
32
|
-
|
33
|
-
|
34
|
-
|
45
|
+
```ruby
|
46
|
+
class UserPbSerializer < Pb::Serializer::Base
|
47
|
+
message ExamplesPb::User
|
35
48
|
|
36
49
|
attribute :id
|
37
|
-
attribute :
|
38
|
-
attribute :body
|
50
|
+
attribute :name
|
39
51
|
end
|
52
|
+
```
|
40
53
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
# @param req [YourApp::ListFriendUsersRequest]
|
50
|
-
# @param call [GRPC::ActiveCall::SingleReqView]
|
51
|
-
# @return [YourApp::ListFriendUsersResponse]
|
52
|
-
def list_friend_users(req, call)
|
53
|
-
current_user = User.find(current_user_id)
|
54
|
-
YourApp::ListFriendUsersResponse.new(
|
55
|
-
users: UserSerializer.bulk_load_and_serialize(ids: current_user.friend_ids, with: req.field_mask)
|
56
|
-
)
|
57
|
-
end
|
58
|
-
end
|
54
|
+
You can serialize Ruby objects to protobuf message object with the implemented PbSerializer.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
user = User.find(123)
|
58
|
+
UserPbSerializer.new(user).to_pb
|
59
|
+
# => <ExamplesPb::User: id: 123, name: "someuser">
|
59
60
|
```
|
60
61
|
|
61
|
-
|
62
|
+
The value of each attribute is determined from the PbSerializer instance or the object passed to the constructor.
|
63
|
+
|
64
|
+
## Next read
|
62
65
|
|
66
|
+
- [Examples](./docs/examples.md)
|
63
67
|
|
64
68
|
## Installation
|
65
69
|
|
@@ -77,10 +81,6 @@ Or install it yourself as:
|
|
77
81
|
|
78
82
|
$ gem install pb-serializer
|
79
83
|
|
80
|
-
## Usage
|
81
|
-
|
82
|
-
TODO: Write usage instructions here
|
83
|
-
|
84
84
|
## Development
|
85
85
|
|
86
86
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/docs/examples.md
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
# Examples
|
2
|
+
|
3
|
+
## Sub messages
|
4
|
+
|
5
|
+
```proto
|
6
|
+
message Post {
|
7
|
+
uint64 id = 1;
|
8
|
+
string title = 2;
|
9
|
+
User author = 3;
|
10
|
+
}
|
11
|
+
|
12
|
+
message User {
|
13
|
+
uint64 id = 1;
|
14
|
+
string name = 2;
|
15
|
+
}
|
16
|
+
```
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
# Schema: [id(integer), title(string), author_id(integer)]
|
20
|
+
class Book < ActiveRecord::Base
|
21
|
+
belongs_to :author, class_name: 'User'
|
22
|
+
end
|
23
|
+
|
24
|
+
# Schema: [id(integer), name(string)]
|
25
|
+
class User < ActiveRecord::Base
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
class BookPbSerializer < Pb::Serializer::Base
|
31
|
+
message ExamplesPb::Book
|
32
|
+
|
33
|
+
attribute :id
|
34
|
+
attribute :title
|
35
|
+
attribute :author, serializer: UserPbSerializer
|
36
|
+
end
|
37
|
+
|
38
|
+
class UserPbSerializer < Pb::Serializer::Base
|
39
|
+
message ExamplesPb::User
|
40
|
+
|
41
|
+
attribute :id
|
42
|
+
attribute :name
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
## Enum
|
47
|
+
|
48
|
+
```proto
|
49
|
+
message Conversation {
|
50
|
+
uint64 id = 1;
|
51
|
+
Status status = 3;
|
52
|
+
|
53
|
+
enum Status {
|
54
|
+
STATUS_UNSPECIFIED = 0;
|
55
|
+
ARCHIVED = 1;
|
56
|
+
ACTIVE = 2;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
```
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
# https://api.rubyonrails.org/classes/ActiveRecord/Enum.html
|
63
|
+
|
64
|
+
# Schema: [id(integer), status(integer)]
|
65
|
+
class Conversation < ApplicationRecord
|
66
|
+
enum status: { active: 0, archived: 1 }, _prefix: true
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
# @!attribute [r] object
|
72
|
+
# @return [Conversation]
|
73
|
+
class ConversationPbSerializer < Pb::Serializer::Base
|
74
|
+
message ExamplesPb::Conversation
|
75
|
+
|
76
|
+
attribute :status
|
77
|
+
|
78
|
+
def status
|
79
|
+
object.status.upcase.to_sym
|
80
|
+
end
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
## Oneof
|
85
|
+
|
86
|
+
```proto
|
87
|
+
message Entry {
|
88
|
+
oneof entry {
|
89
|
+
Message message = 1;
|
90
|
+
Comment comment = 2;
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
message Message {
|
95
|
+
// ...
|
96
|
+
}
|
97
|
+
|
98
|
+
message Comment {
|
99
|
+
// ...
|
100
|
+
}
|
101
|
+
```
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
# see https://api.rubyonrails.org/classes/ActiveRecord/DelegatedType.html
|
105
|
+
|
106
|
+
class Entry < ApplicationRecord
|
107
|
+
delegated_type :entryable, types: %w[Message Comment]
|
108
|
+
end
|
109
|
+
|
110
|
+
class Message < ApplicationRecord
|
111
|
+
# ...
|
112
|
+
end
|
113
|
+
|
114
|
+
class Comment < ApplicationRecord
|
115
|
+
# ...
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
# @!attribute [r] object
|
121
|
+
# @return [Entry]
|
122
|
+
class EntryPbSerializer < Pb::Serializer::Base
|
123
|
+
message ExamplesPb::Entry
|
124
|
+
|
125
|
+
oneof :entry do
|
126
|
+
attribute :message, if: -> { object.message? }, serializer: MessagePbSerializer
|
127
|
+
attribute :comment, if: -> { object.comment? }, serializer: CommentPbSerializer
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# @!attribute [r] object
|
132
|
+
# @return [Message]
|
133
|
+
class MessagePbSerializer < Pb::Serializer::Base
|
134
|
+
message ExamplesPb::Message
|
135
|
+
|
136
|
+
# ...
|
137
|
+
end
|
138
|
+
|
139
|
+
# @!attribute [r] object
|
140
|
+
# @return [Comment]
|
141
|
+
class CommentPbSerializer < Pb::Serializer::Base
|
142
|
+
message ExamplesPb::Comment
|
143
|
+
|
144
|
+
# ...
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
148
|
+
## Serializable model
|
149
|
+
|
150
|
+
```proto
|
151
|
+
message User {
|
152
|
+
uint64 id = 1;
|
153
|
+
string first_name = 2;
|
154
|
+
string last_name = 3;
|
155
|
+
}
|
156
|
+
```
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
# Schema: [id(integer), first_name(string), last_name(string)]
|
160
|
+
class User < ActiveRecord::Base
|
161
|
+
include Pb::Serializable
|
162
|
+
|
163
|
+
message ExamplesPb::User
|
164
|
+
|
165
|
+
attribute :id
|
166
|
+
attribute :first_name
|
167
|
+
attribute :last_name
|
168
|
+
end
|
169
|
+
```
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
User.find(123).to_pb
|
173
|
+
# => <ExamplesPb::User: id: 123, first_name: 'Masayuki', last_name: 'Izumi'>
|
174
|
+
```
|
175
|
+
|
176
|
+
## With FieldMask and ComputedModel
|
177
|
+
|
178
|
+
```proto
|
179
|
+
message User {
|
180
|
+
uint64 id = 1;
|
181
|
+
string first_name = 2;
|
182
|
+
string last_name = 3;
|
183
|
+
string full_name = 4;
|
184
|
+
}
|
185
|
+
```
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
# Schema: [id(integer), first_name(string), last_name(string)]
|
189
|
+
class RawUser < ActiveRecord::Base
|
190
|
+
self.table_name = 'users'
|
191
|
+
end
|
192
|
+
|
193
|
+
class User
|
194
|
+
include ComputedModel::Model
|
195
|
+
|
196
|
+
def initialize(raw_user)
|
197
|
+
@raw_user = user
|
198
|
+
end
|
199
|
+
|
200
|
+
def self.batch_get(ids, with:)
|
201
|
+
bulk_load_and_compute([*Array(with), :id], ids: ids)
|
202
|
+
end
|
203
|
+
|
204
|
+
define_primary_loader :raw_user do |subfields, ids:, **|
|
205
|
+
RawUser.where(id: ids).select(subfields).map { new(_1) }
|
206
|
+
end
|
207
|
+
|
208
|
+
delegate_dependency :id, :first_name, :last_name,
|
209
|
+
to: :raw_user, include_subfields: true
|
210
|
+
|
211
|
+
dependency :first_name, :last_name
|
212
|
+
computed def full_name
|
213
|
+
[first_name, last_name].compact.join(' ')
|
214
|
+
end
|
215
|
+
end
|
216
|
+
```
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
class UserPbSerializer < Pb::Serializer::Base
|
220
|
+
message ExamplesPb::User
|
221
|
+
|
222
|
+
attribute :id
|
223
|
+
attribute :first_name
|
224
|
+
attribute :last_name
|
225
|
+
attribute :full_name
|
226
|
+
end
|
227
|
+
```
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
# req.read_mask # => <Google::Protobuf::FieldMask: paths: ['id', 'full_name']>
|
231
|
+
mask = Pb::Serializer.parse_field_mask(req.read_mask)
|
232
|
+
|
233
|
+
user = User.batch_get([123], with: mask)[0]
|
234
|
+
UserPbSerializer.new(user).to_pb(with: mask)
|
235
|
+
# => <ExamplesPb::User: id: 123, first_name: '', last_name: '', full_name: "Masayuki Izumi">
|
236
|
+
```
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Pb
|
2
|
+
module Serializable
|
3
|
+
module Dsl
|
4
|
+
# @api private
|
5
|
+
class Attribute < Struct.new(
|
6
|
+
:name,
|
7
|
+
:options,
|
8
|
+
:field_descriptor,
|
9
|
+
:oneof,
|
10
|
+
keyword_init: true,
|
11
|
+
)
|
12
|
+
|
13
|
+
ALLOWED_OPTIONS = Set[:allow_nil, :if, :serializer, :ignore].freeze
|
14
|
+
|
15
|
+
def initialize(options:, **)
|
16
|
+
super
|
17
|
+
|
18
|
+
unknown_options = options.keys.to_set - ALLOWED_OPTIONS
|
19
|
+
unless unknown_options.empty?
|
20
|
+
raise ::Pb::Serializer::InvalidAttributeOptionError, "unknown options are specified in #{name} attribute: #{unknown_options.to_a}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Boolean]
|
25
|
+
def allow_nil?
|
26
|
+
options.fetch(:allow_nil, false)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Class]
|
30
|
+
def serializer_class
|
31
|
+
options[:serializer]
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Boolean]
|
35
|
+
def repeated?
|
36
|
+
field_descriptor.label == :repeated
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Boolean]
|
40
|
+
def serializable?(s)
|
41
|
+
return false if options[:ignore]
|
42
|
+
|
43
|
+
cond = options[:if]
|
44
|
+
|
45
|
+
return true unless cond
|
46
|
+
|
47
|
+
case cond
|
48
|
+
when String, Symbol; then s.send(cond)
|
49
|
+
when Proc; then s.instance_exec(&cond)
|
50
|
+
else raise ::Pb::Serializer::InvalidAttributeOptionError, "`if` option can accept only Symbol, String or Proc. but got #{cond.class}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def oneof?
|
55
|
+
!oneof.nil?
|
56
|
+
end
|
57
|
+
|
58
|
+
# @param v [Object]
|
59
|
+
# @param with [Hash, Array]
|
60
|
+
def convert_to_pb(v, with: nil, should_repeat: repeated?)
|
61
|
+
return nil if v.nil?
|
62
|
+
return v.map { |i| convert_to_pb(i, should_repeat: false, with: with) } if should_repeat
|
63
|
+
|
64
|
+
case field_descriptor.type
|
65
|
+
when :message
|
66
|
+
if v.class < Google::Protobuf::MessageExts && v.class.descriptor.name == field_descriptor.submsg_name
|
67
|
+
return v
|
68
|
+
end
|
69
|
+
|
70
|
+
case field_descriptor.submsg_name
|
71
|
+
when "google.protobuf.Timestamp" then Pb.to_timestamp(v)
|
72
|
+
when "google.protobuf.StringValue" then Pb.to_strval(v)
|
73
|
+
when "google.protobuf.Int32Value" then Pb.to_int32val(v)
|
74
|
+
when "google.protobuf.Int64Value" then Pb.to_int64val(v)
|
75
|
+
when "google.protobuf.UInt32Value" then Pb.to_uint32val(v)
|
76
|
+
when "google.protobuf.UInt64Value" then Pb.to_uint64val(v)
|
77
|
+
when "google.protobuf.FloatValue" then Pb.to_floatval(v)
|
78
|
+
when "google.protobuf.DoubleValue" then Pb.to_doubleval(v)
|
79
|
+
when "google.protobuf.BoolValue" then Pb.to_boolval(v)
|
80
|
+
when "google.protobuf.BytesValue" then Pb.to_bytesval(v)
|
81
|
+
else
|
82
|
+
return serializer_class.new(v).to_pb(with: with) if serializer_class
|
83
|
+
return v.to_pb(with: with) if v.kind_of?(::Pb::Serializable)
|
84
|
+
|
85
|
+
raise "serializer was not found for #{field_descriptor.submsg_name}"
|
86
|
+
end
|
87
|
+
else
|
88
|
+
v.nil? ? field_descriptor.default : v
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'pb/serializable/dsl/attribute'
|
2
|
+
require 'pb/serializable/dsl/oneof'
|
3
|
+
|
4
|
+
module Pb
|
5
|
+
module Serializable
|
6
|
+
module Dsl
|
7
|
+
# @param klass [Class] Protobuf message class
|
8
|
+
# @return [void]
|
9
|
+
def message(klass)
|
10
|
+
self.__pb_serializer_message_class = klass
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param name [Symbol] An attribute name
|
14
|
+
# @param opts [Hash] options
|
15
|
+
# @option opts [Boolean] :allow_nil Set true if this attribute allow to be nil
|
16
|
+
# @option opts [Class] :serializer A serializer class for this attribute
|
17
|
+
# @option opts [String, Symbol, Proc] :if A method, proc or string to call to determine to serialize this field
|
18
|
+
# @return [void]
|
19
|
+
# @raise [Pb::Serializer::MissingMessageTypeError] if this class has not been called {#message}
|
20
|
+
# @raise [Pb::Serializer::UnknownFieldError] if the field does not defined in .proto
|
21
|
+
# @raise [Pb::Serializer::InvalidAttributeOptionError] if unknown options are passed
|
22
|
+
def attribute(name, opts = {})
|
23
|
+
raise ::Pb::Serializer::MissingMessageTypeError, "message specificaiton is missed" unless __pb_serializer_message_class
|
24
|
+
|
25
|
+
fd = __pb_serializer_message_class.descriptor.find { |fd| fd.name.to_sym == name }
|
26
|
+
|
27
|
+
raise ::Pb::Serializer::UnknownFieldError, "#{name} is not defined in #{ __pb_serializer_message_class.name}" unless fd
|
28
|
+
|
29
|
+
attr = Attribute.new(
|
30
|
+
name: name,
|
31
|
+
options: opts,
|
32
|
+
field_descriptor: fd,
|
33
|
+
oneof: @current_oneof&.name,
|
34
|
+
)
|
35
|
+
|
36
|
+
__pb_serializer_attr_by_name[name] = attr
|
37
|
+
|
38
|
+
unless method_defined?(attr.name)
|
39
|
+
define_method attr.name do
|
40
|
+
primary_object.public_send(attr.name)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param names [Array<Symbol>] Attribute names to be ignored
|
46
|
+
# @return [void]
|
47
|
+
# @example Ignore attributes
|
48
|
+
# ignore :deprecated_field, :not_implemented_field
|
49
|
+
def ignore(*names)
|
50
|
+
names.each do |name|
|
51
|
+
attribute name, ignore: true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# @param name [Symbol] An oneof attribute name
|
56
|
+
# @param allow_nil [Boolean] Set true if this oneof attribute allow to be nil
|
57
|
+
# @return [void]
|
58
|
+
# @example Define oneof attributes
|
59
|
+
# oneof :test_oneof do
|
60
|
+
# attribute :name
|
61
|
+
# attribute :sub_message
|
62
|
+
# end
|
63
|
+
def oneof(name, allow_nil: false)
|
64
|
+
@current_oneof = Oneof.new(
|
65
|
+
name: name,
|
66
|
+
allow_nil: allow_nil,
|
67
|
+
attributes: [],
|
68
|
+
)
|
69
|
+
yield
|
70
|
+
__pb_serializer_oneof_by_name[name] = @current_oneof
|
71
|
+
@current_oneof = nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/pb/serializable.rb
CHANGED
@@ -1,30 +1,39 @@
|
|
1
|
+
require "pb/serializable/computed_model_support"
|
2
|
+
require "pb/serializable/dsl"
|
3
|
+
|
1
4
|
module Pb
|
2
5
|
module Serializable
|
3
6
|
extend ActiveSupport::Concern
|
4
7
|
include ComputedModel::Model
|
5
8
|
|
9
|
+
# @!parse extend Dsl
|
10
|
+
# @!parse extend ClassMethods
|
11
|
+
|
6
12
|
def self.included(base)
|
7
|
-
base.include
|
8
|
-
base.extend
|
13
|
+
base.include ComputedModelSupport
|
14
|
+
base.extend Dsl
|
9
15
|
end
|
10
16
|
|
11
17
|
# @param with [
|
12
18
|
# Google::Protobuf::FieldMask,
|
13
19
|
# Array<(Symbol, Hash)>,
|
14
|
-
# Hash{Symbol=>(Array,Symbol,Hash)},
|
15
|
-
#
|
20
|
+
# Hash{Symbol=>(Array,Symbol,Hash,Proc)},
|
21
|
+
# ]
|
22
|
+
# Specifies the list of fields to be serialized in the Proto message object.
|
23
|
+
# `nil` means that all fields defined in .proto will be serialized.
|
24
|
+
# @return [Object] a protobuf message object
|
16
25
|
def to_pb(with: nil)
|
17
|
-
with ||= ::Pb::Serializer.build_default_mask(self.class.
|
26
|
+
with ||= ::Pb::Serializer.build_default_mask(self.class.__pb_serializer_message_class.descriptor)
|
18
27
|
with = ::Pb::Serializer.normalize_mask(with)
|
19
28
|
|
20
29
|
oneof_set = []
|
21
30
|
|
22
|
-
o = self.class.
|
23
|
-
self.class.
|
24
|
-
attr = self.class.
|
31
|
+
o = self.class.__pb_serializer_message_class.new
|
32
|
+
self.class.__pb_serializer_message_class.descriptor.each do |fd|
|
33
|
+
attr = self.class.__pb_serializer_attr_by_field_descriptor(fd)
|
25
34
|
|
26
35
|
unless attr
|
27
|
-
msg = "#{self.class.
|
36
|
+
msg = "#{self.class.__pb_serializer_message_class.name}.#{fd.name} is missed in #{self.class.name}"
|
28
37
|
|
29
38
|
case Pb::Serializer.configuration.missing_field_behavior
|
30
39
|
when :raise then raise ::Pb::Serializer::MissingFieldError, msg
|
@@ -62,7 +71,7 @@ module Pb
|
|
62
71
|
end
|
63
72
|
end
|
64
73
|
|
65
|
-
self.class.
|
74
|
+
self.class.__pb_serializer_oneof_by_name.values.each do |oneof|
|
66
75
|
next if oneof_set.include?(oneof.name)
|
67
76
|
next if oneof.allow_nil?
|
68
77
|
raise ::Pb::Serializer::ValidationError, "#{primary_object.class.name}##{oneof.name} is required"
|
@@ -79,7 +88,7 @@ module Pb
|
|
79
88
|
end
|
80
89
|
|
81
90
|
def bulk_load(with: nil, **args)
|
82
|
-
with ||= ::Pb::Serializer.build_default_mask(
|
91
|
+
with ||= ::Pb::Serializer.build_default_mask(__pb_serializer_message_class.descriptor)
|
83
92
|
with = ::Pb::Serializer.normalize_mask(with)
|
84
93
|
with = __pb_serializer_filter_only_computed_model_attrs(with)
|
85
94
|
|
@@ -92,6 +101,28 @@ module Pb
|
|
92
101
|
|
93
102
|
bulk_load_and_compute(with, **args)
|
94
103
|
end
|
104
|
+
|
105
|
+
# @api private
|
106
|
+
attr_accessor :__pb_serializer_message_class
|
107
|
+
|
108
|
+
# @api private
|
109
|
+
# @return [Hash{Symbol=>::Pb::Serializer::Attribute}]
|
110
|
+
def __pb_serializer_attr_by_name
|
111
|
+
@__pb_serializer_attr_by_name ||= {}
|
112
|
+
end
|
113
|
+
|
114
|
+
# @api private
|
115
|
+
# @return [Hash{Symbol=>::Pb::Serializer::Oneof}]
|
116
|
+
def __pb_serializer_oneof_by_name
|
117
|
+
@__pb_serializer_oneof_by_name ||= {}
|
118
|
+
end
|
119
|
+
|
120
|
+
# @api private
|
121
|
+
# @param fd [Google::Protobuf::FieldDescriptor] a field descriptor
|
122
|
+
# @return [Pb::Serializer::Attribute, nil]
|
123
|
+
def __pb_serializer_attr_by_field_descriptor(fd)
|
124
|
+
__pb_serializer_attr_by_name[fd.name.to_sym]
|
125
|
+
end
|
95
126
|
end
|
96
127
|
end
|
97
128
|
end
|
data/lib/pb/serializer/base.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Pb
|
2
2
|
module Serializer
|
3
3
|
class Base
|
4
|
+
# @!parse include Pb::Serializable
|
5
|
+
|
4
6
|
def self.inherited(base)
|
5
7
|
base.include ::Pb::Serializable
|
6
8
|
base.singleton_class.prepend Hook
|
@@ -12,6 +14,7 @@ module Pb
|
|
12
14
|
@object = object
|
13
15
|
end
|
14
16
|
|
17
|
+
# @private
|
15
18
|
module Hook
|
16
19
|
def define_primary_loader(name, &block)
|
17
20
|
class_eval <<~RUBY
|
data/lib/pb/serializer.rb
CHANGED
@@ -3,12 +3,8 @@ require "the_pb"
|
|
3
3
|
require "computed_model"
|
4
4
|
require "google/protobuf/field_mask_pb"
|
5
5
|
|
6
|
-
require "pb/serializer/dsl"
|
7
|
-
require "pb/serializer/computed_model_support"
|
8
6
|
require "pb/serializable"
|
9
7
|
require "pb/serializer/base"
|
10
|
-
require "pb/serializer/attribute"
|
11
|
-
require "pb/serializer/oneof"
|
12
8
|
|
13
9
|
module Pb
|
14
10
|
module Serializer
|
@@ -35,6 +31,7 @@ module Pb
|
|
35
31
|
end
|
36
32
|
|
37
33
|
# @param v [:raise, :warn, :ignore]
|
34
|
+
# @return [void]
|
38
35
|
def missing_field_behavior=(v)
|
39
36
|
@missing_field_behavior = v
|
40
37
|
|
@@ -51,6 +48,7 @@ module Pb
|
|
51
48
|
# end
|
52
49
|
# @yield [c]
|
53
50
|
# @yieldparam [Configuration] config
|
51
|
+
# @return [void]
|
54
52
|
def configure
|
55
53
|
yield configuration
|
56
54
|
end
|
@@ -65,7 +63,7 @@ module Pb
|
|
65
63
|
configuration.logger
|
66
64
|
end
|
67
65
|
|
68
|
-
# @param [Google::Protobuf::Descriptor]
|
66
|
+
# @param descriptor [Google::Protobuf::Descriptor]
|
69
67
|
def build_default_mask(descriptor)
|
70
68
|
set =
|
71
69
|
descriptor.each_with_object(Set[]) do |fd, m|
|
@@ -83,7 +81,7 @@ module Pb
|
|
83
81
|
"google.protobuf.BoolValue" ,
|
84
82
|
"google.protobuf.BytesValue" then m << fd.name.to_sym
|
85
83
|
else
|
86
|
-
m << { fd.name.to_sym => build_default_mask(fd.subtype) }
|
84
|
+
m << { fd.name.to_sym => -> { build_default_mask(fd.subtype) } }
|
87
85
|
end
|
88
86
|
else
|
89
87
|
m << fd.name.to_sym
|
@@ -92,7 +90,7 @@ module Pb
|
|
92
90
|
set.to_a
|
93
91
|
end
|
94
92
|
|
95
|
-
# @param [Google::Protobuf::FieldMask]
|
93
|
+
# @param field_mask [Google::Protobuf::FieldMask]
|
96
94
|
# @return [Array]
|
97
95
|
def parse_field_mask(field_mask)
|
98
96
|
unless field_mask.kind_of?(Google::Protobuf::FieldMask)
|
@@ -104,22 +102,24 @@ module Pb
|
|
104
102
|
end
|
105
103
|
end
|
106
104
|
|
107
|
-
# @param [Google::Protobuf::FieldMask, Symbol, Array<(Symbol,Hash)>, Hash{Symbol=>(Array,Symbol,Hash)}]
|
108
|
-
# @return [Hash{Symbol=>(Array,Hash)}]
|
105
|
+
# @param input [Google::Protobuf::FieldMask, Symbol, Array<(Symbol,Hash)>, Hash{Symbol=>(Array,Symbol,Hash,Proc)}, Proc]
|
106
|
+
# @return [Hash{Symbol=>(Array,Hash,Proc)}]
|
109
107
|
def normalize_mask(input)
|
110
108
|
if input.kind_of?(Google::Protobuf::FieldMask)
|
111
109
|
input = parse_field_mask(input)
|
112
110
|
end
|
113
111
|
|
114
|
-
|
115
|
-
|
112
|
+
input = input.call if input.kind_of?(Proc)
|
116
113
|
input = [input] if input.kind_of?(Hash)
|
114
|
+
|
115
|
+
normalized = {}
|
117
116
|
Array(input).each do |el|
|
118
117
|
case el
|
119
118
|
when Symbol
|
120
119
|
normalized[el] ||= []
|
121
120
|
when Hash
|
122
121
|
el.each do |k, v|
|
122
|
+
v = v.call if v.kind_of?(Proc)
|
123
123
|
v = [v] if v.kind_of?(Hash)
|
124
124
|
normalized[k] ||= []
|
125
125
|
normalized[k].push(*Array(v))
|
data/pb-serializer.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
|
11
11
|
spec.summary = "Serialize objects into Protocol Buffers messages"
|
12
12
|
spec.description = spec.summary
|
13
|
-
spec.homepage = "https://github.com/
|
13
|
+
spec.homepage = "https://github.com/wantedly/pb-serializer"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.metadata["homepage_uri"] = spec.homepage
|
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.add_development_dependency "activerecord", rails_versions
|
35
35
|
spec.add_development_dependency "bundler", "~> 2.0"
|
36
36
|
spec.add_development_dependency "onkcop", "~> 0.53"
|
37
|
-
spec.add_development_dependency "rake", "~>
|
37
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
38
38
|
spec.add_development_dependency "rspec", "~> 3.0"
|
39
39
|
spec.add_development_dependency "rubocop", "0.67.2" # for onkcop
|
40
40
|
spec.add_development_dependency "sqlite3", "~> 1.4"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pb-serializer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- izumin5210
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: google-protobuf
|
@@ -106,14 +106,14 @@ dependencies:
|
|
106
106
|
requirements:
|
107
107
|
- - "~>"
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version: '
|
109
|
+
version: '13.0'
|
110
110
|
type: :development
|
111
111
|
prerelease: false
|
112
112
|
version_requirements: !ruby/object:Gem::Requirement
|
113
113
|
requirements:
|
114
114
|
- - "~>"
|
115
115
|
- !ruby/object:Gem::Version
|
116
|
-
version: '
|
116
|
+
version: '13.0'
|
117
117
|
- !ruby/object:Gem::Dependency
|
118
118
|
name: rspec
|
119
119
|
requirement: !ruby/object:Gem::Requirement
|
@@ -197,31 +197,34 @@ files:
|
|
197
197
|
- ".rspec"
|
198
198
|
- ".rubocop.yml"
|
199
199
|
- ".rubocop_todo.yml"
|
200
|
+
- ".yardopts"
|
200
201
|
- CHANGELOG.md
|
201
202
|
- CODE_OF_CONDUCT.md
|
202
203
|
- Gemfile
|
203
204
|
- LICENSE.txt
|
205
|
+
- README.ja.md
|
204
206
|
- README.md
|
205
207
|
- Rakefile
|
206
208
|
- bin/console
|
207
209
|
- bin/setup
|
208
210
|
- codecov.yml
|
211
|
+
- docs/examples.md
|
209
212
|
- lib/pb/serializable.rb
|
213
|
+
- lib/pb/serializable/computed_model_support.rb
|
214
|
+
- lib/pb/serializable/dsl.rb
|
215
|
+
- lib/pb/serializable/dsl/attribute.rb
|
216
|
+
- lib/pb/serializable/dsl/oneof.rb
|
210
217
|
- lib/pb/serializer.rb
|
211
|
-
- lib/pb/serializer/attribute.rb
|
212
218
|
- lib/pb/serializer/base.rb
|
213
|
-
- lib/pb/serializer/computed_model_support.rb
|
214
|
-
- lib/pb/serializer/dsl.rb
|
215
|
-
- lib/pb/serializer/oneof.rb
|
216
219
|
- lib/pb/serializer/version.rb
|
217
220
|
- pb-serializer.gemspec
|
218
|
-
homepage: https://github.com/
|
221
|
+
homepage: https://github.com/wantedly/pb-serializer
|
219
222
|
licenses:
|
220
223
|
- MIT
|
221
224
|
metadata:
|
222
|
-
homepage_uri: https://github.com/
|
223
|
-
source_code_uri: https://github.com/
|
224
|
-
changelog_uri: https://github.com/
|
225
|
+
homepage_uri: https://github.com/wantedly/pb-serializer
|
226
|
+
source_code_uri: https://github.com/wantedly/pb-serializer
|
227
|
+
changelog_uri: https://github.com/wantedly/pb-serializer
|
225
228
|
post_install_message:
|
226
229
|
rdoc_options: []
|
227
230
|
require_paths:
|
@@ -237,7 +240,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
237
240
|
- !ruby/object:Gem::Version
|
238
241
|
version: '0'
|
239
242
|
requirements: []
|
240
|
-
rubygems_version: 3.2.
|
243
|
+
rubygems_version: 3.2.33
|
241
244
|
signing_key:
|
242
245
|
specification_version: 4
|
243
246
|
summary: Serialize objects into Protocol Buffers messages
|
@@ -1,91 +0,0 @@
|
|
1
|
-
module Pb
|
2
|
-
module Serializer
|
3
|
-
class Attribute < Struct.new(
|
4
|
-
:name,
|
5
|
-
:options,
|
6
|
-
:field_descriptor,
|
7
|
-
:oneof,
|
8
|
-
keyword_init: true,
|
9
|
-
)
|
10
|
-
|
11
|
-
ALLOWED_OPTIONS = Set[:allow_nil, :if, :serializer, :ignore].freeze
|
12
|
-
|
13
|
-
def initialize(options:, **)
|
14
|
-
super
|
15
|
-
|
16
|
-
unknown_options = options.keys.to_set - ALLOWED_OPTIONS
|
17
|
-
unless unknown_options.empty?
|
18
|
-
raise InvalidAttributeOptionError, "unknown options are specified in #{name} attribute: #{unknown_options.to_a}"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# @return [Boolean]
|
23
|
-
def allow_nil?
|
24
|
-
options.fetch(:allow_nil, false)
|
25
|
-
end
|
26
|
-
|
27
|
-
# @return [Class]
|
28
|
-
def serializer_class
|
29
|
-
options[:serializer]
|
30
|
-
end
|
31
|
-
|
32
|
-
# @return [Boolean]
|
33
|
-
def repeated?
|
34
|
-
field_descriptor.label == :repeated
|
35
|
-
end
|
36
|
-
|
37
|
-
# @return [Boolean]
|
38
|
-
def serializable?(s)
|
39
|
-
return false if options[:ignore]
|
40
|
-
|
41
|
-
cond = options[:if]
|
42
|
-
|
43
|
-
return true unless cond
|
44
|
-
|
45
|
-
case cond
|
46
|
-
when String, Symbol; then s.send(cond)
|
47
|
-
when Proc; then s.instance_exec(&cond)
|
48
|
-
else raise InvalidAttributeOptionError, "`if` option can accept only Symbol, String or Proc. but got #{cond.class}"
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def oneof?
|
53
|
-
!oneof.nil?
|
54
|
-
end
|
55
|
-
|
56
|
-
# @param v [Object]
|
57
|
-
# @param with [Hash, Array]
|
58
|
-
def convert_to_pb(v, with: nil, should_repeat: repeated?)
|
59
|
-
return nil if v.nil?
|
60
|
-
return v.map { |i| convert_to_pb(i, should_repeat: false, with: with) } if should_repeat
|
61
|
-
|
62
|
-
case field_descriptor.type
|
63
|
-
when :message
|
64
|
-
if v.class < Google::Protobuf::MessageExts && v.class.descriptor.name == field_descriptor.submsg_name
|
65
|
-
return v
|
66
|
-
end
|
67
|
-
|
68
|
-
case field_descriptor.submsg_name
|
69
|
-
when "google.protobuf.Timestamp" then Pb.to_timestamp(v)
|
70
|
-
when "google.protobuf.StringValue" then Pb.to_strval(v)
|
71
|
-
when "google.protobuf.Int32Value" then Pb.to_int32val(v)
|
72
|
-
when "google.protobuf.Int64Value" then Pb.to_int64val(v)
|
73
|
-
when "google.protobuf.UInt32Value" then Pb.to_uint32val(v)
|
74
|
-
when "google.protobuf.UInt64Value" then Pb.to_uint64val(v)
|
75
|
-
when "google.protobuf.FloatValue" then Pb.to_floatval(v)
|
76
|
-
when "google.protobuf.DoubleValue" then Pb.to_doubleval(v)
|
77
|
-
when "google.protobuf.BoolValue" then Pb.to_boolval(v)
|
78
|
-
when "google.protobuf.BytesValue" then Pb.to_bytesval(v)
|
79
|
-
else
|
80
|
-
return serializer_class.new(v).to_pb(with: with) if serializer_class
|
81
|
-
return v.to_pb(with: with) if v.kind_of?(::Pb::Serializable)
|
82
|
-
|
83
|
-
raise "serializer was not found for #{field_descriptor.submsg_name}"
|
84
|
-
end
|
85
|
-
else
|
86
|
-
v.nil? ? field_descriptor.default : v
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
data/lib/pb/serializer/dsl.rb
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
module Pb
|
2
|
-
module Serializer
|
3
|
-
module Dsl
|
4
|
-
def message(klass)
|
5
|
-
@message_class = klass
|
6
|
-
end
|
7
|
-
|
8
|
-
# @param name [Symbol] An attribute name
|
9
|
-
# @param [Hash] opts options
|
10
|
-
# @option opts [Boolean] :allow_nil Set true if this attribute allow to be nil
|
11
|
-
# @option opts [Class] :serializer A serializer class for this attribute
|
12
|
-
# @option opts [String, Symbol, Proc] :if A method, proc or string to call to determine to serialize this field
|
13
|
-
def attribute(name, opts = {})
|
14
|
-
raise ::Pb::Serializer::MissingMessageTypeError, "message specificaiton is missed" unless message_class
|
15
|
-
|
16
|
-
fd = message_class.descriptor.find { |fd| fd.name.to_sym == name }
|
17
|
-
|
18
|
-
raise ::Pb::Serializer::UnknownFieldError, "#{name} is not defined in #{message_class.name}" unless fd
|
19
|
-
|
20
|
-
attr = ::Pb::Serializer::Attribute.new(
|
21
|
-
name: name,
|
22
|
-
options: opts,
|
23
|
-
field_descriptor: fd,
|
24
|
-
oneof: @current_oneof&.name,
|
25
|
-
)
|
26
|
-
|
27
|
-
@attr_by_name ||= {}
|
28
|
-
@attr_by_name[name] = attr
|
29
|
-
|
30
|
-
unless method_defined?(attr.name)
|
31
|
-
define_method attr.name do
|
32
|
-
primary_object.public_send(attr.name)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# @param names [Array<Symbol>] Attribute names to be ignored
|
38
|
-
def ignore(*names)
|
39
|
-
names.each do |name|
|
40
|
-
attribute name, ignore: true
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def oneof(name, allow_nil: false)
|
45
|
-
@oneof_by_name ||= {}
|
46
|
-
@current_oneof = ::Pb::Serializer::Oneof.new(
|
47
|
-
name: name,
|
48
|
-
allow_nil: allow_nil,
|
49
|
-
attributes: [],
|
50
|
-
)
|
51
|
-
yield
|
52
|
-
@oneof_by_name[name] = @current_oneof
|
53
|
-
@current_oneof = nil
|
54
|
-
end
|
55
|
-
|
56
|
-
attr_reader :message_class
|
57
|
-
|
58
|
-
# @param fd [Google::Protobuf::FieldDescriptor] a field descriptor
|
59
|
-
# @return [Pb::Serializer::Attribute, nil]
|
60
|
-
def find_attribute_by_field_descriptor(fd)
|
61
|
-
(@attr_by_name || {})[fd.name.to_sym]
|
62
|
-
end
|
63
|
-
|
64
|
-
def oneofs
|
65
|
-
@oneof_by_name&.values || []
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|