avro-builder 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f59fdd8a081eaaffd44838edb7a38ccdb9910c608376c736f24c65877c86afb5
4
- data.tar.gz: 5747a1fc177ee16557a4b8d4cf82b3e3c98be8dd5840c3c8be4bac0cf20d5940
3
+ metadata.gz: b69b72980ff9b8694f57b85ccf9167b46a939dbd3e19465dfee1de70f1cf82f9
4
+ data.tar.gz: a6856ad6135354d02549ec405c5b46a1d60616285c4f5b7d17980c59150a615b
5
5
  SHA512:
6
- metadata.gz: e27dd35585d480e3ab97164d0d133f0a84ac617539cb9f8a56aa51c82cd0c56041e0d384f8337f870a40339dd3fecb5b4451132dac0859605c8b7b02fd417cc4
7
- data.tar.gz: a42ae650e5fad0647da8e7d3174af55ee88fe73bafd98bf68ecc121301fcca9965ef686f893e7632c88b8087b07a1426ed58e83853a07c2c21664830dc9d6a7f
6
+ metadata.gz: d56eaaf1a0d4286fa09c2a8680cca7b97a0f9033211222d11436bf4444257b13225100ea2af1aa2226f3c3cb1dca305a7178e07cac8011cd3b389cc34d5d2a0d
7
+ data.tar.gz: 2b9f623c68965f2f557d2bd246a8835347382158cdeab816273985413adb4d36594415c39c29ba4a24666039fc2b35cd2a8b5d17080f48b1e756763550ad0006
data/.circleci/config.yml CHANGED
@@ -2,23 +2,24 @@ version: 2.1
2
2
  jobs:
3
3
  lint:
4
4
  docker:
5
- - image: cimg/ruby:2.7.7
5
+ - image: cimg/ruby:3.3.10
6
6
  working_directory: ~/avro-builder
7
7
  steps:
8
8
  - checkout
9
9
  - restore_cache:
10
10
  keys:
11
- - v2-gems-ruby-2.7.7-{{ checksum "avro-builder.gemspec" }}-{{ checksum "Gemfile" }}
12
- - v2-gems-ruby-2.7.7-
11
+ - v2-gems-ruby-3.3.10-{{ checksum "avro-builder.gemspec" }}-{{ checksum "Gemfile" }}
12
+ - v2-gems-ruby-3.3.10-
13
13
  - run:
14
14
  name: Install Gems
15
15
  command: |
16
- if ! bundle check --path=vendor/bundle; then
17
- bundle install --path=vendor/bundle --jobs=4 --retry=3
16
+ bundle config set path 'vendor/bundle'
17
+ if ! bundle check; then
18
+ bundle install --jobs=4 --retry=3
18
19
  bundle clean
19
20
  fi
20
21
  - save_cache:
21
- key: v2-gems-ruby-2.7.7-{{ checksum "avro-builder.gemspec" }}-{{ checksum "Gemfile" }}
22
+ key: v2-gems-ruby-3.3.10-{{ checksum "avro-builder.gemspec" }}-{{ checksum "Gemfile" }}
22
23
  paths:
23
24
  - "vendor/bundle"
24
25
  - "gemfiles/vendor/bundle"
@@ -46,8 +47,9 @@ jobs:
46
47
  - run:
47
48
  name: Install Gems
48
49
  command: |
49
- if ! bundle check --path=vendor/bundle; then
50
- bundle install --path=vendor/bundle --jobs=4 --retry=3
50
+ bundle config set path 'vendor/bundle'
51
+ if ! bundle check; then
52
+ bundle install --jobs=4 --retry=3
51
53
  bundle clean
52
54
  fi
53
55
  - save_cache:
@@ -69,13 +71,9 @@ workflows:
69
71
  matrix:
70
72
  parameters:
71
73
  gemfile:
72
- - gemfiles/avro_1.9.gemfile
73
- - gemfiles/avro_1.10.gemfile
74
74
  - gemfiles/avro_1.11.gemfile
75
75
  - gemfiles/avro_1.12.gemfile
76
76
  ruby-version:
77
- - 2.7.7
78
- - 3.0.5
79
- - 3.1.3
80
- - 3.2.0
81
- - 3.3.0
77
+ - 3.3.10
78
+ - 3.4.8
79
+ - 4.0.1
data/.github/CODEOWNERS CHANGED
@@ -1 +1 @@
1
- * @salsify/pim-core-backend
1
+ * @salsify/pim-core-backend @jturkel
data/.rubocop.yml CHANGED
@@ -1,13 +1,12 @@
1
1
  inherit_gem:
2
2
  salsify_rubocop: conf/rubocop.yml
3
3
 
