pb-serializer 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/pb-serializer.svg)](https://badge.fury.io/rb/pb-serializer)
|
5
5
|
[![License](https://img.shields.io/github/license/wantedly/pb-serializer)](./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
|