pb-serializer 0.2.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3d65ab2cd7830751ed61e5cd72eca1d688fac910b544167994780d065e55fa3b
4
- data.tar.gz: e991534beffa6fb92b4c23f05bf59b5ba0ef6872cd34dc801148e08ec3f2c075
3
+ metadata.gz: db5867da46505d0facc08d12c1d6a3c93a87d7ca617110e8a9f2265444df72db
4
+ data.tar.gz: 594549370135b3237cbdfc2e701554eafa89c87fababd5b64e33476ed2ce11b2
5
5
  SHA512:
6
- metadata.gz: 0a138f78df718b226cbf65cef851380a941c34306ed80bdc2fb65093f0dec804515d0187d3f0b11e4fcd36bbc339dfca08f2beef49989d6417ccf157ddf9da97
7
- data.tar.gz: bf343a1d3f7f055370f652616fc6914dc682f2fdc6840c6975614537d5ecf02241974b8df986505f96a517401b676e0756aa2040d181898e8dc866ee76ab305c
6
+ metadata.gz: d40d26a27feb02d827068be43c3c93f44146cc7a2314047684bd7290cab34beee1e5930fd9d000922ad768fc00fcd7f544e530a85cf5267f7913a8f7e7f3fe4c
7
+ data.tar.gz: 7f2e1f146b3f5f466b1d3886b60e70ccc6505a919c7b8ef8c2a8498b2182b441a5695318b27e028f5f4943cdf39ea7f650ddc53370d475ea1385e5e4363dd48b
@@ -9,8 +9,10 @@ jobs:
9
9
  fail-fast: false
10
10
  matrix:
11
11
  os: [ ubuntu-latest, macos-latest ]
12
- # ruby: [ 2.5, 2.6, 2.7 ] # TODO: Wait for supporting Ruby 2.7 in google-protobuf
13
- ruby: [ 2.5, 2.6 ]
12
+ # TODO: Wait for supporting Ruby 3.0 in simplecov-cobertura.
13
+ # See https://github.com/dashingrocket/simplecov-cobertura/pull/16
14
+ # ruby: [ 2.5, 2.6, 2.7, 3.0 ]
15
+ ruby: [ 2.5, 2.6, 2.7 ]
14
16
  runs-on: ${{ matrix.os }}
15
17
 
16
18
  steps:
@@ -30,3 +32,9 @@ jobs:
30
32
  - run: bundle install --jobs 4 --retry 3
31
33
 
32
34
  - run: bundle exec rspec
35
+ env:
36
+ CI: true
37
+
38
+ - uses: codecov/codecov-action@v1
39
+ with:
40
+ file: ./coverage/coverage.xml
@@ -15,11 +15,9 @@ jobs:
15
15
  sudo apt-get update
16
16
  sudo apt-get install libsqlite3-dev
17
17
 
18
- - name: Setup reviewdog
19
- run: |
20
- mkdir -p $HOME/bin
21
- curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b $HOME/bin
22
- echo ::add-path::$HOME/bin
18
+ - uses: reviewdog/action-setup@v1
19
+ with:
20
+ reviewdog_version: latest
23
21
 
24
22
  - run: gem install bundler -v 2.1.4
25
23
 
@@ -28,6 +26,8 @@ jobs:
28
26
  - run: bundle install --jobs 4 --retry 3
29
27
 
30
28
  - name: Run Rubocop with Reviewdog
29
+ env:
30
+ REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
31
31
  run: |
32
32
  bundle exec rubocop --fail-level E \
33
33
  | reviewdog -f=rubocop -reporter=github-pr-review
