pb-serializer 0.2.0 → 0.5.1

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 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: []