avro-builder 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|