4
+ inherit_mode:
5
+ merge:
6
+ - Exclude
7
+
8
+ require: rubocop-rspec
9
+
4
10
  AllCops:
5
- inherit_mode:
6
- merge:
7
- - Exclude
8
- NewCops: disable
9
11
  Exclude:
10
12
  - "gemfiles/**/*"
11
-
12
- Style/RaiseArgs:
13
- EnforcedStyle: compact
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.7.7
1
+ 3.3.10
data/Appraisals CHANGED
@@ -1,13 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- appraise 'avro-1.9' do
4
- gem 'avro', '1.9.2'
5
- end
6
-
7
- appraise 'avro-1.10' do
8
- gem 'avro', '1.10.2'
9
- end
10
-
11
3
  appraise 'avro-1.11' do
12
4
  gem 'avro', '~> 1.11.0'
13
5
  end
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # avro-builder changelog
2
2
 
3
+ ## v3.0.0
4
+ - Add support for configurable metadata on fields, records, enums, fixed types, and
5
+ primitives via `Avro::Builder.extra_metadata_attributes`.
6
+ - Add support for field `order` attribute in serialized output.
7
+ - Raise an error when invalid options are passed to a type instead of silently ignoring them.
8
+ - Add support for Ruby 3.4, and 4.0.
9
+ - Drop support for Avro < 1.11.
10
+ - Drop support for Ruby < 3.3.
11
+
3
12
  ## v2.1.0
4
13
  - Add support for Avro 1.12.
5
14
 
