avro-builder 0.2.0 → 0.3.0

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