data/CHANGELOG.md ADDED
@@ -0,0 +1,50 @@
1
+ ## Unreleased
2
+
3
+ ## 0.5.1
4
+
5
+ - Improving interoperability with `computed_model`
6
+ - Simplify field mask normalizer and add `Pb::Serializer.parse_field_mask` method https://github.com/wantedly/pb-serializer/pull/40
7
+ - Stop defining accessor methods in `attribute` DSL if the method of the same name already existss https://github.com/wantedly/pb-serializer/pull/42
8
+ - Refactoring
9
+ - Extract Dsl and Hook from Serializable module https://github.com/wantedly/pb-serializer/pull/41
10
+
11
+ ## 0.5.0
12
+
13
+ - Bump `computed_model` from 0.2.2 to 0.3.0 https://github.com/wantedly/pb-serializer/pull/38
14
+
15
+ ## 0.4.0
16
+
17
+ - Make `#initialize` extensible used with `define_primary_loader` https://github.com/wantedly/pb-serializer/pull/31
18
+ - Supoprt `ignore` directive https://github.com/wantedly/pb-serializer/pull/36
19
+ - Support field mask https://github.com/wantedly/pb-serializer/pull/34
20
+
21
+ ## 0.3.0
22
+
23
+ - Support `if` option https://github.com/wantedly/pb-serializer/pull/24
24
+ - Improve error handling https://github.com/wantedly/pb-serializer/pull/26
25
+ - raise `MissingMessageTypeError` if `message` declaration is missed
26
+ - raise `MissingFieldError` if `attribute` declaration is missed
27
+ - raise `InvalidOptionError` when `attribute` receives invalid params
28
+ - Introduce Pb::Serializer.configure https://github.com/wantedly/pb-serializer/pull/27
29
+ - Add `missing_field_behavior` config to suppress `MissingFieldError`
30
+ - Rename `InvalidOptionError` -> `InvalidAttributeOptionError`
31
+ - Skip serializing when a value is already serialized https://github.com/wantedly/pb-serializer/pull/29
32
+
33
+
34
+ ## 0.2.1
35
+
36
+ - **BREAKING CHANGE** `required` -> `allow_nil` https://github.com/wantedly/pb-serializer/pull/21
37
+ - Make Serializer's constructors extensible https://github.com/wantedly/pb-serializer/pull/22
38
+
39
+ ## 0.2.0
40
+
41
+ - **BREAKING CHANGE** https://github.com/wantedly/pb-serializer/pull/17
42
+ - Support loading and serializing arrays
43
+ - Bump `computed_model` from 0.1.0 to 0.2.1
44
+ - Change API
45
+ - Add example specs https://github.com/wantedly/pb-serializer/pull/18
46
+
47
+
48
+ ## 0.1.0
49
+
50
+ Initial release.
data/README.md CHANGED
@@ -1,12 +1,16 @@
1
1
  # Pb::Serializer
