avro_turf 0.6.2 → 0.7.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: f8eb00c2c29b7e52a48030bca31e7bebf9e84b49
4
- data.tar.gz: f651f0a33989a08b6b17b43b75fa493e083954ba
3
+ metadata.gz: b494234a9f8fb90ae28c68c2536c4b4a53282879
4
+ data.tar.gz: 1bf0027da6a5666c292613c501a3b5d1d95b0e93
5
5
  SHA512:
6
- metadata.gz: 5011cc4ad6e53bf61d3fb88f3c89c17cb195044d37fa9bf91ec5f30003f3cea59b0edd9e7618aaae57e307aadc2cf000057032d87432fb91a8118ba8609d6f7f
7
- data.tar.gz: 9ffb5c68c4ba50dd19ccf550e3fc861e9ffd7bbaa7117f57f0d7d0a987954f00f95f1385ff6ddf7d5953090623be38f6e2549f89ab1669c81741f66ed7d6292a
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
@@ -3,6 +3,7 @@ require 'avro'
3
3
  require 'json'
4
4
  require 'avro_turf/schema_store'
5
5
  require 'avro_turf/core_ext'
6
+ require 'avro_turf/schema_to_avro_patch'
6
7
 
7
8
  class AvroTurf
8
9
  class Error < StandardError; end
@@ -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
- writers_schema_json = @registry.fetch(schema_id)
89
- writers_schema = Avro::Schema.parse(writers_schema_json)
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)
@@ -1,3 +1,3 @@
1
1
  class AvroTurf
2
- VERSION = "0.6.2"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -1,6 +1,6 @@
1
1
  require 'webmock/rspec'
2
2
  require 'avro_turf/cached_schema_registry'
3
- require_relative 'fake_schema_registry_server'
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) }
@@ -1,6 +1,6 @@
1
1
  require 'webmock/rspec'
2
2
  require 'avro_turf/messaging'
3
- require_relative 'fake_schema_registry_server'
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
- it "encodes and decodes messages" do
42
- message = { "full_name" => "John Doe" }
43
- data = avro.encode(message, schema_name: "person")
44
- expect(avro.decode(data)).to eq message
45
- end
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
- it "allows specifying a reader's schema" do
48
- message = { "full_name" => "John Doe" }
49
- data = avro.encode(message, schema_name: "person")
50
- expect(avro.decode(data, schema_name: "person")).to eq message
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
- context "when active_support/core_ext is present" do
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
- super().tap do |messaging|
56
- # Simulate the presence of active_support/core_ext by monkey patching
57
- # the schema store to monkey patch #to_json on the returned schema.
58
- schema_store = messaging.instance_variable_get(:@schema_store)
59
- def schema_store.find(*)
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
- it "encodes and decodes messages" do
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
- data = avro.encode(message, schema_name: "person")
76
- expect(avro.decode(data)).to eq message
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
@@ -1,6 +1,6 @@
1
1
  require 'webmock/rspec'
2
2
  require 'avro_turf/schema_registry'
3
- require_relative 'fake_schema_registry_server'
3
+ require 'avro_turf/test/fake_schema_registry_server'
4
4
 
5
5
  describe AvroTurf::SchemaRegistry do
6
6
  it_behaves_like "a schema registry client" do
@@ -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.6.2
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-05-03 00:00:00.000000000 Z
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: