avro-builder 0.2.0 → 0.3.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
  SHA1:
3
- metadata.gz: a72124e0979079150b62e4ca3f8f6c727b7bb39c
4
- data.tar.gz: 158fc532ae1587737c415069bdcbf3ae136deeaa
3
+ metadata.gz: 28daf07aca9de6893dcd2ca08f78448e2a6df151
4
+ data.tar.gz: 0e3024fb6bf1caeaa24f65e79dc601802b663164
5
5
  SHA512:
6
- metadata.gz: 7f40595449fa8ce0db3ef04d35d96bd3dd133484a383ff1873bdb3c5f1061124c5398b17d31f881e479df603f940c7b3068e9112dbf2156b1a8659e537669b09
7
- data.tar.gz: 7d363c07489a17ca4142c34e8a7928f5f737fac535553326d6a2c63007d1c3d0f3b6c6034f1879c9eded1281a2d08d5a51cbac3ef0a7057978edf847ba8ef340
6
+ metadata.gz: a5b7073dd2493279f98724a1c8fc96fadaee310e80109978754e461f4475966f0fdade6137d08055de80f7e9a2a945123937a4fd15f76b683a948b9d91dbb413
7
+ data.tar.gz: e577b679d984e96cb849b478306b08527f0c831fcdbede83292e760d53ac125d7c622304f80b5d2a190e867ca7a2d34f6bae3e3c27e67f97c713351eefb98dbe
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # avro-builder changelog
2
2
 
3
+ ## v0.3.0
4
+ - Add support for nested records. This includes the ability to reference a
5
+ previously defined record as a type.
6
+
3
7
  ## v0.2.0
4
8
  - Add support for `:union` type.
5
9
  - Make `fixed` syntax more flexible. Both `fixed :f, 7` and `fixed :f, size: 7`
data/README.md CHANGED
@@ -141,6 +141,58 @@ record :my_record_with_named do
141
141
  end
