avro-builder 0.4.0 → 0.5.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 +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/avro/builder/definition_cache.rb +60 -0
- data/lib/avro/builder/dsl.rb +7 -29
- data/lib/avro/builder/errors.rb +28 -0
- data/lib/avro/builder/field.rb +18 -6
- data/lib/avro/builder/type_factory.rb +5 -7
- data/lib/avro/builder/types/complex_type.rb +4 -3
- data/lib/avro/builder/types/named_type.rb +4 -0
- data/lib/avro/builder/types/record_type.rb +10 -13
- data/lib/avro/builder/types/type.rb +12 -2
- data/lib/avro/builder/types/type_referencer.rb +2 -6
- data/lib/avro/builder/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6a6a7c469ac63da10199ce29df3e1b6d82e6904a
|
4
|
+
data.tar.gz: aa7a5d4506499cc9a75ce963e54254d6ded16b49
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2803b8ebb4bedb1256041eaf4533c2c1309e416fb7152ef3f55e76191e32b1af17608895d42e729b026a1ca0edada181aa779993b4ddfd485dcd594d824c057f
|
7
|
+
data.tar.gz: bba50fb4e40081ab522201c764f90c583e351b3be14b2ec237c850565bc84fad1e12ab42b8f8fc32289286e380f1cd41c2c600a10e5afe3bb316a43726304f67
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# avro-builder changelog
|
2
2
|
|
3
|
+
## v0.5.0
|
4
|
+
- Support references to named types that are defined inline.
|
5
|
+
- Raise an error for duplicate definitions with the same fullname.
|
6
|
+
|
3
7
|
## v0.4.0
|
4
8
|
- Add validation for required DSL attributes that are not specified.
|
5
9
|
- Allow name to be configured via a block for top-level record, enum, and fixed
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Avro
|
2
|
+
module Builder
|
3
|
+
|
4
|
+
# This class is used to cache previously defined schema objects
|
5
|
+
# preventing direct access via the DSL.
|
6
|
+
class DefinitionCache
|
7
|
+
def initialize(builder)
|
8
|
+
@builder = builder
|
9
|
+
@schema_objects = {}
|
10
|
+
@schema_names = Set.new
|
11
|
+
end
|
12
|
+
|
13
|
+
# Cache and schema object by name (for convenience) and fullname.
|
14
|
+
# The schema object is only available by name if the unqualified name is unique.
|
15
|
+
def add_schema_object(object)
|
16
|
+
store_by_name(object) if object.namespace
|
17
|
+
store_by_fullname(object)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Lookup an Avro schema object by name, possibly fully qualified by namespace.
|
21
|
+
def lookup_named_type(key, namespace = nil)
|
22
|
+
key_str = Avro::Name.make_fullname(key.to_s, namespace && namespace.to_s)
|
23
|
+
object = schema_objects[key_str]
|
24
|
+
|
25
|
+
if object.nil? && !schema_names.include?(key.to_s)
|
26
|
+
object = builder.import(key)
|
27
|
+
end
|
28
|
+
|
29
|
+
raise DefinitionNotFoundError.new(key) if object.nil? && namespace.nil?
|
30
|
+
|
31
|
+
# Return object or retry without namespace
|
32
|
+
object || lookup_named_type(key, nil)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Schemas are stored by name, provided that the name is unique.
|
38
|
+
# If the unqualified name is ambiguous then it is removed from the cache.
|
39
|
+
# A set of unqualified names is kept to avoid reloading files for
|
40
|
+
# ambiguous references.
|
41
|
+
def store_by_name(object)
|
42
|
+
key = object.name.to_s
|
43
|
+
if schema_objects.key?(key)
|
44
|
+
schema_objects.delete(key)
|
45
|
+
elsif !schema_names.include?(key)
|
46
|
+
schema_objects.store(key, object)
|
47
|
+
end
|
48
|
+
schema_names.add(key)
|
49
|
+
end
|
50
|
+
|
51
|
+
def store_by_fullname(object)
|
52
|
+
key = object.fullname
|
53
|
+
raise DuplicateDefinitionError.new(key, object, schema_objects[key]) if schema_objects.key?(key)
|
54
|
+
schema_objects.store(key, object)
|
55
|
+
end
|
56
|
+
|
57
|
+
attr_reader :schema_objects, :schema_names, :builder
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/avro/builder/dsl.rb
CHANGED
@@ -2,6 +2,7 @@ require 'avro'
|
|
2
2
|
require 'avro/builder/errors'
|
3
3
|
require 'avro/builder/dsl_attributes'
|
4
4
|
require 'avro/builder/namespaceable'
|
5
|
+
require 'avro/builder/definition_cache'
|
5
6
|
require 'avro/builder/type_factory'
|
6
7
|
require 'avro/builder/types'
|
7
8
|
require 'avro/builder/field'
|
@@ -33,8 +34,9 @@ module Avro
|
|
33
34
|
# Imports from the file with specified name fragment.
|
34
35
|
def import(name)
|
35
36
|
previous_namespace = namespace
|
36
|
-
eval_file(name)
|
37
|
+
result = eval_file(name)
|
37
38
|
namespace(previous_namespace)
|
39
|
+
result
|
38
40
|
end
|
39
41
|
|
40
42
|
## DSL methods for Types
|
@@ -48,20 +50,6 @@ module Avro
|
|
48
50
|
create_named_type(name, :fixed, size_option.merge(options), &block)
|
49
51
|
end
|
50
52
|
|
51
|
-
# Lookup an Avro schema object by name, possibly fully qualified by namespace.
|
52
|
-
def lookup_named_type(key)
|
53
|
-
key_str = key.to_s
|
54
|
-
object = schema_objects[key_str]
|
55
|
-
|
56
|
-
unless object
|
57
|
-
import(key)
|
58
|
-
object = schema_objects[key_str]
|
59
|
-
end
|
60
|
-
|
61
|
-
raise "Schema object #{key} not found" unless object
|
62
|
-
object
|
63
|
-
end
|
64
|
-
|
65
53
|
# Return the last schema object processed as a Hash representing
|
66
54
|
# the Avro schema.
|
67
55
|
def to_h
|
@@ -85,28 +73,18 @@ module Avro
|
|
85
73
|
|
86
74
|
private
|
87
75
|
|
88
|
-
def
|
89
|
-
self
|
90
|
-
end
|
91
|
-
|
92
|
-
def schema_objects
|
93
|
-
@schema_objects ||= {}
|
94
|
-
end
|
95
|
-
|
96
|
-
def add_schema_object(object)
|
97
|
-
@last_object = object
|
98
|
-
schema_objects[object.name.to_s] = object
|
99
|
-
schema_objects[object.fullname] = object if object.namespace
|
76
|
+
def cache
|
77
|
+
@cache ||= Avro::Builder::DefinitionCache.new(self)
|
100
78
|
end
|
101
79
|
|
102
80
|
def create_named_type(name, type_name, options = {}, &block)
|
103
81
|
create_and_configure_builtin_type(type_name,
|
104
|
-
|
82
|
+
cache: cache,
|
105
83
|
internal: { name: name, namespace: namespace },
|
106
84
|
options: options,
|
107
85
|
&block).tap do |type|
|
108
86
|
type.validate!
|
109
|
-
add_schema_object(type)
|
87
|
+
@last_object = cache.add_schema_object(type)
|
110
88
|
end
|
111
89
|
end
|
112
90
|
|
data/lib/avro/builder/errors.rb
CHANGED
@@ -11,5 +11,33 @@ module Avro
|
|
11
11
|
super("attribute :#{attribute} missing for #{location}type :#{type}")
|
12
12
|
end
|
13
13
|
end
|
14
|
+
|
15
|
+
class DuplicateDefinitionError < StandardError
|
16
|
+
def initialize(key, object, existing_object)
|
17
|
+
super("definition for #{key.inspect} already exists\n"\
|
18
|
+
"existing definition:\n#{to_json(existing_object)}\n"\
|
19
|
+
"new definition:\n#{to_json(object)})")
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def to_json(object)
|
25
|
+
object.to_h(SchemaSerializerReferenceState.new).to_json
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class DefinitionNotFoundError < StandardError
|
30
|
+
def initialize(name)
|
31
|
+
super("definition not found for '#{name.to_s}'.#{suggest_namespace(name)}")
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def suggest_namespace(name)
|
37
|
+
unless name.to_s.index('.')
|
38
|
+
' Try specifying the full namespace.'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
14
42
|
end
|
15
43
|
end
|
data/lib/avro/builder/field.rb
CHANGED
@@ -7,18 +7,15 @@ module Avro
|
|
7
7
|
# A field must be initialized with a type.
|
8
8
|
class Field
|
9
9
|
include Avro::Builder::DslAttributes
|
10
|
-
include Avro::Builder::Namespaceable
|
11
10
|
include Avro::Builder::TypeFactory
|
12
11
|
|
13
12
|
INTERNAL_ATTRIBUTES = Set.new(%i(optional_field)).freeze
|
14
13
|
|
15
|
-
attr_accessor :type, :optional_field, :builder, :record
|
16
|
-
|
17
14
|
# These attributes may be set as options or via a block in the DSL
|
18
15
|
dsl_attributes :doc, :aliases, :default, :order
|
19
16
|
|
20
|
-
def initialize(name:, type_name:, record:,
|
21
|
-
@
|
17
|
+
def initialize(name:, type_name:, record:, cache:, internal: {}, options: {}, &block)
|
18
|
+
@cache = cache
|
22
19
|
@record = record
|
23
20
|
@name = name.to_s
|
24
21
|
|
@@ -33,15 +30,18 @@ module Avro
|
|
33
30
|
@type = if builtin_type?(type_name)
|
34
31
|
create_and_configure_builtin_type(type_name,
|
35
32
|
field: self,
|
33
|
+
cache: cache,
|
36
34
|
internal: internal,
|
37
35
|
options: options)
|
38
36
|
else
|
39
|
-
|
37
|
+
named_type = true
|
38
|
+
cache.lookup_named_type(type_name, namespace)
|
40
39
|
end
|
41
40
|
|
42
41
|
# DSL calls must be evaluated after the type has been constructed
|
43
42
|
instance_eval(&block) if block_given?
|
44
43
|
@type.validate!
|
44
|
+
@type.cache! unless named_type
|
45
45
|
end
|
46
46
|
|
47
47
|
## Delegate additional DSL calls to the type
|
@@ -58,6 +58,16 @@ module Avro
|
|
58
58
|
record.name_fragment
|
59
59
|
end
|
60
60
|
|
61
|
+
# Delegate setting namespace explicitly via DSL to type
|
62
|
+
# and return the namespace value from the enclosing record.
|
63
|
+
def namespace(value = nil)
|
64
|
+
if value
|
65
|
+
type.namespace(value)
|
66
|
+
else
|
67
|
+
record.namespace
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
61
71
|
# Delegate setting name explicitly via DSL to type
|
62
72
|
def name(value = nil)
|
63
73
|
if value
|
@@ -82,6 +92,8 @@ module Avro
|
|
82
92
|
|
83
93
|
private
|
84
94
|
|
95
|
+
attr_accessor :type, :optional_field, :cache, :record
|
96
|
+
|
85
97
|
# Optional fields must be serialized as a union -- an array of types.
|
86
98
|
def serialized_type(reference_state)
|
87
99
|
result = type.serialize(reference_state)
|
@@ -10,13 +10,13 @@ module Avro
|
|
10
10
|
private
|
11
11
|
|
12
12
|
# Return a new Type instance
|
13
|
-
def create_builtin_type(type_name)
|
13
|
+
def create_builtin_type(type_name, field:, cache:)
|
14
14
|
name = type_name.to_s.downcase
|
15
15
|
case
|
16
16
|
when Avro::Schema::PRIMITIVE_TYPES.include?(name)
|
17
|
-
Avro::Builder::Types::Type.new(name)
|
17
|
+
Avro::Builder::Types::Type.new(name, field: field, cache: cache)
|
18
18
|
when COMPLEX_TYPES.include?(name)
|
19
|
-
Avro::Builder::Types.const_get("#{name.capitalize}Type").new
|
19
|
+
Avro::Builder::Types.const_get("#{name.capitalize}Type").new(field: field, cache: cache)
|
20
20
|
else
|
21
21
|
raise "Invalid builtin type: #{type_name}"
|
22
22
|
end
|
@@ -26,13 +26,11 @@ module Avro
|
|
26
26
|
# and setting attributes via the DSL
|
27
27
|
def create_and_configure_builtin_type(type_name,
|
28
28
|
field: nil,
|
29
|
-
|
29
|
+
cache: nil,
|
30
30
|
internal: {},
|
31
31
|
options: {},
|
32
32
|
&block)
|
33
|
-
create_builtin_type(type_name).tap do |type|
|
34
|
-
type.field = field
|
35
|
-
type.builder = builder
|
33
|
+
create_builtin_type(type_name, field: field, cache: cache).tap do |type|
|
36
34
|
type.configure_options(internal.merge(options))
|
37
35
|
type.instance_eval(&block) if block_given?
|
38
36
|
end
|
@@ -11,11 +11,12 @@ module Avro
|
|
11
11
|
end
|
12
12
|
|
13
13
|
# Override initialize so that type name is not required
|
14
|
-
def initialize
|
14
|
+
def initialize(cache:, field: nil)
|
15
|
+
super(self.class.type_name, cache: cache, field: field)
|
15
16
|
end
|
16
17
|
|
17
|
-
def
|
18
|
-
|
18
|
+
def namespace
|
19
|
+
field.namespace
|
19
20
|
end
|
20
21
|
|
21
22
|
module ClassMethods
|
@@ -26,6 +26,10 @@ module Avro
|
|
26
26
|
required_attribute_error!(:name) if field.nil? && @name.nil?
|
27
27
|
end
|
28
28
|
|
29
|
+
def cache!
|
30
|
+
cache.add_schema_object(self)
|
31
|
+
end
|
32
|
+
|
29
33
|
# Named types that do not have an explicit name are assigned
|
30
34
|
# a named based on the field and its nesting.
|
31
35
|
def name_fragment
|
@@ -7,11 +7,14 @@ module Avro
|
|
7
7
|
|
8
8
|
dsl_attributes :doc
|
9
9
|
|
10
|
-
def initialize(name = nil, options
|
10
|
+
def initialize(name = nil, options: {}, cache:, field: nil, &block)
|
11
|
+
@type_name = :record
|
11
12
|
@name = name
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
@cache = cache
|
14
|
+
@field = field
|
15
|
+
|
16
|
+
configure_options(options)
|
17
|
+
instance_eval(&block) if block_given?
|
15
18
|
end
|
16
19
|
|
17
20
|
# Add a required field to the record
|
@@ -19,7 +22,7 @@ module Avro
|
|
19
22
|
new_field = Avro::Builder::Field.new(name: name,
|
20
23
|
type_name: type_name,
|
21
24
|
record: self,
|
22
|
-
|
25
|
+
cache: cache,
|
23
26
|
internal: { namespace: namespace },
|
24
27
|
options: options,
|
25
28
|
&block)
|
@@ -32,7 +35,7 @@ module Avro
|
|
32
35
|
new_field = Avro::Builder::Field.new(name: name,
|
33
36
|
type_name: type_name,
|
34
37
|
record: self,
|
35
|
-
|
38
|
+
cache: cache,
|
36
39
|
internal: { namespace: namespace,
|
37
40
|
optional_field: true },
|
38
41
|
options: options,
|
@@ -43,7 +46,7 @@ module Avro
|
|
43
46
|
# Adds fields from the record with the specified name to the current
|
44
47
|
# record.
|
45
48
|
def extends(name)
|
46
|
-
fields.merge!(
|
49
|
+
fields.merge!(cache.lookup_named_type(name, namespace).duplicated_fields)
|
47
50
|
end
|
48
51
|
|
49
52
|
def to_h(reference_state = SchemaSerializerReferenceState.new)
|
@@ -79,12 +82,6 @@ module Avro
|
|
79
82
|
def fields
|
80
83
|
@fields ||= {}
|
81
84
|
end
|
82
|
-
|
83
|
-
# A record may be defined as a top-level schema or as the
|
84
|
-
# type for a field.
|
85
|
-
def builder
|
86
|
-
super || field.builder
|
87
|
-
end
|
88
85
|
end
|
89
86
|
end
|
90
87
|
end
|
@@ -8,10 +8,11 @@ module Avro
|
|
8
8
|
include Avro::Builder::DslAttributes
|
9
9
|
|
10
10
|
attr_reader :type_name
|
11
|
-
attr_accessor :field, :builder
|
12
11
|
|
13
|
-
def initialize(type_name)
|
12
|
+
def initialize(type_name, cache:, field: nil)
|
14
13
|
@type_name = type_name
|
14
|
+
@cache = cache
|
15
|
+
@field = field
|
15
16
|
end
|
16
17
|
|
17
18
|
def serialize(_reference_state)
|
@@ -36,6 +37,11 @@ module Avro
|
|
36
37
|
def validate!
|
37
38
|
end
|
38
39
|
|
40
|
+
# Subclasses should override this method if the type definition should
|
41
|
+
# be cached for reuse.
|
42
|
+
def cache!
|
43
|
+
end
|
44
|
+
|
39
45
|
private
|
40
46
|
|
41
47
|
def required_attribute_error!(attribute_name)
|
@@ -51,6 +57,10 @@ module Avro
|
|
51
57
|
required_attribute_error!(attribute_name)
|
52
58
|
end
|
53
59
|
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
attr_accessor :field, :cache
|
54
64
|
end
|
55
65
|
end
|
56
66
|
end
|
@@ -8,15 +8,11 @@ module Avro
|
|
8
8
|
module TypeReferencer
|
9
9
|
include Avro::Builder::TypeFactory
|
10
10
|
|
11
|
-
def builder
|
12
|
-
(!field.nil? && field.builder) || super
|
13
|
-
end
|
14
|
-
|
15
11
|
def create_builtin_or_lookup_named_type(type_name)
|
16
12
|
if builtin_type?(type_name)
|
17
|
-
create_builtin_type(type_name)
|
13
|
+
create_builtin_type(type_name, field: field, cache: cache)
|
18
14
|
else
|
19
|
-
|
15
|
+
cache.lookup_named_type(type_name)
|
20
16
|
end
|
21
17
|
end
|
22
18
|
end
|
data/lib/avro/builder/version.rb
CHANGED
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: 0.
|
4
|
+
version: 0.5.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: 2016-05-
|
11
|
+
date: 2016-05-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: avro
|
@@ -113,6 +113,7 @@ files:
|
|
113
113
|
- bin/console
|
114
114
|
- bin/setup
|
115
115
|
- lib/avro/builder.rb
|
116
|
+
- lib/avro/builder/definition_cache.rb
|
116
117
|
- lib/avro/builder/dsl.rb
|
117
118
|
- lib/avro/builder/dsl_attributes.rb
|
118
119
|
- lib/avro/builder/errors.rb
|