json-schema_builder 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +66 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +1 -0
- data/json-schema_builder.gemspec +28 -0
- data/lib/json/schema_builder.rb +14 -0
- data/lib/json/schema_builder/array.rb +29 -0
- data/lib/json/schema_builder/boolean.rb +9 -0
- data/lib/json/schema_builder/dsl.rb +42 -0
- data/lib/json/schema_builder/entity.rb +101 -0
- data/lib/json/schema_builder/integer.rb +9 -0
- data/lib/json/schema_builder/null.rb +9 -0
- data/lib/json/schema_builder/number.rb +9 -0
- data/lib/json/schema_builder/numeric.rb +13 -0
- data/lib/json/schema_builder/object.rb +36 -0
- data/lib/json/schema_builder/schema.rb +16 -0
- data/lib/json/schema_builder/string.rb +12 -0
- data/lib/json/schema_builder/version.rb +5 -0
- data/spec/integration/entity_literals_spec.rb +40 -0
- data/spec/integration/mixed_arrays_spec.rb +16 -0
- data/spec/integration/schema_builder_spec.rb +19 -0
- data/spec/integration/terse_arrays_spec.rb +18 -0
- data/spec/integration/terse_objects_spec.rb +30 -0
- data/spec/integration/verbose_arrays_spec.rb +18 -0
- data/spec/integration/verbose_objects_spec.rb +30 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/.keep +0 -0
- data/spec/support/attribute_matcher.rb +17 -0
- data/spec/support/examples/entity_literals.rb +33 -0
- data/spec/support/examples/mixed_arrays.rb +14 -0
- data/spec/support/examples/schema_builder.rb +5 -0
- data/spec/support/examples/terse_arrays.rb +11 -0
- data/spec/support/examples/terse_objects.rb +16 -0
- data/spec/support/examples/verbose_arrays.rb +19 -0
- data/spec/support/examples/verbose_objects.rb +24 -0
- data/spec/support/integration_helper.rb +12 -0
- data/spec/support/shared_contexts_for_entity.rb +24 -0
- data/spec/support/shared_examples_for_numeric.rb +10 -0
- data/spec/unit/array_spec.rb +55 -0
- data/spec/unit/boolean_spec.rb +6 -0
- data/spec/unit/dsl_spec.rb +70 -0
- data/spec/unit/entity_spec.rb +114 -0
- data/spec/unit/integer_spec.rb +7 -0
- data/spec/unit/null_spec.rb +6 -0
- data/spec/unit/number_spec.rb +7 -0
- data/spec/unit/object_spec.rb +28 -0
- data/spec/unit/schema_spec.rb +35 -0
- data/spec/unit/string_spec.rb +10 -0
- metadata +230 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module JSON
|
4
|
+
module SchemaBuilder
|
5
|
+
class Schema < OpenStruct
|
6
|
+
def merge(schema)
|
7
|
+
self.class.new to_h.deep_merge schema.to_h
|
8
|
+
end
|
9
|
+
|
10
|
+
def merge!(schema)
|
11
|
+
@table = to_h.deep_merge schema.to_h
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Examples::EntityLiterals, type: :integration do
|
4
|
+
it_behaves_like 'a builder' do
|
5
|
+
let(:expected_json) do
|
6
|
+
{
|
7
|
+
type: :object,
|
8
|
+
required: [:one],
|
9
|
+
properties: {
|
10
|
+
one: {
|
11
|
+
enum: [:string, :null]
|
12
|
+
},
|
13
|
+
two: {
|
14
|
+
allOf: [
|
15
|
+
{ type: :integer, minimum: 1 },
|
16
|
+
{ type: :string }
|
17
|
+
]
|
18
|
+
},
|
19
|
+
three: {
|
20
|
+
anyOf: [
|
21
|
+
{ type: :integer, minimum: 1 },
|
22
|
+
{ type: :null }
|
23
|
+
]
|
24
|
+
},
|
25
|
+
four: {
|
26
|
+
not: {
|
27
|
+
type: :null
|
28
|
+
}
|
29
|
+
},
|
30
|
+
five: {
|
31
|
+
oneOf: [
|
32
|
+
{ type: :string },
|
33
|
+
{ type: :number }
|
34
|
+
]
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Examples::MixedArrays, type: :integration do
|
4
|
+
it_behaves_like 'a builder' do
|
5
|
+
let(:expected_json) do
|
6
|
+
{
|
7
|
+
type: :array,
|
8
|
+
items: {
|
9
|
+
type: :array,
|
10
|
+
items: [{ }, { }],
|
11
|
+
additionalItems: false
|
12
|
+
}
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Examples::SchemaBuilder, type: :integration do
|
4
|
+
types = %w(array boolean integer null number object string)
|
5
|
+
|
6
|
+
types.each do |type|
|
7
|
+
describe "##{ type }" do
|
8
|
+
let(:klass){ "JSON::SchemaBuilder::#{ type.classify }".constantize }
|
9
|
+
|
10
|
+
it "should register #{ type } type" do
|
11
|
+
expect(subject.types[type.to_sym]).to be klass
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should define a #{ type } method" do
|
15
|
+
expect(subject).to respond_to type.to_sym
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Examples::TerseArrays, type: :integration do
|
4
|
+
it_behaves_like 'a builder' do
|
5
|
+
let(:expected_json) do
|
6
|
+
{
|
7
|
+
type: :array,
|
8
|
+
uniqueItems: true,
|
9
|
+
items: {
|
10
|
+
type: :string,
|
11
|
+
minLength: 1,
|
12
|
+
maxLength: 5,
|
13
|
+
pattern: '^test'
|
14
|
+
}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Examples::TerseObjects, type: :integration do
|
4
|
+
it_behaves_like 'a builder' do
|
5
|
+
let(:expected_json) do
|
6
|
+
{
|
7
|
+
type: :object,
|
8
|
+
properties: {
|
9
|
+
name: {
|
10
|
+
type: :string,
|
11
|
+
minLength: 1
|
12
|
+
},
|
13
|
+
ids: {
|
14
|
+
type: :array,
|
15
|
+
minItems: 1,
|
16
|
+
items: {
|
17
|
+
type: :object,
|
18
|
+
required: [:id],
|
19
|
+
properties: {
|
20
|
+
id: {
|
21
|
+
type: :integer
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Examples::VerboseArrays, type: :integration do
|
4
|
+
it_behaves_like 'a builder' do
|
5
|
+
let(:expected_json) do
|
6
|
+
{
|
7
|
+
type: :array,
|
8
|
+
uniqueItems: true,
|
9
|
+
items: {
|
10
|
+
type: :string,
|
11
|
+
minLength: 1,
|
12
|
+
maxLength: 5,
|
13
|
+
pattern: '^test'
|
14
|
+
}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Examples::VerboseObjects, type: :integration do
|
4
|
+
it_behaves_like 'a builder' do
|
5
|
+
let(:expected_json) do
|
6
|
+
{
|
7
|
+
type: :object,
|
8
|
+
properties: {
|
9
|
+
name: {
|
10
|
+
type: :string,
|
11
|
+
minLength: 1
|
12
|
+
},
|
13
|
+
ids: {
|
14
|
+
type: :array,
|
15
|
+
minItems: 1,
|
16
|
+
items: {
|
17
|
+
type: :object,
|
18
|
+
required: [:id],
|
19
|
+
properties: {
|
20
|
+
id: {
|
21
|
+
type: :integer
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'pry'
|
2
|
+
%w(lib spec/support).each do |path|
|
3
|
+
Dir["./#{ path }/**/*.rb"].sort.each{ |file| require file }
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'codeclimate-test-reporter'
|
7
|
+
CodeClimate::TestReporter.start
|
8
|
+
|
9
|
+
require 'rspec/its'
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.disable_monkey_patching!
|
12
|
+
config.include IntegrationHelper, type: :integration
|
13
|
+
end
|
data/spec/support/.keep
ADDED
File without changes
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec::Matchers.define :define_attribute do |attribute|
|
4
|
+
match do |entity_klass|
|
5
|
+
entity = entity_klass.new 'entity'
|
6
|
+
entity.respond_to?(attribute) &&
|
7
|
+
entity.respond_to?("#{ attribute }=")
|
8
|
+
end
|
9
|
+
|
10
|
+
failure_message do |entity_klass|
|
11
|
+
"#{ entity_klass.class } does not define attribute #{ attribute.inspect }"
|
12
|
+
end
|
13
|
+
|
14
|
+
failure_message_when_negated do |entity_klass|
|
15
|
+
"#{ entity_klass.class } does define attribute #{ attribute.inspect }"
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Examples
|
2
|
+
class EntityLiterals
|
3
|
+
include JSON::SchemaBuilder
|
4
|
+
|
5
|
+
def example
|
6
|
+
object do
|
7
|
+
entity 'one', required: true do
|
8
|
+
enum [:string, :null]
|
9
|
+
end
|
10
|
+
|
11
|
+
entity 'two' do
|
12
|
+
all_of positive_int, string
|
13
|
+
end
|
14
|
+
|
15
|
+
entity 'three' do
|
16
|
+
any_of [positive_int, null]
|
17
|
+
end
|
18
|
+
|
19
|
+
entity 'four' do
|
20
|
+
not_a null
|
21
|
+
end
|
22
|
+
|
23
|
+
entity 'five' do
|
24
|
+
one_of string, number
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def positive_int
|
30
|
+
integer minimum: 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Examples
|
2
|
+
class TerseObjects
|
3
|
+
include JSON::SchemaBuilder
|
4
|
+
|
5
|
+
def example
|
6
|
+
object do
|
7
|
+
string :name, min_length: 1
|
8
|
+
array :ids, min_items: 1 do
|
9
|
+
items type: :object do
|
10
|
+
integer :id, required: true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Examples
|
2
|
+
class VerboseObjects
|
3
|
+
include JSON::SchemaBuilder
|
4
|
+
|
5
|
+
def example
|
6
|
+
object do
|
7
|
+
string :name do
|
8
|
+
min_length 1
|
9
|
+
end
|
10
|
+
|
11
|
+
array :ids do
|
12
|
+
min_items 1
|
13
|
+
|
14
|
+
items do
|
15
|
+
object do
|
16
|
+
required :id
|
17
|
+
integer :id
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module IntegrationHelper
|
2
|
+
extend RSpec::SharedContext
|
3
|
+
let(:schema){ described_class.new.example }
|
4
|
+
let(:json){ schema.as_json }
|
5
|
+
let(:expected_json){ { } }
|
6
|
+
|
7
|
+
RSpec.shared_examples_for 'a builder' do
|
8
|
+
it 'should produce the correct json schema' do
|
9
|
+
expect(json).to eql expected_json.as_json
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.shared_context 'an entity' do
|
4
|
+
let(:klass) do
|
5
|
+
Class.new JSON::SchemaBuilder::Entity do
|
6
|
+
attribute :test
|
7
|
+
attribute :test_name
|
8
|
+
attribute :test_list, array: true
|
9
|
+
attribute :test_as, as: :testOther
|
10
|
+
def initialize(*args); end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
subject{ klass.new }
|
15
|
+
end
|
16
|
+
|
17
|
+
RSpec.shared_context 'an entity with a parent' do
|
18
|
+
let(:parent){ OpenStruct.new children: [], required: [] }
|
19
|
+
subject do
|
20
|
+
JSON::SchemaBuilder::Entity.new 'name', title: 'test', parent: parent do
|
21
|
+
schema.evaluated_block = true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.shared_examples_for 'a numeric entity' do
|
4
|
+
subject{ described_class }
|
5
|
+
it{ is_expected.to define_attribute :multiple_of }
|
6
|
+
it{ is_expected.to define_attribute :minimum }
|
7
|
+
it{ is_expected.to define_attribute :maximum }
|
8
|
+
it{ is_expected.to define_attribute :exclusive_minimum }
|
9
|
+
it{ is_expected.to define_attribute :exclusive_maximum }
|
10
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe JSON::SchemaBuilder::Array, type: :unit do
|
4
|
+
subject{ described_class }
|
5
|
+
|
6
|
+
it{ is_expected.to define_attribute :additional_items }
|
7
|
+
it{ is_expected.to define_attribute :min_items }
|
8
|
+
it{ is_expected.to define_attribute :max_items }
|
9
|
+
it{ is_expected.to define_attribute :unique_items }
|
10
|
+
its(:registered_type){ is_expected.to eql :array }
|
11
|
+
|
12
|
+
describe '#items' do
|
13
|
+
context 'with a simple array' do
|
14
|
+
let(:array) do
|
15
|
+
described_class.new 'name' do
|
16
|
+
min_items 2
|
17
|
+
items type: :string do
|
18
|
+
min_length 3
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
subject{ array.as_json }
|
24
|
+
its(['type']){ is_expected.to eql 'array' }
|
25
|
+
its(['minItems']){ is_expected.to eql 2 }
|
26
|
+
its(['items']){ is_expected.to eql 'type' => 'string', 'minLength' => 3 }
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with nested structure' do
|
30
|
+
let(:array) do
|
31
|
+
described_class.new 'name' do
|
32
|
+
items do
|
33
|
+
object do
|
34
|
+
string :key_name, required: true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
subject{ array.as_json }
|
41
|
+
its(['type']){ is_expected.to eql 'array' }
|
42
|
+
|
43
|
+
describe 'items' do
|
44
|
+
subject{ array.as_json['items'] }
|
45
|
+
its(['type']){ is_expected.to eql 'object' }
|
46
|
+
its(['required']){ is_expected.to eql ['key_name'] }
|
47
|
+
|
48
|
+
describe "items['properties']" do
|
49
|
+
subject{ array.as_json['items']['properties'] }
|
50
|
+
its(['key_name']){ is_expected.to eql 'type' => 'string' }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|