142
142
  ```
143
143
 
144
+ ### Nested Records
145
+
146
+ Nested records may be created by referring to the name of the previously
147
+ defined record or using the field type `:record`.
148
+
149
+ ```ruby
150
+ record :sub_rec do
151
+ required :i, :int
152
+ end
153
+
154
+ record :top_rec do
155
+ required :sub, :sub_rec
156
+ end
157
+ ```
158
+
159
+ Definining a subrecord inline:
160
+
161
+ ```ruby
162
+ record :my_rec do
163
+ required :nested, :record do
164
+ required :s, :string
165
+ end
166
+ end
167
+ ```
168
+
169
+ Nested record types defined without an explicit name are given a generated
170
+ name based on the name of the field and record that they are nested within.
171
+ In the example above, the nested record type would have the generated name
172
+ `__my_rec_nested_record`:
173
+
174
+ ```json
175
+ {
176
+ "type": "record",
177
+ "name": "my_rec",
178
+ "fields": [
179
+ {
180
+ "name": "nested",
181
+ "type": {
182
+ "type": "record",
183
+ "name": "__my_rec_nested_record",
184
+ "fields": [
185
+ {
186
+ "name": "s",
187
+ "type": "string"
188
+ }
189
+ ]
190
+ }
191
+ }
192
+ ]
193
+ }
194
+ ```
195
+
144
196
  ### Unions
145
197
 
146
198
  A union may be specified within a record using `required` and `optional` with
@@ -4,7 +4,6 @@ require 'avro/builder/namespaceable'
4
4
  require 'avro/builder/type_factory'
5
5
  require 'avro/builder/types'
6
6
  require 'avro/builder/field'
7
- require 'avro/builder/record'
8
7
  require 'avro/builder/file_handler'
9
8
  require 'avro/builder/schema_serializer_reference_state'
10
9
 
@@ -85,6 +84,8 @@ module Avro
85
84
  def to_json(validate: true, pretty: true)
86
85
  hash = to_h
87
86
  (pretty ? JSON.pretty_generate(hash) : hash.to_json).tap do |json|
87
+ # Uncomment the next line to debug:
88
+ # puts json
88
89
  # Parse the schema to validate before returning
89
90
  ::Avro::Schema.parse(json) if validate
90
91
  end
@@ -111,10 +112,11 @@ module Avro
111
112
  end
112
113
 
113
114
  def build_record(name, options, &block)
114
- Record.new(name, options.merge(namespace: namespace)).tap do |record|
115
- record.builder = builder
116
- record.instance_eval(&block)
117
- end
115
+ Avro::Builder::Types::RecordType
116
+ .new(name, options.merge(namespace: namespace)).tap do |record|
117
+ record.builder = builder
118
+ record.instance_eval(&block)
119
+ end
118
120
  end
119
121
 
120
122
  def eval_file(name)
@@ -10,15 +10,16 @@ module Avro
10
10
  include Avro::Builder::Namespaceable
11
11
  include Avro::Builder::TypeFactory
12
12
 
13
- INTERNAL_ATTRIBUTES = Set.new(%i(optional)).freeze
13
+ INTERNAL_ATTRIBUTES = Set.new(%i(optional_field)).freeze
14
14
 
15
- attr_accessor :type, :optional, :builder
15
+ attr_accessor :type, :optional_field, :builder, :record
16
16
 
17
17
  # These attributes may be set as options or via a block in the DSL
18
18
  dsl_attributes :doc, :aliases, :default, :order
19
19
 
20
- def initialize(name:, type_name:, builder:, internal: {}, options: {}, &block)
20
+ def initialize(name:, type_name:, record:, builder:, internal: {}, options: {}, &block)
21
21
  @builder = builder
22
+ @record = record
22
23
  @name = name.to_s
23
24
 
24
25
  internal.each do |key, value|
@@ -42,8 +43,12 @@ module Avro
42
43
  super || type.respond_to?(id, include_all)
43
44
  end
44
45
 
45
- def method_missing(id, *args)
46
- type.respond_to?(id) ? type.send(id, *args) : super
46
+ def method_missing(id, *args, &block)
47
+ type.respond_to?(id) ? type.send(id, *args, &block) : super
48
+ end
49
+
50
+ def name_fragment
51
+ record.name_fragment
47
52
  end
48
53
 
49
54
  # Delegate setting name explicitly via DSL to type
@@ -68,10 +73,10 @@ module Avro
68
73
 
69
74
  private
70
75
 
71
- # Optional types must be serialized as a union -- an array of types.
76
+ # Optional fields must be serialized as a union -- an array of types.
72
77
  def serialized_type(reference_state)
73
78
  result = type.serialize(reference_state)
74
- optional ? type.class.union_with_null(result) : result
79
+ optional_field ? type.class.union_with_null(result) : result
75
80
  end
76
81
  end
77
82
  end
@@ -12,10 +12,21 @@ module Avro
12
12
  include Avro::Builder::Namespaceable
13
13
  include Avro::Builder::Types::ConfigurableType
14
14
 
15
- dsl_attributes :name, :namespace, :aliases
15
+ dsl_attributes :namespace, :aliases
16
16
 
17
- def generated_name
18
- name || "__#{field.name}_#{type_name}"
17
+ dsl_attribute :name do |value = nil|
18
+ if value
19
+ @name = value
20
+ else
21
+ @name || "__#{name_fragment}_#{type_name}"
22
+ end
23
+ end
24
+
25
+ # Named types that do not have an explicit name are assigned
26
+ # a named based on the field and its nesting.
27
+ def name_fragment
28
+ [field && field.name_fragment,
29
+ @name || (field && field.name)].compact.join('_')
19
30
  end
20
31
 
21
32
  # As a type for a field
@@ -24,7 +35,7 @@ module Avro
24
35
  def serialize(reference_state, overrides: {})
25
36
  reference_state.definition_or_reference(fullname) do
26
37
  {
27
- name: generated_name,
38
+ name: name,
28
39
  type: type_name,
29
40
  namespace: namespace
30
41
  }.merge(overrides).reject { |_, v| v.nil? }
@@ -0,0 +1,91 @@
1
+ module Avro
2
+ module Builder
3
+ module Types
4
+ # This class represents a record in an Avro schema. Records may be defined
5
+ # at the top-level or as the type for a field in a record.
6
+ class RecordType < Avro::Builder::Types::NamedType
7
+
8
+ dsl_attributes :doc
9
+
10
+ def initialize(name = nil, options = {})
11
+ @name = name
12
+ options.each do |key, value|
13
+ send(key, value)
14
+ end
15
+ end
16
+
17
+ # Add a required field to the record
18
+ def required(name, type_name, options = {}, &block)
19
+ new_field = Avro::Builder::Field.new(name: name,
20
+ type_name: type_name,
21
+ record: self,
22
+ builder: builder,
23
+ internal: { namespace: namespace },
24
+ options: options,
25
+ &block)
26
+ add_field(new_field)
27
+ end
28
+
29
+ # Add an optional field to the record. In Avro this is represented
30
+ # as a union of null and the type specified here.
31
+ def optional(name, type_name, options = {}, &block)
32
+ new_field = Avro::Builder::Field.new(name: name,
33
+ type_name: type_name,
34
+ record: self,
35
+ builder: builder,
36
+ internal: { namespace: namespace,
37
+ optional_field: true },
38
+ options: options,
39
+ &block)
40
+ add_field(new_field)
41
+ end
42
+
43
+ # Adds fields from the record with the specified name to the current
44
+ # record.
45
+ def extends(name)
46
+ fields.merge!(builder.lookup(name).duplicated_fields)
47
+ end
48
+
49
+ def to_h(reference_state = SchemaSerializerReferenceState.new)
50
+ reference_state.definition_or_reference(fullname) do
51
+ {
52
+ type: :record,
53
+ name: name,
54
+ namespace: namespace,
55
+ doc: doc,
56
+ aliases: aliases,
57
+ fields: fields.values.map { |field| field.serialize(reference_state) }
58
+ }.reject { |_, v| v.nil? }
59
+ end
60
+ end
61
+ alias_method :serialize, :to_h
62
+
63
+ protected
64
+
65
+ def duplicated_fields
66
+ fields.each_with_object(Hash.new) do |(name, field), result|
67
+ field_copy = field.dup
68
+ result[name] = field_copy
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ # Add field, replacing any existing field with the same name.
75
+ def add_field(field)
76
+ fields[field.name] = field
77
+ end
78
+
79
+ def fields
80
+ @fields ||= {}
81
+ 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
+ end
89
+ end
90
+ end
91
+ end
@@ -3,6 +3,7 @@ require 'avro/builder/types/specific_type'
3
3
  require 'avro/builder/types/configurable_type'
4
4
  require 'avro/builder/types/type_referencer'
5
5
  require 'avro/builder/types/named_type'
6
+ require 'avro/builder/types/record_type'
6
7
  require 'avro/builder/types/enum_type'
7
8
  require 'avro/builder/types/fixed_type'
8
9
  require 'avro/builder/types/array_type'
@@ -1,5 +1,5 @@
1
1
  module Avro
2
2
  module Builder
3
- VERSION = "0.2.0"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
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.2.0
4
+ version: 0.3.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-03-18 00:00:00.000000000 Z
11
+ date: 2016-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: avro
@@ -117,7 +117,6 @@ files:
117
117
  - lib/avro/builder/field.rb
118
118
  - lib/avro/builder/file_handler.rb
119
119
  - lib/avro/builder/namespaceable.rb
120
- - lib/avro/builder/record.rb
121
120
  - lib/avro/builder/schema_serializer_reference_state.rb
122
121
  - lib/avro/builder/type_factory.rb
123
122
  - lib/avro/builder/types.rb
@@ -127,6 +126,7 @@ files:
127
126
  - lib/avro/builder/types/fixed_type.rb
128
127
  - lib/avro/builder/types/map_type.rb
129
128
  - lib/avro/builder/types/named_type.rb
129
+ - lib/avro/builder/types/record_type.rb
130
130
  - lib/avro/builder/types/specific_type.rb
131
131
  - lib/avro/builder/types/type.rb
132
132
  - lib/avro/builder/types/type_referencer.rb
@@ -1,84 +0,0 @@
1
- module Avro
2
- module Builder
3
- # This class represents a record in an Avro schema.
4
- class Record
5
- include Avro::Builder::DslAttributes
6
- include Avro::Builder::Namespaceable
7
-
8
- attr_accessor :builder
9
- attr_reader :name
10
-
11
- dsl_attributes :doc, :aliases, :namespace
12
-
13
- def initialize(name, options = {})
14
- @name = name
15
- options.each do |key, value|
16
- send(key, value)
17
- end
18
- end
19
-
20
- # Add a required field to the record
21
- def required(name, type_name, options = {}, &block)
22
- new_field = Avro::Builder::Field.new(name: name,
23
- type_name: type_name,
24
- builder: builder,
25
- internal: { namespace: namespace },
26
- options: options,
27
- &block)
28
- add_field(new_field)
29
- end
30
-
31
- # Add an optional field to the record. In Avro this is represented
32
- # as a union of null and the type specified here.
33
- def optional(name, type_name, options = {}, &block)
34
- new_field = Avro::Builder::Field.new(name: name,
35
- type_name: type_name,
36
- builder: builder,
37
- internal: { namespace: namespace,
38
- optional: true },
39
- options: options,
40
- &block)
41
- add_field(new_field)
42
- end
43
-
44
- # Adds fields from the record with the specified name to the current
45
- # record.
46
- def extends(name)
47
- fields.merge!(builder.lookup(name).duplicated_fields)
48
- end
49
-
50
- def to_h(reference_state = SchemaSerializerReferenceState.new)
51
- reference_state.definition_or_reference(fullname) do
52
- {
53
- type: :record,
54
- name: name,
55
- namespace: namespace,
56
- doc: doc,
57
- aliases: aliases,
58
- fields: fields.values.map { |field| field.serialize(reference_state) }
59
- }.reject { |_, v| v.nil? }
60
- end
61
- end
62
-
63
- protected
64
-
65
- def duplicated_fields
66
- fields.each_with_object(Hash.new) do |(name, field), result|
67
- field_copy = field.dup
68
- result[name] = field_copy
69
- end
70
- end
71
-
72
- private
73
-
74
- # Add field, replacing any existing field with the same name.
75
- def add_field(field)
76
- fields[field.name] = field
77
- end
78
-
79
- def fields
80
- @fields ||= {}
81
- end
82
- end
83
- end
84
- end