avro_turf 1.19.0 → 1.20.1
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/.github/workflows/ruby.yml +20 -11
- data/CHANGELOG.md +12 -0
- data/Gemfile +5 -2
- data/Rakefile +2 -1
- data/avro_turf.gemspec +16 -17
- data/lib/avro_turf/cached_confluent_schema_registry.rb +9 -8
- data/lib/avro_turf/cached_schema_registry.rb +3 -1
- data/lib/avro_turf/confluent_schema_registry.rb +23 -17
- data/lib/avro_turf/core_ext/date.rb +2 -0
- data/lib/avro_turf/core_ext/enumerable.rb +2 -0
- data/lib/avro_turf/core_ext/false_class.rb +2 -0
- data/lib/avro_turf/core_ext/hash.rb +4 -2
- data/lib/avro_turf/core_ext/nil_class.rb +2 -0
- data/lib/avro_turf/core_ext/numeric.rb +2 -0
- data/lib/avro_turf/core_ext/string.rb +2 -0
- data/lib/avro_turf/core_ext/symbol.rb +2 -0
- data/lib/avro_turf/core_ext/time.rb +2 -0
- data/lib/avro_turf/core_ext/true_class.rb +2 -0
- data/lib/avro_turf/core_ext.rb +12 -10
- data/lib/avro_turf/disk_cache.rb +13 -12
- data/lib/avro_turf/in_memory_cache.rb +2 -0
- data/lib/avro_turf/messaging.rb +25 -15
- data/lib/avro_turf/mutable_schema_store.rb +25 -4
- data/lib/avro_turf/schema_registry.rb +3 -1
- data/lib/avro_turf/schema_store.rb +3 -2
- data/lib/avro_turf/schema_to_avro_patch.rb +14 -12
- data/lib/avro_turf/test/fake_confluent_schema_registry_server.rb +39 -37
- data/lib/avro_turf/test/fake_prefixed_confluent_schema_registry_server.rb +12 -10
- data/lib/avro_turf/test/fake_schema_registry_server.rb +3 -1
- data/lib/avro_turf/test/fake_server.rb +186 -0
- data/lib/avro_turf/version.rb +3 -1
- data/lib/avro_turf.rb +15 -13
- data/perf/encoding_size.rb +4 -2
- data/perf/encoding_speed.rb +4 -2
- data/spec/avro_turf_spec.rb +24 -23
- data/spec/cached_confluent_schema_registry_spec.rb +9 -7
- data/spec/confluent_schema_registry_spec.rb +31 -10
- data/spec/core_ext/date_spec.rb +2 -0
- data/spec/core_ext/enumerable_spec.rb +2 -0
- data/spec/core_ext/false_class_spec.rb +2 -0
- data/spec/core_ext/hash_spec.rb +3 -1
- data/spec/core_ext/nil_class_spec.rb +2 -0
- data/spec/core_ext/numeric_spec.rb +2 -0
- data/spec/core_ext/string_spec.rb +2 -0
- data/spec/core_ext/symbol_spec.rb +2 -0
- data/spec/core_ext/time_spec.rb +2 -0
- data/spec/core_ext/true_class_spec.rb +2 -0
- data/spec/disk_cached_confluent_schema_registry_spec.rb +23 -21
- data/spec/messaging_spec.rb +145 -99
- data/spec/mutable_schema_store_spec.rb +134 -0
- data/spec/schema_store_spec.rb +23 -21
- data/spec/schema_to_avro_patch_spec.rb +8 -7
- data/spec/spec_helper.rb +9 -9
- data/spec/support/authorized_fake_confluent_schema_registry_server.rb +4 -2
- data/spec/support/authorized_fake_prefixed_confluent_schema_registry_server.rb +4 -2
- data/spec/support/confluent_schema_registry_context.rb +32 -30
- data/spec/test/fake_confluent_schema_registry_server_http_contract_spec.rb +722 -0
- data/spec/test/fake_confluent_schema_registry_server_spec.rb +97 -94
- metadata +7 -40
|
@@ -1,36 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
class AvroTurf
|
|
2
4
|
module AvroGemPatch
|
|
3
5
|
module RecordSchema
|
|
4
6
|
module ClassMethods
|
|
5
|
-
def make_field_objects(field_data, names, namespace=nil)
|
|
7
|
+
def make_field_objects(field_data, names, namespace = nil)
|
|
6
8
|
new_field_data = []
|
|
7
9
|
field_data.each do |field|
|
|
8
|
-
if field.respond_to?(:[]) && !field.key?(
|
|
10
|
+
if field.respond_to?(:[]) && !field.key?("default")
|
|
9
11
|
field = field.clone
|
|
10
|
-
field[
|
|
12
|
+
field["default"] = :no_default
|
|
11
13
|
end
|
|
12
14
|
new_field_data << field
|
|
13
15
|
end
|
|
14
16
|
super(new_field_data, names, namespace)
|
|
15
17
|
end
|
|
16
18
|
end
|
|
17
|
-
|
|
19
|
+
|
|
18
20
|
def self.prepended(base)
|
|
19
21
|
class << base
|
|
20
22
|
prepend ClassMethods
|
|
21
23
|
end
|
|
22
24
|
end
|
|
23
25
|
end
|
|
24
|
-
|
|
26
|
+
|
|
25
27
|
module Field
|
|
26
|
-
def initialize(type, name, default
|
|
27
|
-
super
|
|
28
|
+
def initialize(type, name, default = :no_default, order = nil, names = nil, namespace = nil)
|
|
29
|
+
super
|
|
28
30
|
end
|
|
29
|
-
|
|
30
|
-
def to_avro(names=Set.new)
|
|
31
|
-
{
|
|
32
|
-
avro[
|
|
33
|
-
avro[
|
|
31
|
+
|
|
32
|
+
def to_avro(names = Set.new)
|
|
33
|
+
{"name" => name, "type" => type.to_avro(names)}.tap do |avro|
|
|
34
|
+
avro["default"] = default unless default == :no_default
|
|
35
|
+
avro["order"] = order if order
|
|
34
36
|
end
|
|
35
37
|
end
|
|
36
38
|
end
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "fake_server"
|
|
4
|
+
|
|
5
|
+
class FakeConfluentSchemaRegistryServer
|
|
6
|
+
include AvroTurf::Test::FakeServer
|
|
2
7
|
|
|
3
|
-
class FakeConfluentSchemaRegistryServer < Sinatra::Base
|
|
4
8
|
QUALIFIED_SUBJECT = /
|
|
5
9
|
:(?<context>\.[^:]*)
|
|
6
10
|
:(?<subject>.*)
|
|
7
11
|
/x
|
|
8
|
-
DEFAULT_CONTEXT =
|
|
9
|
-
SUBJECTS = Hash.new { |hash, key| hash[key] = Hash.new {
|
|
10
|
-
SCHEMAS = Hash.new { |hash, key| hash[key] =
|
|
11
|
-
CONFIGS =
|
|
12
|
-
SUBJECT_NOT_FOUND = {
|
|
13
|
-
VERSION_NOT_FOUND = {
|
|
14
|
-
SCHEMA_NOT_FOUND = {
|
|
15
|
-
DEFAULT_GLOBAL_CONFIG = {
|
|
12
|
+
DEFAULT_CONTEXT = "."
|
|
13
|
+
SUBJECTS = Hash.new { |hash, key| hash[key] = Hash.new { [] } }
|
|
14
|
+
SCHEMAS = Hash.new { |hash, key| hash[key] = [] }
|
|
15
|
+
CONFIGS = {}
|
|
16
|
+
SUBJECT_NOT_FOUND = {error_code: 40401, message: "Subject not found"}.to_json.freeze
|
|
17
|
+
VERSION_NOT_FOUND = {error_code: 40402, message: "Version not found"}.to_json.freeze
|
|
18
|
+
SCHEMA_NOT_FOUND = {error_code: 40403, message: "Schema not found"}.to_json.freeze
|
|
19
|
+
DEFAULT_GLOBAL_CONFIG = {"compatibility" => "BACKWARD"}.freeze
|
|
16
20
|
|
|
17
21
|
@global_config = DEFAULT_GLOBAL_CONFIG.dup
|
|
18
22
|
|
|
@@ -20,22 +24,21 @@ class FakeConfluentSchemaRegistryServer < Sinatra::Base
|
|
|
20
24
|
attr_reader :global_config
|
|
21
25
|
end
|
|
22
26
|
|
|
23
|
-
helpers
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
end
|
|
27
|
+
# Helper methods (previously in Sinatra helpers block)
|
|
28
|
+
def parse_schema
|
|
29
|
+
request.body.rewind
|
|
30
|
+
JSON.parse(request.body.read).fetch("schema").tap do |schema|
|
|
31
|
+
Avro::Schema.parse(schema)
|
|
29
32
|
end
|
|
33
|
+
end
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
def parse_config
|
|
36
|
+
request.body.rewind
|
|
37
|
+
JSON.parse(request.body.read)
|
|
38
|
+
end
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
end
|
|
40
|
+
def global_config
|
|
41
|
+
self.class.global_config
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
post "/subjects/:qualified_subject/versions" do
|
|
@@ -51,7 +54,7 @@ class FakeConfluentSchemaRegistryServer < Sinatra::Base
|
|
|
51
54
|
SUBJECTS[context][subject] = SUBJECTS[context][subject] << schema_id
|
|
52
55
|
end
|
|
53
56
|
|
|
54
|
-
{
|
|
57
|
+
{id: schema_id}.to_json
|
|
55
58
|
end
|
|
56
59
|
|
|
57
60
|
get "/schemas/ids/:schema_id/versions" do
|
|
@@ -60,7 +63,7 @@ class FakeConfluentSchemaRegistryServer < Sinatra::Base
|
|
|
60
63
|
schema = SCHEMAS[context].at(schema_id)
|
|
61
64
|
halt(404, SCHEMA_NOT_FOUND) unless schema
|
|
62
65
|
|
|
63
|
-
related_subjects = SUBJECTS[context].select {|_, vs| vs.include? schema_id }
|
|
66
|
+
related_subjects = SUBJECTS[context].select { |_, vs| vs.include? schema_id }
|
|
64
67
|
|
|
65
68
|
related_subjects.map do |subject, versions|
|
|
66
69
|
{
|
|
@@ -74,14 +77,13 @@ class FakeConfluentSchemaRegistryServer < Sinatra::Base
|
|
|
74
77
|
context, _subject = parse_qualified_subject(params[:subject])
|
|
75
78
|
schema = SCHEMAS[context].at(params[:schema_id].to_i)
|
|
76
79
|
halt(404, SCHEMA_NOT_FOUND) unless schema
|
|
77
|
-
{
|
|
80
|
+
{schema: schema}.to_json
|
|
78
81
|
end
|
|
79
82
|
|
|
80
83
|
get "/subjects" do
|
|
81
|
-
subject_names = SUBJECTS.
|
|
84
|
+
subject_names = SUBJECTS.each_with_object([]) do |args, acc|
|
|
82
85
|
context, subjects = args
|
|
83
|
-
subjects.keys.each { |subject| acc << (context ==
|
|
84
|
-
acc
|
|
86
|
+
subjects.keys.each { |subject| acc << ((context == ".") ? subject : ":#{context}:#{subject}") }
|
|
85
87
|
end
|
|
86
88
|
subject_names.to_json
|
|
87
89
|
end
|
|
@@ -98,11 +100,11 @@ class FakeConfluentSchemaRegistryServer < Sinatra::Base
|
|
|
98
100
|
schema_ids = SUBJECTS[context][subject]
|
|
99
101
|
halt(404, SUBJECT_NOT_FOUND) if schema_ids.empty?
|
|
100
102
|
|
|
101
|
-
schema_id = if params[:version] ==
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
schema_id = if params[:version] == "latest"
|
|
104
|
+
schema_ids.last
|
|
105
|
+
else
|
|
106
|
+
schema_ids.at(Integer(params[:version]) - 1)
|
|
107
|
+
end
|
|
106
108
|
halt(404, VERSION_NOT_FOUND) unless schema_id
|
|
107
109
|
|
|
108
110
|
schema = SCHEMAS[context].at(schema_id)
|
|
@@ -171,13 +173,13 @@ class FakeConfluentSchemaRegistryServer < Sinatra::Base
|
|
|
171
173
|
def parse_qualified_subject(qualified_subject)
|
|
172
174
|
match = QUALIFIED_SUBJECT.match(qualified_subject)
|
|
173
175
|
if !match.nil?
|
|
174
|
-
match.named_captures.values_at(
|
|
176
|
+
match.named_captures.values_at("context", "subject")
|
|
175
177
|
else
|
|
176
|
-
[
|
|
178
|
+
[DEFAULT_CONTEXT, qualified_subject]
|
|
177
179
|
end
|
|
178
180
|
end
|
|
179
181
|
|
|
180
182
|
def qualify_subject(context, subject)
|
|
181
|
-
context == "." ? subject : ":#{context}:#{subject}"
|
|
183
|
+
(context == ".") ? subject : ":#{context}:#{subject}"
|
|
182
184
|
end
|
|
183
185
|
end
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "fake_confluent_schema_registry_server"
|
|
2
4
|
|
|
3
5
|
class FakePrefixedConfluentSchemaRegistryServer < FakeConfluentSchemaRegistryServer
|
|
4
|
-
DEFAULT_CONTEXT=
|
|
6
|
+
DEFAULT_CONTEXT = "."
|
|
5
7
|
|
|
6
8
|
post "/prefix/subjects/:subject/versions" do
|
|
7
9
|
schema = parse_schema
|
|
@@ -9,7 +11,7 @@ class FakePrefixedConfluentSchemaRegistryServer < FakeConfluentSchemaRegistrySer
|
|
|
9
11
|
|
|
10
12
|
schemas_for_subject =
|
|
11
13
|
SCHEMAS[DEFAULT_CONTEXT].select
|
|
12
|
-
|
|
14
|
+
.with_index { |_, i| ids_for_subject.include?(i) }
|
|
13
15
|
|
|
14
16
|
if schemas_for_subject.include?(schema)
|
|
15
17
|
schema_id = SCHEMAS[DEFAULT_CONTEXT].index(schema)
|
|
@@ -19,13 +21,13 @@ class FakePrefixedConfluentSchemaRegistryServer < FakeConfluentSchemaRegistrySer
|
|
|
19
21
|
SUBJECTS[DEFAULT_CONTEXT][params[:subject]] = SUBJECTS[DEFAULT_CONTEXT][params[:subject]] << schema_id
|
|
20
22
|
end
|
|
21
23
|
|
|
22
|
-
{
|
|
24
|
+
{id: schema_id}.to_json
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
get "/prefix/schemas/ids/:schema_id" do
|
|
26
28
|
schema = SCHEMAS[DEFAULT_CONTEXT].at(params[:schema_id].to_i)
|
|
27
29
|
halt(404, SCHEMA_NOT_FOUND) unless schema
|
|
28
|
-
{
|
|
30
|
+
{schema: schema}.to_json
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
get "/prefix/subjects" do
|
|
@@ -42,11 +44,11 @@ class FakePrefixedConfluentSchemaRegistryServer < FakeConfluentSchemaRegistrySer
|
|
|
42
44
|
schema_ids = SUBJECTS[DEFAULT_CONTEXT][params[:subject]]
|
|
43
45
|
halt(404, SUBJECT_NOT_FOUND) if schema_ids.empty?
|
|
44
46
|
|
|
45
|
-
schema_id = if params[:version] ==
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
schema_id = if params[:version] == "latest"
|
|
48
|
+
schema_ids.last
|
|
49
|
+
else
|
|
50
|
+
schema_ids.at(Integer(params[:version]) - 1)
|
|
51
|
+
end
|
|
50
52
|
halt(404, VERSION_NOT_FOUND) unless schema_id
|
|
51
53
|
|
|
52
54
|
schema = SCHEMAS[DEFAULT_CONTEXT].at(schema_id)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "avro_turf/test/fake_confluent_schema_registry_server"
|
|
2
4
|
|
|
3
5
|
# FakeSchemaRegistryServer is deprecated and will be removed in a future release.
|
|
4
6
|
# Use FakeConfluentSchemaRegistryServer instead.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "rack"
|
|
5
|
+
|
|
6
|
+
# Ensure AvroTurf class exists so we can add modules to it
|
|
7
|
+
# This is defined as a class (not module) in lib/avro_turf/version.rb
|
|
8
|
+
class AvroTurf
|
|
9
|
+
module Test
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# A lightweight Rack-based router module that provides Sinatra-like DSL.
|
|
14
|
+
# This module is designed to replace Sinatra::Base for the fake schema registry servers
|
|
15
|
+
# used in testing, eliminating the sinatra dependency.
|
|
16
|
+
#
|
|
17
|
+
# Usage:
|
|
18
|
+
# class MyServer
|
|
19
|
+
# include AvroTurf::Test::FakeServer
|
|
20
|
+
#
|
|
21
|
+
# get "/path/:param" do
|
|
22
|
+
# { result: params[:param] }.to_json
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# post "/other" do
|
|
26
|
+
# halt(404, '{"error": "not found"}') if some_condition
|
|
27
|
+
# '{"ok": true}'
|
|
28
|
+
# end
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
module AvroTurf::Test::FakeServer
|
|
32
|
+
def self.included(base)
|
|
33
|
+
base.extend(ClassMethods)
|
|
34
|
+
base.include(InstanceMethods)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
module ClassMethods
|
|
38
|
+
# Storage for routes defined in this class
|
|
39
|
+
def routes
|
|
40
|
+
@routes ||= {"GET" => [], "POST" => [], "PUT" => [], "DELETE" => []}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# When a class inherits from another that includes FakeServer,
|
|
44
|
+
# ensure it gets its own routes hash
|
|
45
|
+
def inherited(subclass)
|
|
46
|
+
super
|
|
47
|
+
subclass.instance_variable_set(:@routes, nil)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Define a GET route
|
|
51
|
+
def get(pattern, &block)
|
|
52
|
+
add_route("GET", pattern, block)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Define a POST route
|
|
56
|
+
def post(pattern, &block)
|
|
57
|
+
add_route("POST", pattern, block)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Define a PUT route
|
|
61
|
+
def put(pattern, &block)
|
|
62
|
+
add_route("PUT", pattern, block)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Define a DELETE route
|
|
66
|
+
def delete(pattern, &block)
|
|
67
|
+
add_route("DELETE", pattern, block)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Sinatra-compatible `set` method for configuration
|
|
71
|
+
def set(key, value)
|
|
72
|
+
case key
|
|
73
|
+
when :host_authorization
|
|
74
|
+
@host_authorization = value
|
|
75
|
+
else
|
|
76
|
+
instance_variable_set(:"@#{key}", value)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Access host authorization settings
|
|
81
|
+
def host_authorization
|
|
82
|
+
@host_authorization
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Rack interface - creates a new instance and calls it
|
|
86
|
+
def call(env)
|
|
87
|
+
new.call(env)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def add_route(method, pattern, block)
|
|
93
|
+
routes[method] << [compile_pattern(pattern), pattern, block]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Convert a route pattern like "/subjects/:subject/versions" to a regex
|
|
97
|
+
# with named capture groups: /^\/subjects\/(?<subject>[^\/]+)\/versions$/
|
|
98
|
+
def compile_pattern(pattern)
|
|
99
|
+
regex_str = Regexp.escape(pattern).gsub(/:(\w+)/) { "(?<#{$1}>[^/]+)" }
|
|
100
|
+
Regexp.new("^#{regex_str}$")
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
module InstanceMethods
|
|
105
|
+
attr_reader :request, :params
|
|
106
|
+
|
|
107
|
+
def call(env)
|
|
108
|
+
@request = Rack::Request.new(env)
|
|
109
|
+
@params = {}
|
|
110
|
+
|
|
111
|
+
# Check host authorization if configured
|
|
112
|
+
if (auth = self.class.host_authorization)
|
|
113
|
+
permitted = auth[:permitted_hosts] || []
|
|
114
|
+
unless permitted.include?(@request.host)
|
|
115
|
+
return [403, {"Content-Type" => "text/plain"}, ["Forbidden"]]
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Use catch/throw for halt mechanism (like Sinatra)
|
|
120
|
+
catch(:halt) do
|
|
121
|
+
route_and_dispatch(env)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Early return from a route handler with a specific status and body
|
|
126
|
+
def halt(status, body)
|
|
127
|
+
throw :halt, [status, {"Content-Type" => "application/json"}, [body]]
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
def route_and_dispatch(env)
|
|
133
|
+
method = env["REQUEST_METHOD"]
|
|
134
|
+
path = env["PATH_INFO"]
|
|
135
|
+
|
|
136
|
+
# Parse query string into params (with both string and symbol keys for compatibility)
|
|
137
|
+
query_params = Rack::Utils.parse_query(env["QUERY_STRING"] || "")
|
|
138
|
+
@params = {}
|
|
139
|
+
query_params.each do |key, value|
|
|
140
|
+
@params[key] = value
|
|
141
|
+
@params[key.to_sym] = value
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Find matching route (check own class first, then ancestors)
|
|
145
|
+
matched = find_route(method, path)
|
|
146
|
+
|
|
147
|
+
if matched
|
|
148
|
+
regex, _pattern, block = matched
|
|
149
|
+
|
|
150
|
+
# Extract path parameters from the match
|
|
151
|
+
if (match = regex.match(path))
|
|
152
|
+
match.names.each do |name|
|
|
153
|
+
# Store with both symbol and string keys for compatibility
|
|
154
|
+
@params[name.to_sym] = match[name]
|
|
155
|
+
@params[name] = match[name]
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Execute the route block in the context of this instance
|
|
160
|
+
body = instance_exec(&block)
|
|
161
|
+
[200, {"Content-Type" => "text/html;charset=utf-8"}, [body]]
|
|
162
|
+
else
|
|
163
|
+
[404, {"Content-Type" => "text/plain"}, ["Not Found"]]
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Find a matching route by searching this class's routes first,
|
|
168
|
+
# then parent classes (to support inheritance)
|
|
169
|
+
def find_route(method, path)
|
|
170
|
+
klass = self.class
|
|
171
|
+
while klass
|
|
172
|
+
if klass.respond_to?(:routes, true) && klass.routes[method]
|
|
173
|
+
klass.routes[method].each do |route|
|
|
174
|
+
regex, _, _ = route
|
|
175
|
+
return route if regex.match(path)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
# Move up the inheritance chain
|
|
179
|
+
klass = klass.superclass
|
|
180
|
+
# Stop if we've gone past classes that include FakeServer
|
|
181
|
+
break unless klass.respond_to?(:routes, true)
|
|
182
|
+
end
|
|
183
|
+
nil
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
data/lib/avro_turf/version.rb
CHANGED
data/lib/avro_turf.rb
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
begin
|
|
2
|
-
require
|
|
4
|
+
require "avro-patches"
|
|
3
5
|
rescue LoadError
|
|
4
6
|
false
|
|
5
7
|
end
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
10
|
-
require
|
|
8
|
+
require "avro_turf/version"
|
|
9
|
+
require "avro"
|
|
10
|
+
require "json"
|
|
11
|
+
require "avro_turf/schema_store"
|
|
12
|
+
require "avro_turf/core_ext"
|
|
11
13
|
|
|
12
14
|
# check for something that indicates Avro v1.9.0 or later
|
|
13
15
|
unless defined?(::Avro::LogicalTypes)
|
|
14
|
-
require
|
|
16
|
+
require "avro_turf/schema_to_avro_patch"
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
class AvroTurf
|
|
@@ -69,10 +71,10 @@ class AvroTurf
|
|
|
69
71
|
#
|
|
70
72
|
# Returns nothing.
|
|
71
73
|
def encode_to_stream(data, schema_name: nil, stream: nil, namespace: @namespace,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
validate: false,
|
|
75
|
+
validate_options: {recursive: true,
|
|
76
|
+
encoded: false,
|
|
77
|
+
fail_on_extra_fields: true})
|
|
76
78
|
schema = @schema_store.find(schema_name, namespace)
|
|
77
79
|
writer = Avro::IO::DatumWriter.new(schema)
|
|
78
80
|
|
|
@@ -98,7 +100,7 @@ class AvroTurf
|
|
|
98
100
|
decode_stream(stream, schema_name: schema_name, namespace: namespace)
|
|
99
101
|
end
|
|
100
102
|
|
|
101
|
-
|
|
103
|
+
alias_method :decode, :decode_first
|
|
102
104
|
|
|
103
105
|
# Returns all entries encoded in the data.
|
|
104
106
|
def decode_all(encoded_data, schema_name: nil, namespace: @namespace)
|
|
@@ -119,7 +121,7 @@ class AvroTurf
|
|
|
119
121
|
data.first
|
|
120
122
|
end
|
|
121
123
|
|
|
122
|
-
|
|
124
|
+
alias_method :decode_stream, :decode_first_from_stream
|
|
123
125
|
|
|
124
126
|
# Returns all entries encoded in the stream.
|
|
125
127
|
def decode_all_from_stream(stream, schema_name: nil, namespace: @namespace)
|
data/perf/encoding_size.rb
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
2
4
|
#
|
|
3
5
|
# Measures the encoded size of messages of increasing size.
|
|
4
6
|
|
|
5
7
|
$LOAD_PATH.unshift(File.expand_path("../lib", File.dirname(__FILE__)))
|
|
6
8
|
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
+
require "benchmark"
|
|
10
|
+
require "avro_turf"
|
|
9
11
|
|
|
10
12
|
sizes = [1, 10, 100, 1_000, 10_000]
|
|
11
13
|
avro = AvroTurf.new(schemas_path: File.dirname(__FILE__))
|
data/perf/encoding_speed.rb
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
2
4
|
#
|
|
3
5
|
# Measures the time it takes to encode messages of increasing size.
|
|
4
6
|
|
|
5
7
|
$LOAD_PATH.unshift(File.expand_path("../lib", File.dirname(__FILE__)))
|
|
6
8
|
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
+
require "benchmark"
|
|
10
|
+
require "avro_turf"
|
|
9
11
|
|
|
10
12
|
# Number of iterations per run.
|
|
11
13
|
N = 10_000
|