2
+ [![CI](https://github.com/wantedly/pb-serializer/workflows/CI/badge.svg?branch=master)](https://github.com/wantedly/pb-serializer/actions?query=workflow%3ACI+branch%3Amaster)
3
+ [![codecov](https://codecov.io/gh/wantedly/pb-serializer/branch/master/graph/badge.svg)](https://codecov.io/gh/wantedly/pb-serializer)
4
+ [![Gem Version](https://badge.fury.io/rb/pb-serializer.svg)](https://badge.fury.io/rb/pb-serializer)
5
+ [![License](https://img.shields.io/github/license/wantedly/pb-serializer)](./LICENSE)
2
6
 
3
7
  ```rb
4
8
  class UserSerializer < Pb::Serializer::Base
5
9
  message YourApp::User
6
10
 
7
- attribute :id, required: true
8
- attribute :name, required: true
9
- attribute :posts, required: true
11
+ attribute :id
12
+ attribute :name
13
+ attribute :posts
10
14
 
11
15
  define_primary_loader :user do |subdeps, ids:, **|
12
16
  User.where(id: ids).preload(subdeps).map { |u| new(u) }
@@ -29,9 +33,9 @@ class PostSerializer < Pb::Serializer::Base
29
33
  Post.where(user_id: user_ids).preload(subdeps).map { |p| new(p) }
30
34
  end
31
35
 
32
- attribute :id, required: true
33
- attribute :title, required: true
34
- attribute :body, required: true
36
+ attribute :id
37
+ attribute :title
38
+ attribute :body
35
39
  end
36
40
 
37
41
  class UserGrpcService < YourApp::UserService::Service
data/codecov.yml ADDED
@@ -0,0 +1,6 @@
1
+ coverage:
2
+ status:
3
+ project:
4
+ default:
5
+ target: 95%
6
+ patch: off
@@ -1,13 +1,21 @@
1
1
  module Pb
2
2
  module Serializable
3
+ extend ActiveSupport::Concern
4
+ include ComputedModel::Model
5
+
3
6
  def self.included(base)
4
- base.extend ClassMethods
5
- base.include ComputedModel
6
- base.singleton_class.prepend Hook
7
+ base.include Pb::Serializer::ComputedModelSupport
8
+ base.extend Pb::Serializer::Dsl
7
9
  end
8
10
 
11
+ # @param with [
12
+ # Google::Protobuf::FieldMask,
13
+ # Array<(Symbol, Hash)>,
14
+ # Hash{Symbol=>(Array,Symbol,Hash)},
15
+ # ]
9
16
  def to_pb(with: nil)
10
- # TODO: apply masks
17
+ with ||= ::Pb::Serializer.build_default_mask(self.class.message_class.descriptor)
18
+ with = ::Pb::Serializer.normalize_mask(with)
11
19
 
12
20
  oneof_set = []
13
21
 
@@ -15,26 +23,38 @@ module Pb
15
23
  self.class.message_class.descriptor.each do |fd|
16
24
  attr = self.class.find_attribute_by_field_descriptor(fd)
17
25
 
18
- next unless attr # TODO
26
+ unless attr
27
+ msg = "#{self.class.message_class.name}.#{fd.name} is missed in #{self.class.name}"
28
+
29
+ case Pb::Serializer.configuration.missing_field_behavior
30
+ when :raise then raise ::Pb::Serializer::MissingFieldError, msg
31
+ when :warn then Pb::Serializer.logger.warn msg
32
+ end
33
+
34
+ next
35
+ end
36
+
37
+ next unless with.key?(attr.name)
38
+ next unless attr.serializable?(self)
19
39
 
20
40
  raise "#{self.name}.#{attr.name} is not defined" unless respond_to?(attr.name)
21
41
 
22
42
  v = public_send(attr.name)
23
- v = attr.convert_to_pb(v)
43
+ v = attr.convert_to_pb(v, with: with[attr.name])
24
44
 
25
- if attr.required && attr.field_descriptor.default == v
45
+ if attr.oneof?
46
+ if !v.nil?
47
+ if oneof_set.include?(attr.oneof)
48
+ raise ::Pb::Serializer::ConflictOneofError, "#{primary_object.class.name}##{attr.name} is oneof attribute"
49
+ end
50
+ oneof_set << attr.oneof
51
+ end
52
+ elsif !attr.allow_nil? && v.nil?
26
53
  raise ::Pb::Serializer::ValidationError, "#{primary_object.class.name}##{attr.name} is required"
27
54
  end
28
55
 
29
56
  next if v.nil?
30
57
 
31
- if attr.oneof?
32
- if oneof_set.include?(attr.oneof)
33
- raise ::Pb::Serializer::ConflictOneofError, "#{primary_object.class.name}##{attr.name} is oneof attribute"
34
- end
35
- oneof_set << attr.oneof
36
- end
37
-
38
58
  if attr.repeated?
39
59
  o.public_send(attr.name).push(*v)
40
60
  else
@@ -44,76 +64,15 @@ module Pb
44
64
 
45
65
  self.class.oneofs.each do |oneof|
46
66
  next if oneof_set.include?(oneof.name)
47
- next unless oneof.required?
67
+ next if oneof.allow_nil?
48
68
  raise ::Pb::Serializer::ValidationError, "#{primary_object.class.name}##{oneof.name} is required"
49
69
  end
50
70
 
51
71
  o
52
72
  end
53
73
 
54
- private def primary_object
55
- primary_object_name = self.class.__pb_serializer_primary_model_name
56
- if primary_object_name
57
- send(primary_object_name)
58
- elsif kind_of?(Serializer::Base)
59
- send(:object)
60
- else
61
- self
62
- end
63
- end
64
-
65
- module Hook
66
- def define_primary_loader(name)
67
- self.__pb_serializer_primary_model_name = name
68
-
69
- super
70
- end
71
-
72
- def computed(name)
73
- __pb_serializer_attrs << name
74
-
75
- super
76
- end
77
-
78
- def define_loader(name, **)
79
- __pb_serializer_attrs << name
80
-
81
- super
82
- end
83
- end
84
-
85
74
  module ClassMethods
86
- attr_reader :message_class
87
- attr_accessor :__pb_serializer_primary_model_name
88
-
89
- def message(klass)
90
- @message_class = klass
91
- end
92
-
93
- # @param name [Symbol] An attribute name
94
- # @param required [Boolean] Set true if this attribute should not zero-value
95
- # @param serializer [Class] A serializer class for this attribute
96
- def attribute(name, required: false, serializer: nil)
97
- fd = message_class.descriptor.find { |fd| fd.name.to_sym == name }
98
- raise ::Pb::Serializer::UnknownFieldError, "#{name} is not defined in #{message_class.name}" unless fd
99
-
100
- attr = ::Pb::Serializer::Attribute.new(
101
- name: name,
102
- required: required,
103
- serializer_class: serializer,
104
- field_descriptor: fd,
105
- oneof: @current_oneof&.name,
106
- )
107
-
108
- @attr_by_name ||= {}
109
- @attr_by_name[name] = attr
110
-
111
- define_method attr.name do
112
- primary_object.public_send(attr.name)
113
- end
114
- end
115
-
116
- # @param with [Array, Google::Protobuf::FieldMask, nil]
75
+ # @param with [Array, Hash, Google::Protobuf::FieldMask, nil]
117
76
  # @return [Array]
118
77
  def bulk_load_and_serialize(with: nil, **args)
119
78
  bulk_load(with: with, **args).map { |s| s.to_pb(with: with) }
@@ -121,36 +80,18 @@ module Pb
121
80
 
122
81
  def bulk_load(with: nil, **args)
123
82
  with ||= ::Pb::Serializer.build_default_mask(message_class.descriptor)
124
- with = with.reject { |c| (__pb_serializer_attrs & (c.kind_of?(Hash) ? c.keys : [c])).empty? }
83
+ with = ::Pb::Serializer.normalize_mask(with)
84
+ with = __pb_serializer_filter_only_computed_model_attrs(with)
85
+
86
+ primary_object_name = __pb_serializer_primary_model_name
87
+ if primary_object_name
88
+ (with[primary_object_name] ||= []) << true
89
+ elsif self < Serializer::Base
90
+ (with[:object] ||= []) << true
91
+ end
125
92
 
126
93
  bulk_load_and_compute(with, **args)
127
94
  end
128
-
129
- def oneof(name, required: true)
130
- @oneof_by_name ||= {}
131
- @current_oneof = ::Pb::Serializer::Oneof.new(
132
- name: name,
133
- required: required,
134
- attributes: [],
135
- )
136
- yield
137
- @oneof_by_name[name] = @current_oneof
138
- @current_oneof = nil
139
- end
140
-
141
- private def __pb_serializer_attrs
142
- @__pb_serializer_attrs ||= Set.new
143
- end
144
-
145
- # @param fd [Google::Protobuf::FieldDescriptor] a field descriptor
146
- # @return [Pb::Serializer::Attribute, nil]
147
- def find_attribute_by_field_descriptor(fd)
148
- @attr_by_name[fd.name.to_sym]
149
- end
150
-
151
- def oneofs
152
- @oneof_by_name&.values || []
153
- end
154
95
  end
155
96
  end
156
97
  end
data/lib/pb/serializer.rb CHANGED
@@ -1,7 +1,10 @@
1
1
  require "pb/serializer/version"
2
2
  require "the_pb"
3
3
  require "computed_model"
4
+ require "google/protobuf/field_mask_pb"
4
5
 
6
+ require "pb/serializer/dsl"
7
+ require "pb/serializer/computed_model_support"
5
8
  require "pb/serializable"
6
9
  require "pb/serializer/base"
7
10
  require "pb/serializer/attribute"
@@ -10,11 +13,58 @@ require "pb/serializer/oneof"
10
13
  module Pb
11
14
  module Serializer
12
15
  class Error < StandardError; end
16
+ class InvalidConfigurationError < Error; end
17
+ class MissingMessageTypeError < Error; end
13
18
  class UnknownFieldError < Error; end
14
19
  class ValidationError < Error; end
15
20
  class ConflictOneofError < Error; end
21
+ class InvalidAttributeOptionError < Error; end
22
+ class MissingFieldError < Error; end
23
+
24
+ class Configuration
25
+ # @!attribute logger
26
+ # @return [Logger]
27
+ attr_accessor :logger
28
+ # @!attribute [r] missing_field_behavior
29
+ # @return [:raise, :warn, :ignore] default: `:raise`
30
+ attr_reader :missing_field_behavior
31
+
32
+ def initialize
33
+ self.missing_field_behavior = :raise
34
+ self.logger = Logger.new(STDOUT)
35
+ end
36
+
37
+ # @param v [:raise, :warn, :ignore]
38
+ def missing_field_behavior=(v)
39
+ @missing_field_behavior = v
40
+
41
+ unless %i(raise warn ignore).include?(v)
42
+ raise InvalidConfigurationError, "missing_field_behavior #{v} is not allowed"
43
+ end
44
+ end
45
+ end
16
46
 
17
47
  class << self
48
+ # @example
49
+ # Pb::Serializer.configuration do |c|
50
+ # c.missing_field_behavior = :raise # :raise, :warn or :ignore (defualt: :raise)
51
+ # end
52
+ # @yield [c]
53
+ # @yieldparam [Configuration] config
54
+ def configure
55
+ yield configuration
56
+ end
57
+
58
+ # @return [Pb::Serializer::Configuration]
59
+ def configuration
60
+ @configuraiton ||= Configuration.new
61
+ end
62
+
63
+ # @return [Logger]
64
+ def logger
65
+ configuration.logger
66
+ end
67
+
18
68
  # @param [Google::Protobuf::Descriptor]
19
69
  def build_default_mask(descriptor)
20
70
  set =
@@ -41,6 +91,46 @@ module Pb
41
91
  end
42
92
  set.to_a
43
93
  end
94
+
95
+ # @param [Google::Protobuf::FieldMask]
96
+ # @return [Array]
97
+ def parse_field_mask(field_mask)
98
+ unless field_mask.kind_of?(Google::Protobuf::FieldMask)
99
+ raise ArgumentError, "expected Google::Protobuf::FieldMask, but got #{field_mask.class}"
100
+ end
101
+
102
+ field_mask.paths.map do |path|
103
+ path.split(".").reverse.inject(nil) { |h, key| h.nil? ? key.to_sym : { key.to_sym => [h].compact } }
104
+ end
105
+ end
106
+
107
+ # @param [Google::Protobuf::FieldMask, Symbol, Array<(Symbol,Hash)>, Hash{Symbol=>(Array,Symbol,Hash)}]
108
+ # @return [Hash{Symbol=>(Array,Hash)}]
109
+ def normalize_mask(input)
110
+ if input.kind_of?(Google::Protobuf::FieldMask)
111
+ input = parse_field_mask(input)
112
+ end
113
+
114
+ normalized = {}
115
+
116
+ input = [input] if input.kind_of?(Hash)
117
+ Array(input).each do |el|
118
+ case el
119
+ when Symbol
120
+ normalized[el] ||= []
121
+ when Hash
122
+ el.each do |k, v|
123
+ v = [v] if v.kind_of?(Hash)
124
+ normalized[k] ||= []
125
+ normalized[k].push(*Array(v))
126
+ end
127
+ else
128
+ raise "not supported field mask type: #{input.class}"
129
+ end
130
+ end
131
+
132
+ normalized
133
+ end
44
134
  end
45
135
  end
46
136
  end
@@ -2,16 +2,31 @@ module Pb
2
2
  module Serializer
3
3
  class Attribute < Struct.new(
4
4
  :name,
5
- :required,
6
- :serializer_class,
5
+ :options,
7
6
  :field_descriptor,
8
7
  :oneof,
9
8
  keyword_init: true,
10
9
  )
11
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
+
12
22
  # @return [Boolean]
13
- def required?
14
- required
23
+ def allow_nil?
24
+ options.fetch(:allow_nil, false)
25
+ end
26
+
27
+ # @return [Class]
28
+ def serializer_class
29
+ options[:serializer]
15
30
  end
16
31
 
17
32
  # @return [Boolean]
@@ -19,16 +34,37 @@ module Pb
19
34
  field_descriptor.label == :repeated
20
35
  end
21
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
+
22
52
  def oneof?
23
53
  !oneof.nil?
24
54
  end
25
55
 
26
56
  # @param v [Object]
27
- def convert_to_pb(v, should_repeat: repeated?)
28
- return v.map { |i| convert_to_pb(i, should_repeat: false) } if should_repeat
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
29
61
 
30
62
  case field_descriptor.type
31
63
  when :message
64
+ if v.class < Google::Protobuf::MessageExts && v.class.descriptor.name == field_descriptor.submsg_name
65
+ return v
66
+ end
67
+
32
68
  case field_descriptor.submsg_name
33
69
  when "google.protobuf.Timestamp" then Pb.to_timestamp(v)
34
70
  when "google.protobuf.StringValue" then Pb.to_strval(v)
@@ -41,9 +77,8 @@ module Pb
41
77
  when "google.protobuf.BoolValue" then Pb.to_boolval(v)
42
78
  when "google.protobuf.BytesValue" then Pb.to_bytesval(v)
43
79
  else
44
- return nil if v.nil?
45
- return serializer_class.new(v).to_pb if serializer_class
46
- return v.to_pb if v.kind_of?(::Pb::Serializable)
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)
47
82
 
48
83
  raise "serializer was not found for #{field_descriptor.submsg_name}"
49
84
  end
@@ -8,16 +8,21 @@ module Pb
8
8
 
9
9
  attr_reader :object
10
10
 
11
- def initialize(object)
11
+ def initialize(object, *)
12
12
  @object = object
13
13
  end
14
14
 
15
15
  module Hook
16
16
  def define_primary_loader(name, &block)
17
17
  class_eval <<~RUBY
18
- def initialize(object)
19
- @#{name} = object
18
+ module PbSerializerDefinePrimaryLoaderPrependMethods
19
+ def initialize(*args)
20
+ super
21
+ @#{name} = object
22
+ end
20
23
  end
24
+
25
+ prepend PbSerializerDefinePrimaryLoaderPrependMethods
21
26
  RUBY
22
27
 
23
28
  super
@@ -0,0 +1,51 @@
1
+ module Pb
2
+ module Serializer
3
+ module ComputedModelSupport
4
+ def self.included(base)
5
+ base.singleton_class.prepend Hook
6
+ end
7
+
8
+ private def primary_object
9
+ primary_object_name = self.class.__pb_serializer_primary_model_name
10
+ if primary_object_name
11
+ send(primary_object_name)
12
+ elsif kind_of?(Serializer::Base)
13
+ send(:object)
14
+ else
15
+ self
16
+ end
17
+ end
18
+
19
+ module Hook
20
+ attr_accessor :__pb_serializer_primary_model_name
21
+
22
+ def define_primary_loader(name)
23
+ self.__pb_serializer_primary_model_name = name
24
+
25
+ super
26
+ end
27
+
28
+ def computed(name)
29
+ __pb_serializer_attrs << name
30
+
31
+ super
32
+ end
33
+
34
+ def define_loader(name, **)
35
+ __pb_serializer_attrs << name
36
+
37
+ super
38
+ end
39
+
40
+ # @param with [Array]
41
+ private def __pb_serializer_filter_only_computed_model_attrs(with)
42
+ with.reject { |c| (__pb_serializer_attrs & (c.kind_of?(Hash) ? c.keys : [c])).empty? }
43
+ end
44
+
45
+ private def __pb_serializer_attrs
46
+ @__pb_serializer_attrs ||= Set.new
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,69 @@
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
@@ -2,13 +2,13 @@ module Pb
2
2
  module Serializer
3
3
  class Oneof < Struct.new(
4
4
  :name,
5
- :required,
5
+ :allow_nil,
6
6
  :attributes,
7
7
  keyword_init: true,
8
8
  )
9
9
  # @return [Boolean]
10
- def required?
11
- required
10
+ def allow_nil?
11
+ allow_nil
12
12
  end
13
13
  end
14
14
  end
@@ -1,5 +1,5 @@
1
1
  module Pb
2
2
  module Serializer
3
- VERSION = "0.2.0".freeze
3
+ VERSION = "0.5.1".freeze
4
4
  end
5
5
  end
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  rails_versions = [">= 5.2", "< 6.1"]
30
30
  spec.add_runtime_dependency "google-protobuf", "~> 3.0"
31
31
  spec.add_runtime_dependency "the_pb", "~> 0.0.1"
32
- spec.add_runtime_dependency "computed_model", "~> 0.2.1"
32
+ spec.add_runtime_dependency "computed_model", "~> 0.3.0"
33
33
 
34
34
  spec.add_development_dependency "activerecord", rails_versions
35
35
  spec.add_development_dependency "bundler", "~> 2.0"
@@ -38,4 +38,6 @@ Gem::Specification.new do |spec|
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"
41
+ spec.add_development_dependency "simplecov", "~> 0.18.5"
42
+ spec.add_development_dependency "simplecov-cobertura", "~> 1.3"
41
43
  end
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.2.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - izumin5210
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-29 00:00:00.000000000 Z
11
+ date: 2021-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: google-protobuf
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.2.1
47
+ version: 0.3.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.2.1
54
+ version: 0.3.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: activerecord
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -156,6 +156,34 @@ dependencies:
156
156
  - - "~>"
157
157
  - !ruby/object:Gem::Version
158
158
  version: '1.4'
159
+ - !ruby/object:Gem::Dependency
160
+ name: simplecov
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: 0.18.5
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: 0.18.5
173
+ - !ruby/object:Gem::Dependency
174
+ name: simplecov-cobertura
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: '1.3'
180
+ type: :development
181
+ prerelease: false
182
+ version_requirements: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '1.3'
159
187
  description: Serialize objects into Protocol Buffers messages
160
188
  email:
161
189
  - m@izum.in
@@ -169,6 +197,7 @@ files:
169
197
  - ".rspec"
170
198
  - ".rubocop.yml"
171
199
  - ".rubocop_todo.yml"
200
+ - CHANGELOG.md
172
201
  - CODE_OF_CONDUCT.md
173
202
  - Gemfile
174
203
  - LICENSE.txt
@@ -176,10 +205,13 @@ files:
176
205
  - Rakefile
177
206
  - bin/console
178
207
  - bin/setup
208
+ - codecov.yml
179
209
  - lib/pb/serializable.rb
180
210
  - lib/pb/serializer.rb
181
211
  - lib/pb/serializer/attribute.rb
182
212
  - lib/pb/serializer/base.rb
213
+ - lib/pb/serializer/computed_model_support.rb
214
+ - lib/pb/serializer/dsl.rb
183
215
  - lib/pb/serializer/oneof.rb
184
216
  - lib/pb/serializer/version.rb
185
217
  - pb-serializer.gemspec
@@ -190,7 +222,7 @@ metadata:
190
222
  homepage_uri: https://github.com/izumin5210/pb-serializer
191
223
  source_code_uri: https://github.com/izumin5210/pb-serializer
192
224
  changelog_uri: https://github.com/izumin5210/pb-serializer
193
- post_install_message:
225
+ post_install_message:
194
226
  rdoc_options: []
195
227
  require_paths:
196
228
  - lib
@@ -205,8 +237,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
205
237
  - !ruby/object:Gem::Version
206
238
  version: '0'
207
239
  requirements: []
208
- rubygems_version: 3.1.2
209
- signing_key:
240
+ rubygems_version: 3.2.3
241
+ signing_key:
210
242
  specification_version: 4
211
243
  summary: Serialize objects into Protocol Buffers messages
212
244
  test_files: []