data/README.md CHANGED
@@ -458,6 +458,22 @@ Avro::Builder::Rake::AvroGenerateTask.new(name: :custom_gen,
458
458
  end
459
459
  ```
460
460
 
461
+ ### Extra metadata attributes
462
+
463
+ The [Avro specification](https://avro.apache.org/docs/1.12.0/specification/) allows metadata attribute to be added to any type or field as long as it does not clash with Avro specification attributes.
464
+
465
+ ```ruby
466
+ # Configure the metadata attributes an initializer
467
+ Avro::Builder.extra_metadata_attributes(:deprecated_by, :documentation_url)
468
+
469
+ # Use metadata attributes in your schema definition like any other DSL attributes
470
+ record :my_record do
471
+ documentation_url 'https://example.com/docs'
472
+ required :my_boolean, :boolean, deprecated_by: 'com.example.my_boolean2'
473
+ required :my_boolean2, :boolean
474
+ end
475
+ ```
476
+
461
477
  ## Development
462
478
 
463
479
  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/Rakefile CHANGED
@@ -8,9 +8,13 @@ RSpec::Core::RakeTask.new(:default_spec)
8
8
 
9
9
  Appraisal::Task.new
10
10
 
11
+ # rubocop:disable Rake/DuplicateTask
12
+ # rubocop:disable Rake/Desc
11
13
  if !ENV['APPRAISAL_INITIALIZED']
12
14
  task default: :appraisal
13
15
  task spec: :appraisal
14
16
  else
15
17
  task default: :default_spec
16
18
  end
19
+ # rubocop:enable Rake/DuplicateTask
20
+ # rubocop:enable Rake/Desc
data/avro-builder.gemspec CHANGED
@@ -27,18 +27,17 @@ Gem::Specification.new do |spec|
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ['lib']
29
29
 
30
- spec.required_ruby_version = '>= 2.7'
30
+ spec.required_ruby_version = '>= 3.3'
31
31
 
32
- spec.add_runtime_dependency 'avro', '>= 1.9.0', '< 1.13'
32
+ spec.add_runtime_dependency 'avro', '>= 1.11.0', '< 1.13'
33
33
 
34
34
  spec.add_development_dependency 'appraisal'
35
- spec.add_development_dependency 'bundler', '~> 2.0'
36
35
  spec.add_development_dependency 'json_spec'
37
36
  spec.add_development_dependency 'overcommit'
38
- spec.add_development_dependency 'rake', '~> 10.0'
39
- spec.add_development_dependency 'rspec', '~> 3.0'
37
+ spec.add_development_dependency 'rake', '~> 13.0'
38
+ spec.add_development_dependency 'rspec', '~> 3.13'
40
39
  spec.add_development_dependency 'rspec-its'
41
40
  spec.add_development_dependency 'rspec_junit_formatter'
42
- spec.add_development_dependency 'salsify_rubocop', '~> 1.0.1'
41
+ spec.add_development_dependency 'salsify_rubocop', '~> 1.85.1'
43
42
  spec.add_development_dependency 'simplecov'
44
43
  end
@@ -17,6 +17,29 @@ module Avro
17
17
  # When a DSL attribute is defined, the class also keeps track of the
18
18
  # attribute names.
19
19
  module DslAttributes
20
+ ATTRIBUTE_NAMES = [
21
+ :abstract,
22
+ :aliases,
23
+ :default,
24
+ :doc,
25
+ :items,
26
+ :logicalType,
27
+ :logical_type,
28
+ :namespace,
29
+ :order,
30
+ :precision,
31
+ :scale,
32
+ :size,
33
+ :symbols,
34
+ :type,
35
+ :type_aliases,
36
+ :type_doc,
37
+ :type_name,
38
+ :type_namespace,
39
+ :types,
40
+ :values
41
+ ].freeze
42
+
20
43
  def self.included(base)
21
44
  base.extend ClassMethods
22
45
  end
@@ -59,6 +82,10 @@ module Avro
59
82
  end
60
83
 
61
84
  def add_attribute_name(name)
85
+ unless ATTRIBUTE_NAMES.include?(name)
86
+ raise "DSL attribute #{name.inspect} must be added to Avro::Builder::DslAttributes::RESERVED_NAMES"
87
+ end
88
+
62
89
  dsl_attribute_names << name
63
90
  add_option_name(name)
64
91
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'avro/builder/type_factory'
4
4
  require 'avro/builder/aliasable'
5
+ require 'avro/builder/metadata'
5
6
 
6
7
  module Avro
7
8
  module Builder
@@ -13,33 +14,30 @@ module Avro
13
14
  include Avro::Builder::DslAttributes
14
15
  include Avro::Builder::Aliasable
15
16
  include Avro::Builder::AnonymousTypes
16
-
17
- INTERNAL_ATTRIBUTES = [:optional_field].to_set.freeze
17
+ include Avro::Builder::Metadata
18
18
 
19
19
  # These attributes may be set as options or via a block in the DSL
20
20
  dsl_attributes :doc, :default, :order
21
21
 
22
- def initialize(name:, avro_type_or_name:, record:, cache:, internal: {}, options: {}, &block)
22
+ def initialize(name:, avro_type_or_name:, record:, cache:, optional:, options: {}, &block)
23
23
  @cache = cache
24
24
  @record = record
25
25
  @name = name.to_s
26
-
27
- internal.each do |key, value|
28
- send("#{key}=", value) if INTERNAL_ATTRIBUTES.include?(key)
29
- end
26
+ @optional_field = optional
30
27
 
31
28
  type_options = options.dup
32
29
  options.keys.each do |key|
33
- send(key, type_options.delete(key)) if dsl_attribute?(key)
30
+ send(key, type_options.delete(key)) if dsl_attribute?(key) || extra_metadata_attribute?(key)
34
31
  end
35
32
 
36
33
  # Find existing Type or build a new instance of a builtin Type using
37
34
  # the supplied block
38
35
  @field_type = type_lookup(avro_type_or_name, namespace) do |avro_type_name|
36
+ type_internal = NAMED_TYPES.include?(avro_type_name.to_s) ? { type_namespace: namespace } : {}
39
37
  create_and_configure_builtin_type(avro_type_name,
40
38
  field: self,
41
39
  cache: cache,
42
- internal: internal,
40
+ internal: type_internal,
43
41
  validate_type: false,
44
42
  options: type_options)
45
43
  end
@@ -56,7 +54,15 @@ module Avro
56
54
  end
57
55
 
58
56
  def method_missing(id, *args, &block)
59
- field_type.dsl_respond_to?(id) ? field_type.send(id, *args, &block) : super
57
+ # We can't just rely on Metadata#method_missing because we want to give precedence to defining
58
+ # metadata on the field rather than the type
59
+ if extra_metadata_accessor?(id)
60
+ extra_metadata_access(id, args)
61
+ elsif field_type.dsl_respond_to?(id)
62
+ field_type.send(id, *args, &block)
63
+ else
64
+ super
65
+ end
60
66
  end
61
67
 
62
68
  def name_fragment
@@ -84,15 +90,15 @@ module Avro
84
90
  end
85
91
 
86
92
  def serialize(reference_state)
87
- # TODO: order is not included here
88
93
  {
89
94
  name: name,
90
95
  type: serialized_type(reference_state),
91
96
  doc: doc,
92
97
  default: default,
93
- aliases: aliases
94
- }.reject { |_, v| v.nil? }.tap do |result|
95
- result.merge!(default: nil) if optional_field
98
+ aliases: aliases,
99
+ order: order
100
+ }.merge(extra_metadata).compact.tap do |result|
101
+ result[:default] = nil if optional_field
96
102
  end
97
103
  end
98
104
 
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Avro
4
+ module Builder
5
+ module Metadata
6
+ @attribute_names = Set.new
7
+
8
+ class << self
9
+ def register(*attrs)
10
+ attrs = attrs.flatten.map(&:to_sym) - attribute_names.to_a
11
+
12
+ reserved = attrs & Avro::Builder::DslAttributes::ATTRIBUTE_NAMES
13
+ if reserved.any?
14
+ raise ArgumentError.new(
15
+ "Extra metadata attribute(s) use reserved name(s): #{reserved.join(', ')}"
16
+ )
17
+ end
18
+
19
+ attribute_names.merge(attrs)
20
+ end
21
+
22
+ def reset!
23
+ attribute_names.clear
24
+ end
25
+
26
+ def attribute?(name)
27
+ attribute_names.include?(name.to_sym)
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :attribute_names
33
+ end
34
+
35
+ def respond_to_missing?(id, include_all)
36
+ extra_metadata_accessor?(id) || super
37
+ end
38
+
39
+ def method_missing(id, *args, &block)
40
+ if extra_metadata_accessor?(id)
41
+ extra_metadata_access(id, args)
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def extra_metadata_attribute?(name)
50
+ Metadata.attribute?(name)
51
+ end
52
+
53
+ def extra_metadata
54
+ @extra_metadata ||= {}
55
+ end
56
+
57
+ def extra_metadata_accessor?(name)
58
+ name = name.to_s.delete_suffix('=').to_sym
59
+ extra_metadata_attribute?(name)
60
+ end
61
+
62
+ def extra_metadata_access(name, args)
63
+ name = name.to_s.delete_suffix('=').to_sym
64
+
65
+ if !extra_metadata_attribute?(name)
66
+ raise ArgumentError.new("Unknown metadata attribute: #{name}")
67
+ elsif args.size > 1
68
+ raise ArgumentError.new("Expected 0 or 1 arguments. Got #{args.size}")
69
+ elsif args.empty? || args.first.nil?
70
+ extra_metadata[name]
71
+ else
72
+ extra_metadata[name] = args.first
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -24,6 +24,15 @@ module Avro
24
24
  yield
25
25
  end
26
26
  end
27
+
28
+ def definition(fullname)
29
+ if references.include?(fullname)
30
+ raise ArgumentError.new("Type '#{fullname}' has already been defined")
31
+ else
32
+ references << fullname
33
+ yield
34
+ end
35
+ end
27
36
  end
28
37
  end
29
38
  end
@@ -6,8 +6,8 @@ module Avro
6
6
  # This concern is used by classes that create new Type instances.
7
7
  module TypeFactory
8
8
 
9
- NAMED_TYPES = ['enum', 'fixed', 'record'].map(&:freeze).to_set.freeze
10
- COMPLEX_TYPES = ['array', 'enum', 'fixed', 'map', 'record', 'union'].map(&:freeze).to_set.freeze
9
+ NAMED_TYPES = ['enum', 'fixed', 'record'].to_set.freeze
10
+ COMPLEX_TYPES = ['array', 'enum', 'fixed', 'map', 'record', 'union'].to_set.freeze
11
11
  BUILTIN_TYPES = Avro::Schema::PRIMITIVE_TYPES.union(COMPLEX_TYPES).freeze
12
12
 
13
13
  private
@@ -33,7 +33,7 @@ module Avro
33
33
  if name == 'bytes'
34
34
  Avro::Builder::Types::BytesType.new(field: field, cache: cache)
35
35
  elsif Avro::Schema::PRIMITIVE_TYPES.include?(name)
36
- Avro::Builder::Types::Type.new(name, field: field, cache: cache)
36
+ Avro::Builder::Types::PrimitiveType.new(name, field: field, cache: cache)
37
37
  elsif field.nil? && NAMED_TYPES.include?(name)
38
38
  Avro::Builder.const_get(name.capitalize).new(cache: cache)
39
39
  elsif COMPLEX_TYPES.include?(name)
@@ -3,8 +3,7 @@
3
3
  module Avro
4
4
  module Builder
5
5
  module Types
6
- class ArrayType < Type
7
- include Avro::Builder::Types::ComplexType
6
+ class ArrayType < ComplexType
8
7
  include Avro::Builder::Types::TypeReferencer
9
8
 
10
9
  dsl_attribute :items do |items_type = nil|
@@ -19,9 +18,10 @@ module Avro
19
18
  validate_required_attribute!(:items)
20
19
  end
21
20
 
22
- def serialize(referenced_state)
23
- super(referenced_state,
24
- overrides: { items: items.serialize(referenced_state) })
21
+ private
22
+
23
+ def serialized_attribute_hash(reference_state)
24
+ super.merge(items: items.serialize(reference_state))
25
25
  end
26
26
  end
27
27
  end
@@ -4,25 +4,17 @@ module Avro
4
4
  module Builder
5
5
  module Types
6
6
  # Subclass for the primitive Bytes type because it supports the decimal logical type.
7
- class BytesType < Type
7
+ class BytesType < PrimitiveType
8
8
  dsl_attributes :precision, :scale
9
9
 
10
10
  def initialize(cache:, field: nil)
11
11
  super('bytes', field: field, cache: cache)
12
12
  end
13
13
 
14
- def serialize(reference_state)
15
- super(reference_state, overrides: serialized_attributes)
16
- end
17
-
18
- def to_h(reference_state)
19
- super(reference_state, overrides: serialized_attributes)
20
- end
21
-
22
14
  private
23
15
 
24
- def serialized_attributes
25
- { precision: precision, scale: scale }
16
+ def serialized_attribute_hash(reference_state)
17
+ super.merge(precision: precision, scale: scale)
26
18
  end
27
19
  end
28
20
  end
@@ -4,12 +4,13 @@ module Avro
4
4
  module Builder
5
5
  module Types
6
6
 
7
- # This module provides common functionality for non-primitive types
8
- # that do not require a name to be created.
9
- module ComplexType
7
+ # This is an abstract class that provides common functionality for
8
+ # non-primitive types that do not require a name to be created.
9
+ class ComplexType < Type
10
10
 
11
- def self.included(base)
12
- base.extend ClassMethods
11
+ # Infer avro_type_name based on class
12
+ def self.avro_type_name
13
+ @avro_type_name ||= name.split('::').last.sub('Type', '').downcase.to_sym
13
14
  end
14
15
 
15
16
  # Override initialize so that type name is not required
@@ -20,25 +21,6 @@ module Avro
20
21
  def namespace
21
22
  field.namespace
22
23
  end
23
-
24
- def serialize(_reference_state, overrides: {})
25
- {
26
- type: avro_type_name,
27
- logicalType: logical_type
28
- }.merge(overrides).reject { |_, v| v.nil? }
29
- end
30
-
31
- def to_h(reference_state)
32
- serialize(reference_state)
33
- end
34
-
35
- module ClassMethods
36
-
37
- # Infer avro_type_name based on class
38
- def avro_type_name
39
- @avro_type_name ||= name.split('::').last.sub('Type', '').downcase.to_sym
40
- end
41
- end
42
24
  end
43
25
  end
44
26
  end
@@ -16,14 +16,6 @@ module Avro
16
16
  end
17
17
  end
18
18
 
19
- def serialize(reference_state)
20
- super(reference_state, overrides: serialized_attributes)
21
- end
22
-
23
- def to_h(reference_state)
24
- super(reference_state, overrides: serialized_attributes)
25
- end
26
-
27
19
  def validate!
28
20
  super
29
21
  validate_required_attribute!(:symbols)
@@ -38,8 +30,8 @@ module Avro
38
30
  end
39
31
  end
40
32
 
41
- def serialized_attributes
42
- { symbols: symbols, doc: doc, default: default }
33
+ def serialized_attribute_hash(_reference_state)
34
+ super.merge(symbols: symbols, doc: doc, default: default)
43
35
  end
44
36
  end
45
37
  end
@@ -7,14 +7,6 @@ module Avro
7
7
 
8
8
  dsl_attributes :size, :precision, :scale
9
9
 
10
- def serialize(reference_state)
11
- super(reference_state, overrides: serialized_attributes)
12
- end
13
-
14
- def to_h(reference_state)
15
- super(reference_state, overrides: serialized_attributes)
16
- end
17
-
18
10
  def validate!
19
11
  super
20
12
  validate_required_attribute!(:size)
@@ -22,8 +14,8 @@ module Avro
22
14
 
23
15
  private
24
16
 
25
- def serialized_attributes
26
- { size: size, precision: precision, scale: scale }
17
+ def serialized_attribute_hash(_reference_state)
18
+ super.merge(size: size, precision: precision, scale: scale)
27
19
  end
28
20
  end
29
21
  end
@@ -3,8 +3,7 @@
3
3
  module Avro
4
4
  module Builder
5
5
  module Types
6
- class MapType < Type
7
- include Avro::Builder::Types::ComplexType
6
+ class MapType < ComplexType
8
7
  include Avro::Builder::Types::TypeReferencer
9
8
 
10
9
  dsl_attribute :values do |value_type = nil|
@@ -15,14 +14,15 @@ module Avro
15
14
  end
16
15
  end
17
16
 
18
- def serialize(referenced_state)
19
- super(referenced_state,
20
- overrides: { values: values.serialize(referenced_state) })
21
- end
22
-
23
17
  def validate!
24
18
  validate_required_attribute!(:values)
25
19
  end
20
+
21
+ private
22
+
23
+ def serialized_attribute_hash(reference_state)
24
+ super.merge(values: values.serialize(reference_state))
25
+ end
26
26
  end
27
27
  end
28
28
  end
@@ -10,8 +10,7 @@ module Avro
10
10
 
11
11
  # This is an abstract class that represents a type that can be defined
12
12
  # with a name, outside a record.
13
- class NamedType < Type
14
- include Avro::Builder::Types::ComplexType
13
+ class NamedType < ComplexType
15
14
  include Avro::Builder::Namespaceable
16
15
  include Avro::Builder::Aliasable
17
16
 
@@ -55,33 +54,30 @@ module Avro
55
54
  end
56
55
 
57
56
  # As a type for a field
58
- # Subclasses may call super with additional overrides to be added
59
- # to the serialized value.
60
- def serialize(reference_state, overrides: {})
57
+ def serialize(reference_state)
58
+ # Return the full type definition if it hasn't been seen yet.
59
+ # Otherwise, just return its name.
61
60
  reference_state.definition_or_reference(fullname) do
62
- serialized_attribute_hash.merge(overrides).reject { |_, v| v.nil? }
61
+ super
63
62
  end
64
63
  end
65
64
 
66
65
  # As a top-level, named type
67
- # Subclasses may call super with additional overrides to be added
68
- # to the hash representation.
69
- def to_h(_reference_state, overrides: {})
70
- serialized_attribute_hash
71
- .merge(aliases: aliases)
72
- .merge(overrides)
73
- .reject { |_, v| v.nil? }
66
+ def to_h(reference_state)
67
+ # Always return the type definition. It's an error if the type has already been seen.
68
+ reference_state.definition(fullname) do
69
+ super
70
+ end
74
71
  end
75
72
 
76
73
  private
77
74
 
78
- def serialized_attribute_hash
79
- {
75
+ def serialized_attribute_hash(_reference_state)
76
+ super.merge(
80
77
  name: name,
81
- type: avro_type_name,
82
78
  namespace: namespace,
83
- logicalType: logical_type
84
- }
79
+ aliases: aliases
80
+ )
85
81
  end
86
82
  end
87
83
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Avro
4
+ module Builder
5
+ module Types
6
+ # Subclass of Type for primitive Avro types (null, boolean, int, long,
7
+ # float, double, bytes, string).
8
+ class PrimitiveType < Type
9
+ def serialize(reference_state)
10
+ if logical_type
11
+ super
12
+ else
13
+ avro_type_name
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -13,14 +13,9 @@ module Avro
13
13
  dsl_attribute :doc
14
14
  dsl_attribute_alias :type_doc, :doc
15
15
 
16
- def initialize(name = nil, cache:, options: {}, field: nil, &block) # rubocop:disable Lint/MissingSuper
17
- # TODO: Fix missing call to super
18
- @avro_type_name = :record
16
+ def initialize(name = nil, cache:, field: nil, &block)
17
+ super(cache: cache, field: field)
19
18
  @name = name
20
- @cache = cache
21
- @field = field
22
-
23
- configure_options(options)
24
19
  instance_eval(&block) if block_given?
25
20
  end
26
21
 
@@ -34,7 +29,7 @@ module Avro
34
29
  avro_type_or_name: avro_type_or_name,
35
30
  record: self,
36
31
  cache: cache,
37
- internal: { type_namespace: namespace },
32
+ optional: false,
38
33
  options: options,
39
34
  &block)
40
35
  add_field(new_field)
@@ -47,8 +42,7 @@ module Avro
47
42
  avro_type_or_name: avro_type_or_name,
48
43
  record: self,
49
44
  cache: cache,
50
- internal: { type_namespace: namespace,
51
- optional_field: true },
45
+ optional: true,
52
46
  options: options,
53
47
  &block)
54
48
  add_field(new_field)
@@ -60,21 +54,6 @@ module Avro
60
54
  fields.merge!(cache.lookup_named_type(name, options.delete(:namespace) || namespace).duplicated_fields)
61
55
  end
62
56
 
63
- def to_h(reference_state = SchemaSerializerReferenceState.new)
64
- reference_state.definition_or_reference(fullname) do
65
- {
66
- type: :record,
67
- name: name,
68
- namespace: namespace,
69
- doc: doc,
70
- aliases: aliases,
71
- logicalType: logical_type,
72
- fields: fields.values.map { |field| field.serialize(reference_state) }
73
- }.reject { |_, v| v.nil? }
74
- end
75
- end
76
- alias_method :serialize, :to_h
77
-
78
57
  protected
79
58
 
80
59
  def duplicated_fields
@@ -86,6 +65,13 @@ module Avro
86
65
 
87
66
  private
88
67
 
68
+ def serialized_attribute_hash(reference_state)
69
+ super.merge(
70
+ doc: doc,
71
+ fields: fields.values.map { |field| field.serialize(reference_state) }
72
+ )
73
+ end
74
+
89
75
  # Add field, replacing any existing field with the same name.
90
76
  def add_field(field)
91
77
  fields[field.name] = field
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'avro/builder/metadata'
4
+
3
5
  module Avro
4
6
  module Builder
5
7
  module Types
6
- # Base class for simple types. The type name is specified when the
7
- # type is constructed. The type has no additional attributes, and
8
- # the type is serialized as just the type name.
9
8
  class Type
10
9
  include Avro::Builder::DslOptions
11
10
  include Avro::Builder::DslAttributes
11
+ include Avro::Builder::Metadata
12
12
 
13
13
  dsl_attributes :logical_type, :abstract
14
14
 
@@ -24,16 +24,12 @@ module Avro
24
24
  !!abstract
25
25
  end
26
26
 
27
- def serialize(_reference_state, overrides: {})
28
- if logical_type
29
- serialized_attributes_hash(overrides)
30
- else
31
- avro_type_name
32
- end
27
+ def serialize(reference_state)
28
+ serialized_attribute_hash(reference_state).compact
33
29
  end
34
30
 
35
- def to_h(_reference_state, overrides: {})
36
- serialized_attributes_hash(overrides)
31
+ def to_h(reference_state)
32
+ serialized_attribute_hash(reference_state).compact
37
33
  end
38
34
 
39
35
  def namespace
@@ -42,7 +38,7 @@ module Avro
42
38
 
43
39
  def configure_options(options = {})
44
40
  options.each do |key, value|
45
- send("#{key}=", value) if dsl_option?(key)
41
+ send("#{key}=", value)
46
42
  end
47
43
  end
48
44
 
@@ -74,10 +70,8 @@ module Avro
74
70
 
75
71
  private
76
72
 
77
- def serialized_attributes_hash(overrides)
78
- { type: avro_type_name, logicalType: logical_type }
79
- .merge(overrides)
80
- .reject { |_, v| v.nil? }
73
+ def serialized_attribute_hash(_reference_state)
74
+ extra_metadata.merge(type: avro_type_name, logicalType: logical_type)
81
75
  end
82
76
 
83
77
  def required_attribute_error!(attribute_name)
@@ -3,8 +3,7 @@
3
3
  module Avro
4
4
  module Builder
5
5
  module Types
6
- class UnionType < Type
7
- include Avro::Builder::Types::ComplexType
6
+ class UnionType < ComplexType
8
7
  include Avro::Builder::Types::TypeReferencer
9
8
 
10
9
  NULL_TYPE = 'null'
@@ -18,8 +17,12 @@ module Avro
18
17
  end
19
18
 
20
19
  # Unions are serialized as an array of types
21
- def serialize(referenced_state)
22
- types.map { |type| type.serialize(referenced_state) }
20
+ def serialize(reference_state)
21
+ types.map { |type| type.serialize(reference_state) }
22
+ end
23
+
24
+ def to_h(reference_state)
25
+ serialize(reference_state)
23
26
  end
24
27
 
25
28
  # serialized will be an array of types. If the array includes
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'avro/builder/types/type'
4
+ require 'avro/builder/types/primitive_type'
4
5
  require 'avro/builder/types/bytes_type'
5
6
  require 'avro/builder/types/complex_type'
6
7
  require 'avro/builder/types/type_referencer'
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Avro
4
4
  module Builder
5
- VERSION = '2.1.0'
5
+ VERSION = '3.0.0'
6
6
  end
7
7
  end
data/lib/avro/builder.rb CHANGED
@@ -26,6 +26,16 @@ module Avro
26
26
  def self.add_load_path(*paths)
27
27
  Avro::Builder::DSL.load_paths.merge(paths)
28
28
  end
29
+
30
+ # Define extra allowable metadata attributes for fields and types
31
+ def self.extra_metadata_attributes(*attrs)
32
+ Avro::Builder::Metadata.register(*attrs)
33
+ end
34
+
35
+ # Reset extra metadata attributes, removing all registered attributes
36
+ def self.reset_extra_metadata_attributes!
37
+ Avro::Builder::Metadata.reset!
38
+ end
29
39
  end
30
40
  end
31
41
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: avro-builder
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Salsify Engineering
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-08-05 00:00:00.000000000 Z
11
+ date: 2026-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: avro
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 1.9.0
19
+ version: 1.11.0
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '1.13'
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 1.9.0
29
+ version: 1.11.0
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '1.13'
@@ -44,20 +44,6 @@ dependencies:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0'
47
- - !ruby/object:Gem::Dependency
48
- name: bundler
49
- requirement: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '2.0'
54
- type: :development
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: '2.0'
61
47
  - !ruby/object:Gem::Dependency
62
48
  name: json_spec
63
49
  requirement: !ruby/object:Gem::Requirement
@@ -92,28 +78,28 @@ dependencies:
92
78
  requirements:
93
79
  - - "~>"
94
80
  - !ruby/object:Gem::Version
95
- version: '10.0'
81
+ version: '13.0'
96
82
  type: :development
97
83
  prerelease: false
98
84
  version_requirements: !ruby/object:Gem::Requirement
99
85
  requirements:
100
86
  - - "~>"
101
87
  - !ruby/object:Gem::Version
102
- version: '10.0'
88
+ version: '13.0'
103
89
  - !ruby/object:Gem::Dependency
104
90
  name: rspec
105
91
  requirement: !ruby/object:Gem::Requirement
106
92
  requirements:
107
93
  - - "~>"
108
94
  - !ruby/object:Gem::Version
109
- version: '3.0'
95
+ version: '3.13'
110
96
  type: :development
111
97
  prerelease: false
112
98
  version_requirements: !ruby/object:Gem::Requirement
113
99
  requirements:
114
100
  - - "~>"
115
101
  - !ruby/object:Gem::Version
116
- version: '3.0'
102
+ version: '3.13'
117
103
  - !ruby/object:Gem::Dependency
118
104
  name: rspec-its
119
105
  requirement: !ruby/object:Gem::Requirement
@@ -148,14 +134,14 @@ dependencies:
148
134
  requirements:
149
135
  - - "~>"
150
136
  - !ruby/object:Gem::Version
151
- version: 1.0.1
137
+ version: 1.85.1
152
138
  type: :development
153
139
  prerelease: false
154
140
  version_requirements: !ruby/object:Gem::Requirement
155
141
  requirements:
156
142
  - - "~>"
157
143
  - !ruby/object:Gem::Version
158
- version: 1.0.1
144
+ version: 1.85.1
159
145
  - !ruby/object:Gem::Dependency
160
146
  name: simplecov
161
147
  requirement: !ruby/object:Gem::Requirement
@@ -193,10 +179,8 @@ files:
193
179
  - avro-builder.gemspec
194
180
  - bin/console
195
181
  - bin/setup
196
- - gemfiles/avro_1.10.gemfile
197
182
  - gemfiles/avro_1.11.gemfile
198
183
  - gemfiles/avro_1.12.gemfile
199
- - gemfiles/avro_1.9.gemfile
200
184
  - lib/avro/builder.rb
201
185
  - lib/avro/builder/aliasable.rb
202
186
  - lib/avro/builder/anonymous_types.rb
@@ -209,6 +193,7 @@ files:
209
193
  - lib/avro/builder/field.rb
210
194
  - lib/avro/builder/file_handler.rb
211
195
  - lib/avro/builder/fixed.rb
196
+ - lib/avro/builder/metadata.rb
212
197
  - lib/avro/builder/namespaceable.rb
213
198
  - lib/avro/builder/railtie.rb
214
199
  - lib/avro/builder/rake/avro_generate_task.rb
@@ -226,6 +211,7 @@ files:
226
211
  - lib/avro/builder/types/map_type.rb
227
212
  - lib/avro/builder/types/named_error_handling.rb
228
213
  - lib/avro/builder/types/named_type.rb
214
+ - lib/avro/builder/types/primitive_type.rb
229
215
  - lib/avro/builder/types/record_type.rb
230
216
  - lib/avro/builder/types/top_level.rb
231
217
  - lib/avro/builder/types/type.rb
@@ -246,14 +232,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
246
232
  requirements:
247
233
  - - ">="
248
234
  - !ruby/object:Gem::Version
249
- version: '2.7'
235
+ version: '3.3'
250
236
  required_rubygems_version: !ruby/object:Gem::Requirement
251
237
  requirements:
252
238
  - - ">="
253
239
  - !ruby/object:Gem::Version
254
240
  version: '0'
255
241
  requirements: []
256
- rubygems_version: 3.3.26
242
+ rubygems_version: 3.5.22
257
243
  signing_key:
258
244
  specification_version: 4
259
245
  summary: Ruby DSL to create Avro schemas
@@ -1,7 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "avro", "1.10.2"
6
-
7
- gemspec path: "../"
@@ -1,7 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "avro", "1.9.2"
6
-
7
- gemspec path: "../"