avro_turf 0.6.2 → 0.7.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/README.md +27 -0
- data/lib/avro_turf.rb +1 -0
- data/lib/avro_turf/messaging.rb +11 -5
- data/lib/avro_turf/schema_to_avro_patch.rb +41 -0
- data/{spec → lib/avro_turf/test}/fake_schema_registry_server.rb +0 -0
- data/lib/avro_turf/version.rb +1 -1
- data/spec/cached_schema_registry_spec.rb +1 -1
- data/spec/messaging_spec.rb +56 -30
- data/spec/schema_registry_spec.rb +1 -1
- data/spec/schema_to_avro_patch_spec.rb +24 -0
- data/spec/support/schema_registry_context.rb +46 -0
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b494234a9f8fb90ae28c68c2536c4b4a53282879
|
4
|
+
data.tar.gz: 1bf0027da6a5666c292613c501a3b5d1d95b0e93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2b4e531bda7323a871176fd1e75a85f8aac7285af95ed9ddb51fe274085724fae0af50bf3e098457142453a5c4ba4348904318213cbb425eb58cfbd42ebdb0a
|
7
|
+
data.tar.gz: ca5046c5fa48b1ceb2d491ca20e4e5ff7367d931ef32634579954459be9a66d50ebad46ad7de756b96541c3c9b7ad745ac83d15f0947ace042fd06dec343b09a
|
data/README.md
CHANGED
@@ -101,9 +101,13 @@ The Messaging API will automatically register schemas used for encoding data, an
|
|
101
101
|
|
102
102
|
**NOTE:** The Messaging format is _not_ compatible with the Avro data file API.
|
103
103
|
|
104
|
+
The Messaging API is not included by default, so you must require 'avro_turf/messaging' explicitly if you want to use it.
|
105
|
+
|
104
106
|
Using the Messaging API is simple once you have set up a Schema Registry service:
|
105
107
|
|
106
108
|
```ruby
|
109
|
+
require 'avro_turf/messaging'
|
110
|
+
|
107
111
|
# You need to pass the URL of your Schema Registry.
|
108
112
|
avro = AvroTurf::Messaging.new(registry_url: "http://my-registry:8081/")
|
109
113
|
|
@@ -116,3 +120,26 @@ data = avro.encode({ "title" => "hello, world" }, schema_name: "greeting")
|
|
116
120
|
# instances of the same schema id will be served by the cache.
|
117
121
|
avro.decode(data) #=> { "title" => "hello, world" }
|
118
122
|
```
|
123
|
+
|
124
|
+
### Testing Support
|
125
|
+
|
126
|
+
AvroTurf includes a `FakeSchemaRegistryServer` that can be used in tests. The
|
127
|
+
fake schema registry server depends on Sinatra but it is _not_ listed as a runtime
|
128
|
+
dependency for AvroTurf. Sinatra must be added to your Gemfile or gemspec in order
|
129
|
+
to use the fake server.
|
130
|
+
|
131
|
+
Example using RSpec:
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
require 'avro_turf/test/fake_schema_registry_server'
|
135
|
+
require 'webmock/rspec'
|
136
|
+
|
137
|
+
# within an example
|
138
|
+
let(:registry_url) { "http://registry.example.com" }
|
139
|
+
before do
|
140
|
+
stub_request(:any, /^#{registry_url}/).to_rack(FakeSchemaRegistryServer)
|
141
|
+
FakeSchemaRegistryServer.clear
|
142
|
+
end
|
143
|
+
|
144
|
+
# Messaging objects created with the same registry_url will now use the fake server.
|
145
|
+
```
|
data/lib/avro_turf.rb
CHANGED
data/lib/avro_turf/messaging.rb
CHANGED
@@ -19,15 +19,19 @@ class AvroTurf
|
|
19
19
|
|
20
20
|
# Instantiate a new Messaging instance with the given configuration.
|
21
21
|
#
|
22
|
+
# registry - A schema registry object that responds to all methods in the
|
23
|
+
# AvroTurf::SchemaRegistry interface.
|
22
24
|
# registry_url - The String URL of the schema registry that should be used.
|
25
|
+
# schema_store - A schema store object that responds to #find(schema_name, namespace).
|
23
26
|
# schemas_path - The String file system path where local schemas are stored.
|
24
27
|
# namespace - The String default schema namespace.
|
25
28
|
# logger - The Logger that should be used to log information (optional).
|
26
|
-
def initialize(registry_url: nil, schemas_path: nil, namespace: nil, logger: nil)
|
29
|
+
def initialize(registry: nil, registry_url: nil, schema_store: nil, schemas_path: nil, namespace: nil, logger: nil)
|
27
30
|
@logger = logger || Logger.new($stderr)
|
28
31
|
@namespace = namespace
|
29
|
-
@schema_store = SchemaStore.new(path: schemas_path || DEFAULT_SCHEMAS_PATH)
|
30
|
-
@registry = CachedSchemaRegistry.new(SchemaRegistry.new(registry_url, logger: @logger))
|
32
|
+
@schema_store = schema_store || SchemaStore.new(path: schemas_path || DEFAULT_SCHEMAS_PATH)
|
33
|
+
@registry = registry || CachedSchemaRegistry.new(SchemaRegistry.new(registry_url, logger: @logger))
|
34
|
+
@schemas_by_id = {}
|
31
35
|
end
|
32
36
|
|
33
37
|
# Encodes a message using the specified schema.
|
@@ -85,8 +89,10 @@ class AvroTurf
|
|
85
89
|
# The schema id is a 4-byte big-endian integer.
|
86
90
|
schema_id = decoder.read(4).unpack("N").first
|
87
91
|
|
88
|
-
|
89
|
-
|
92
|
+
writers_schema = @schemas_by_id.fetch(schema_id) do
|
93
|
+
schema_json = @registry.fetch(schema_id)
|
94
|
+
@schemas_by_id[schema_id] = Avro::Schema.parse(schema_json)
|
95
|
+
end
|
90
96
|
|
91
97
|
reader = Avro::IO::DatumReader.new(writers_schema, readers_schema)
|
92
98
|
reader.read(decoder)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class AvroTurf
|
2
|
+
module AvroGemPatch
|
3
|
+
module RecordSchema
|
4
|
+
module ClassMethods
|
5
|
+
def make_field_objects(field_data, names, namespace=nil)
|
6
|
+
new_field_data = []
|
7
|
+
field_data.each do |field|
|
8
|
+
if field.respond_to?(:[]) && !field.key?('default')
|
9
|
+
field = field.clone
|
10
|
+
field['default'] = :no_default
|
11
|
+
end
|
12
|
+
new_field_data << field
|
13
|
+
end
|
14
|
+
super(new_field_data, names, namespace)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.prepended(base)
|
19
|
+
class << base
|
20
|
+
prepend ClassMethods
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module Field
|
26
|
+
def initialize(type, name, default=:no_default, order=nil, names=nil, namespace=nil)
|
27
|
+
super(type, name, default, order, names, namespace)
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_avro(names=Set.new)
|
31
|
+
{'name' => name, 'type' => type.to_avro(names)}.tap do |avro|
|
32
|
+
avro['default'] = default unless default == :no_default
|
33
|
+
avro['order'] = order if order
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
Avro::Schema::RecordSchema.send(:prepend, AvroTurf::AvroGemPatch::RecordSchema)
|
41
|
+
Avro::Schema::Field.send(:prepend, AvroTurf::AvroGemPatch::Field)
|
File without changes
|
data/lib/avro_turf/version.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'webmock/rspec'
|
2
2
|
require 'avro_turf/cached_schema_registry'
|
3
|
-
|
3
|
+
require 'avro_turf/test/fake_schema_registry_server'
|
4
4
|
|
5
5
|
describe AvroTurf::CachedSchemaRegistry do
|
6
6
|
let(:upstream) { instance_double(AvroTurf::SchemaRegistry) }
|
data/spec/messaging_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'webmock/rspec'
|
2
2
|
require 'avro_turf/messaging'
|
3
|
-
|
3
|
+
require 'avro_turf/test/fake_schema_registry_server'
|
4
4
|
|
5
5
|
describe AvroTurf::Messaging do
|
6
6
|
let(:registry_url) { "http://registry.example.com" }
|
@@ -14,6 +14,8 @@ describe AvroTurf::Messaging do
|
|
14
14
|
)
|
15
15
|
}
|
16
16
|
|
17
|
+
let(:message) { { "full_name" => "John Doe" } }
|
18
|
+
|
17
19
|
before do
|
18
20
|
FileUtils.mkdir_p("spec/schemas")
|
19
21
|
end
|
@@ -38,42 +40,66 @@ describe AvroTurf::Messaging do
|
|
38
40
|
AVSC
|
39
41
|
end
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
shared_examples_for "encoding and decoding" do
|
44
|
+
it "encodes and decodes messages" do
|
45
|
+
data = avro.encode(message, schema_name: "person")
|
46
|
+
expect(avro.decode(data)).to eq message
|
47
|
+
end
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
49
|
+
it "allows specifying a reader's schema" do
|
50
|
+
data = avro.encode(message, schema_name: "person")
|
51
|
+
expect(avro.decode(data, schema_name: "person")).to eq message
|
52
|
+
end
|
53
|
+
|
54
|
+
it "caches parsed schemas for decoding" do
|
55
|
+
data = avro.encode(message, schema_name: "person")
|
56
|
+
avro.decode(data)
|
57
|
+
allow(Avro::Schema).to receive(:parse).and_call_original
|
58
|
+
expect(avro.decode(data)).to eq message
|
59
|
+
expect(Avro::Schema).not_to have_received(:parse)
|
60
|
+
end
|
51
61
|
end
|
52
62
|
|
53
|
-
|
63
|
+
it_behaves_like "encoding and decoding"
|
64
|
+
|
65
|
+
context "with a provided registry" do
|
66
|
+
let(:registry) { AvroTurf::SchemaRegistry.new(registry_url, logger: logger) }
|
67
|
+
|
54
68
|
let(:avro) do
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
super.extend(Module.new do
|
61
|
-
# Replace to_json on the returned schema with an implementation
|
62
|
-
# that returns something similar to active_support/core_ext/json
|
63
|
-
def to_json(*args)
|
64
|
-
instance_variables.each_with_object(Hash.new) do |ivar, result|
|
65
|
-
result[ivar.to_s.sub('@','')] = instance_variable_get(ivar)
|
66
|
-
end.to_json(*args)
|
67
|
-
end
|
68
|
-
end)
|
69
|
-
end
|
70
|
-
end
|
69
|
+
AvroTurf::Messaging.new(
|
70
|
+
registry: registry,
|
71
|
+
schemas_path: "spec/schemas",
|
72
|
+
logger: logger
|
73
|
+
)
|
71
74
|
end
|
72
75
|
|
73
|
-
|
76
|
+
it_behaves_like "encoding and decoding"
|
77
|
+
|
78
|
+
it "uses the provided registry" do
|
79
|
+
allow(registry).to receive(:register).and_call_original
|
74
80
|
message = { "full_name" => "John Doe" }
|
75
|
-
|
76
|
-
expect(
|
81
|
+
avro.encode(message, schema_name: "person")
|
82
|
+
expect(registry).to have_received(:register)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "with a provided schema store" do
|
87
|
+
let(:schema_store) { AvroTurf::SchemaStore.new(path: "spec/schemas") }
|
88
|
+
|
89
|
+
let(:avro) do
|
90
|
+
AvroTurf::Messaging.new(
|
91
|
+
registry_url: registry_url,
|
92
|
+
schema_store: schema_store,
|
93
|
+
logger: logger
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
it_behaves_like "encoding and decoding"
|
98
|
+
|
99
|
+
it "uses the provided schema store" do
|
100
|
+
allow(schema_store).to receive(:find).and_call_original
|
101
|
+
avro.encode(message, schema_name: "person")
|
102
|
+
expect(schema_store).to have_received(:find).with("person", nil)
|
77
103
|
end
|
78
104
|
end
|
79
105
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'webmock/rspec'
|
2
|
+
|
3
|
+
# This spec verifies the monkey-patch that we have to apply until the avro
|
4
|
+
# gem releases a fix for bug AVRO-1848:
|
5
|
+
# https://issues.apache.org/jira/browse/AVRO-1848
|
6
|
+
|
7
|
+
describe Avro::Schema do
|
8
|
+
it "correctly handles falsey field defaults" do
|
9
|
+
schema = Avro::Schema.parse <<-SCHEMA
|
10
|
+
{"type": "record", "name": "Record", "namespace": "my.name.space",
|
11
|
+
"fields": [
|
12
|
+
{"name": "is_usable", "type": "boolean", "default": false}
|
13
|
+
]
|
14
|
+
}
|
15
|
+
SCHEMA
|
16
|
+
|
17
|
+
expect(schema.to_avro).to eq({
|
18
|
+
'type' => 'record', 'name' => 'Record', 'namespace' => 'my.name.space',
|
19
|
+
'fields' => [
|
20
|
+
{'name' => 'is_usable', 'type' => 'boolean', 'default' => false}
|
21
|
+
]
|
22
|
+
})
|
23
|
+
end
|
24
|
+
end
|
@@ -26,6 +26,26 @@ shared_examples_for "a schema registry client" do
|
|
26
26
|
|
27
27
|
expect(fetched_schema).to eq(schema)
|
28
28
|
end
|
29
|
+
|
30
|
+
context "with an Avro::Schema" do
|
31
|
+
let(:avro_schema) { Avro::Schema.parse(schema) }
|
32
|
+
|
33
|
+
it "allows registration using an Avro::Schema" do
|
34
|
+
id = registry.register(subject_name, avro_schema)
|
35
|
+
expect(registry.fetch(id)).to eq(avro_schema.to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with ActiveSupport present" do
|
39
|
+
before do
|
40
|
+
break_to_json(avro_schema)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "allows registering an Avro schema" do
|
44
|
+
id = registry.register(subject_name, avro_schema)
|
45
|
+
expect(registry.fetch(id)).to eq(avro_schema.to_s)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
29
49
|
end
|
30
50
|
|
31
51
|
describe "#fetch" do
|
@@ -133,6 +153,22 @@ shared_examples_for "a schema registry client" do
|
|
133
153
|
it "returns the schema details" do
|
134
154
|
expect(registry.check(subject_name, schema)).to eq(JSON.parse(expected))
|
135
155
|
end
|
156
|
+
|
157
|
+
context "with an Avro::Schema" do
|
158
|
+
let(:avro_schema) { Avro::Schema.parse(schema) }
|
159
|
+
|
160
|
+
it "supports a check using an Avro schema" do
|
161
|
+
expect(registry.check(subject_name, avro_schema)).to eq(JSON.parse(expected))
|
162
|
+
end
|
163
|
+
|
164
|
+
context "with ActiveSupport present" do
|
165
|
+
before { break_to_json(avro_schema) }
|
166
|
+
|
167
|
+
it "supports a check using an Avro schema" do
|
168
|
+
expect(registry.check(subject_name, avro_schema)).to eq(JSON.parse(expected))
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
136
172
|
end
|
137
173
|
|
138
174
|
context "when the schema is not registered" do
|
@@ -141,4 +177,14 @@ shared_examples_for "a schema registry client" do
|
|
141
177
|
end
|
142
178
|
end
|
143
179
|
end
|
180
|
+
|
181
|
+
# Monkey patch an Avro::Schema to simulate the presence of
|
182
|
+
# active_support/core_ext.
|
183
|
+
def break_to_json(avro_schema)
|
184
|
+
def avro_schema.to_json(*args)
|
185
|
+
instance_variables.each_with_object(Hash.new) do |ivar, result|
|
186
|
+
result[ivar.to_s.sub('@', '')] = instance_variable_get(ivar)
|
187
|
+
end.to_json(*args)
|
188
|
+
end
|
189
|
+
end
|
144
190
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: avro_turf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Schierbeck
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: avro
|
@@ -173,6 +173,8 @@ files:
|
|
173
173
|
- lib/avro_turf/messaging.rb
|
174
174
|
- lib/avro_turf/schema_registry.rb
|
175
175
|
- lib/avro_turf/schema_store.rb
|
176
|
+
- lib/avro_turf/schema_to_avro_patch.rb
|
177
|
+
- lib/avro_turf/test/fake_schema_registry_server.rb
|
176
178
|
- lib/avro_turf/version.rb
|
177
179
|
- perf/address.avsc
|
178
180
|
- perf/encoding_size.rb
|
@@ -190,10 +192,10 @@ files:
|
|
190
192
|
- spec/core_ext/symbol_spec.rb
|
191
193
|
- spec/core_ext/time_spec.rb
|
192
194
|
- spec/core_ext/true_class_spec.rb
|
193
|
-
- spec/fake_schema_registry_server.rb
|
194
195
|
- spec/messaging_spec.rb
|
195
196
|
- spec/schema_registry_spec.rb
|
196
197
|
- spec/schema_store_spec.rb
|
198
|
+
- spec/schema_to_avro_patch_spec.rb
|
197
199
|
- spec/spec_helper.rb
|
198
200
|
- spec/support/schema_registry_context.rb
|
199
201
|
homepage: https://github.com/dasch/avro_turf
|
@@ -234,10 +236,10 @@ test_files:
|
|
234
236
|
- spec/core_ext/symbol_spec.rb
|
235
237
|
- spec/core_ext/time_spec.rb
|
236
238
|
- spec/core_ext/true_class_spec.rb
|
237
|
-
- spec/fake_schema_registry_server.rb
|
238
239
|
- spec/messaging_spec.rb
|
239
240
|
- spec/schema_registry_spec.rb
|
240
241
|
- spec/schema_store_spec.rb
|
242
|
+
- spec/schema_to_avro_patch_spec.rb
|
241
243
|
- spec/spec_helper.rb
|
242
244
|
- spec/support/schema_registry_context.rb
|
243
245
|
has_rdoc:
|