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 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: