sinatra-schema 0.0.5 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/lib/sinatra/schema.rb +4 -1
- data/lib/sinatra/schema/definition.rb +4 -27
- data/lib/sinatra/schema/dsl/definitions.rb +9 -13
- data/lib/sinatra/schema/dsl/resources.rb +28 -5
- data/lib/sinatra/schema/json_schema.rb +73 -0
- data/lib/sinatra/schema/link.rb +2 -10
- data/lib/sinatra/schema/reference.rb +36 -0
- data/lib/sinatra/schema/resource.rb +0 -13
- data/lib/sinatra/schema/root.rb +0 -10
- data/lib/sinatra/schema/version.rb +1 -1
- data/spec/definition_spec.rb +21 -18
- data/spec/dsl/definitions_spec.rb +12 -35
- data/spec/dsl/resources_spec.rb +28 -1
- data/spec/integration_spec.rb +46 -15
- data/spec/json_schema_spec.rb +50 -20
- data/spec/reference_spec.rb +53 -0
- data/spec/support/test_app.rb +2 -2
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ed53578697a31d2928747c5e315a6bf74e3a5b1
|
4
|
+
data.tar.gz: 1c8fabb80b8ee50d14186838d1a29f53d4bc039b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3a1e7c20a2737d577391748a479bddb1761183777c4797cf6dfc6009076c4caec7dc408aa174eaab7f382d34abc839f49a01728d99e2753e88e5a42c2c668ec
|
7
|
+
data.tar.gz: 7e12db3a44fe1ee3574f5feb3d9fa47c676820d24587a9f85e5d391b7678574288460bd2cef166d6c26055701f147d43fceef2acc5a4ae491f12594af1cf11dd
|
data/README.md
CHANGED
@@ -17,7 +17,7 @@ class MyApi < Sinatra::Base
|
|
17
17
|
resource("/account") do |res|
|
18
18
|
res.property.text :email
|
19
19
|
|
20
|
-
res.
|
20
|
+
res.get do |link|
|
21
21
|
link.action do
|
22
22
|
# note per definition above we need to serialize "email"
|
23
23
|
MultiJson.encode(email: current_user.email)
|
@@ -35,7 +35,7 @@ Links can have properties too:
|
|
35
35
|
resource("/account") do |res|
|
36
36
|
res.property.text :email
|
37
37
|
|
38
|
-
res.
|
38
|
+
res.post do |link|
|
39
39
|
link.property.ref :email # reuse the property defined above
|
40
40
|
link.property.text :role, optional: true
|
41
41
|
link.property.bool :admin
|
data/lib/sinatra/schema.rb
CHANGED
@@ -2,12 +2,15 @@ require "active_support/inflector"
|
|
2
2
|
require "sinatra/base"
|
3
3
|
require "singleton"
|
4
4
|
require "multi_json"
|
5
|
+
require "time"
|
5
6
|
|
6
7
|
require "sinatra/schema/definition"
|
7
8
|
require "sinatra/schema/error"
|
9
|
+
require "sinatra/schema/json_schema"
|
8
10
|
require "sinatra/schema/link"
|
9
11
|
require "sinatra/schema/param_parsing"
|
10
12
|
require "sinatra/schema/param_validation"
|
13
|
+
require "sinatra/schema/reference"
|
11
14
|
require "sinatra/schema/resource"
|
12
15
|
require "sinatra/schema/root"
|
13
16
|
require "sinatra/schema/utils"
|
@@ -32,7 +35,7 @@ module Sinatra
|
|
32
35
|
app.get "/schema" do
|
33
36
|
content_type("application/schema+json")
|
34
37
|
response.headers["Cache-Control"] = "public, max-age=3600"
|
35
|
-
MultiJson.encode(app.schema_root
|
38
|
+
MultiJson.encode(JsonSchema.dump(app.schema_root), pretty: true)
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
@@ -18,6 +18,8 @@ module Sinatra
|
|
18
18
|
case type
|
19
19
|
when "boolean"
|
20
20
|
%w( t true 1 ).include?(value.to_s)
|
21
|
+
when "datetime"
|
22
|
+
Time.parse(value.to_s)
|
21
23
|
when "email", "string", "uuid"
|
22
24
|
value.to_s
|
23
25
|
end
|
@@ -29,6 +31,8 @@ module Sinatra
|
|
29
31
|
case type
|
30
32
|
when "boolean"
|
31
33
|
[true, false].include?(value)
|
34
|
+
when "datetime"
|
35
|
+
value.to_s =~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-2][0-9]:[0-5][0-9]:[0-5][0-9](\.[0-9]+)?(Z|[\-+][0-9]{2}:[0-5][0-9])$/
|
32
36
|
when "email"
|
33
37
|
value.to_s =~ /\A[^@\s]+@([^@\s]+\.)+[^@\s]+\z/
|
34
38
|
when "string"
|
@@ -37,33 +41,6 @@ module Sinatra
|
|
37
41
|
value.to_s =~ /\A[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}\Z/
|
38
42
|
end
|
39
43
|
end
|
40
|
-
|
41
|
-
def to_schema
|
42
|
-
schema_type, schema_format = json_schema_type_and_format
|
43
|
-
attrs = { type: schema_type }
|
44
|
-
if schema_format
|
45
|
-
attrs[:format] = schema_format
|
46
|
-
end
|
47
|
-
if description
|
48
|
-
attrs[:description] = description
|
49
|
-
end
|
50
|
-
attrs
|
51
|
-
end
|
52
|
-
|
53
|
-
protected
|
54
|
-
|
55
|
-
def json_schema_type_and_format
|
56
|
-
case type
|
57
|
-
when "boolean"
|
58
|
-
"boolean"
|
59
|
-
when "email"
|
60
|
-
["string", "email"]
|
61
|
-
when "string"
|
62
|
-
"string"
|
63
|
-
when "uuid"
|
64
|
-
["string", "uuid"]
|
65
|
-
end
|
66
|
-
end
|
67
44
|
end
|
68
45
|
end
|
69
46
|
end
|
@@ -25,6 +25,11 @@ module Sinatra
|
|
25
25
|
add Definition.new(options)
|
26
26
|
end
|
27
27
|
|
28
|
+
def datetime(id, options={})
|
29
|
+
options.merge!(id: id, type: "datetime")
|
30
|
+
add Definition.new(options)
|
31
|
+
end
|
32
|
+
|
28
33
|
def email(id, options={})
|
29
34
|
options.merge!(id: id, type: "email")
|
30
35
|
add Definition.new(options)
|
@@ -40,25 +45,16 @@ module Sinatra
|
|
40
45
|
end
|
41
46
|
|
42
47
|
def ref(id, ref_to=nil)
|
43
|
-
|
44
|
-
unless definition = resource.defs[ref_to] || Sinatra::Schema::Root.instance.find_definition(ref_to)
|
45
|
-
raise BadReference.new(id)
|
46
|
-
end
|
47
|
-
add definition, true, id
|
48
|
+
add Reference.new(resource, id, ref_to), true
|
48
49
|
end
|
49
50
|
|
50
51
|
# TODO support other types
|
51
52
|
|
52
53
|
protected
|
53
54
|
|
54
|
-
def add(definition, reference=false
|
55
|
-
|
56
|
-
|
57
|
-
# here's the trick, and here's why the first target is always the
|
58
|
-
# resource def: skip it when adding a reference (eg: it's already)
|
59
|
-
# in the resource def, just add the property!
|
60
|
-
next if reference && i == 0
|
61
|
-
hash[id] = definition
|
55
|
+
def add(definition, reference=false)
|
56
|
+
targets.each do |target|
|
57
|
+
target[definition.id] ||= definition
|
62
58
|
end
|
63
59
|
end
|
64
60
|
end
|
@@ -17,12 +17,35 @@ module Sinatra
|
|
17
17
|
DSL::Definitions.new(resource, [resource.defs, resource.properties])
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
20
|
+
def delete(href="/", &blk)
|
21
|
+
build_link(:delete, href, &blk)
|
22
|
+
end
|
23
|
+
|
24
|
+
def get(href="/", &blk)
|
25
|
+
build_link(:get, href, &blk)
|
26
|
+
end
|
27
|
+
|
28
|
+
def patch(href="/", &blk)
|
29
|
+
build_link(:patch, href, &blk)
|
30
|
+
end
|
31
|
+
|
32
|
+
def post(href="/", &blk)
|
33
|
+
build_link(:post, href, &blk)
|
34
|
+
end
|
35
|
+
|
36
|
+
def put(href="/", &blk)
|
37
|
+
build_link(:put, href, &blk)
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def build_link(method, href="/", &blk)
|
21
43
|
dsl = DSL::Links.new(resource: resource, method: method, href: href)
|
22
|
-
blk.call(dsl)
|
23
|
-
link
|
24
|
-
|
25
|
-
|
44
|
+
blk.call(dsl) if blk
|
45
|
+
dsl.link.tap do |link|
|
46
|
+
link.register(app)
|
47
|
+
resource.links << link
|
48
|
+
end
|
26
49
|
end
|
27
50
|
end
|
28
51
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module Schema
|
3
|
+
class JsonSchema
|
4
|
+
attr_accessor :root
|
5
|
+
|
6
|
+
def self.dump(root)
|
7
|
+
new(root).dump_root
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(root)
|
11
|
+
@root = root
|
12
|
+
end
|
13
|
+
|
14
|
+
def dump_root
|
15
|
+
{
|
16
|
+
"$schema" => "http://json-schema.org/draft-04/hyper-schema",
|
17
|
+
"definitions" => root.resources.inject({}) { |result, (id, resource)|
|
18
|
+
result.merge(id => dump_resource(resource))
|
19
|
+
}
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def dump_resource(resource)
|
24
|
+
{
|
25
|
+
title: resource.title,
|
26
|
+
description: resource.description,
|
27
|
+
type: "object",
|
28
|
+
definitions: resource.defs.inject({}) { |h, (id, definition)|
|
29
|
+
h.merge(id => dump_definition(definition))
|
30
|
+
},
|
31
|
+
links: resource.links.map { |link| dump_link(link) }
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def dump_definition(definition)
|
36
|
+
schema_type, schema_format = json_schema_type_and_format(definition.type)
|
37
|
+
attrs = { type: schema_type }
|
38
|
+
if schema_format
|
39
|
+
attrs[:format] = schema_format
|
40
|
+
end
|
41
|
+
if definition.description
|
42
|
+
attrs[:description] = definition.description
|
43
|
+
end
|
44
|
+
attrs
|
45
|
+
end
|
46
|
+
|
47
|
+
def dump_link(link)
|
48
|
+
{
|
49
|
+
description: link.description,
|
50
|
+
href: link.href,
|
51
|
+
method: link.method.to_s.upcase,
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def json_schema_type_and_format(type)
|
58
|
+
case type
|
59
|
+
when "boolean"
|
60
|
+
"boolean"
|
61
|
+
when "datetime"
|
62
|
+
["string", "date-time"]
|
63
|
+
when "email"
|
64
|
+
["string", "email"]
|
65
|
+
when "string"
|
66
|
+
"string"
|
67
|
+
when "uuid"
|
68
|
+
["string", "uuid"]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/sinatra/schema/link.rb
CHANGED
@@ -3,7 +3,7 @@ module Sinatra
|
|
3
3
|
class Link
|
4
4
|
attr_accessor :action_block, :resource, :title, :description, :href, :method, :properties, :rel
|
5
5
|
|
6
|
-
def initialize(options)
|
6
|
+
def initialize(options={})
|
7
7
|
@resource = options[:resource]
|
8
8
|
@method = options[:method]
|
9
9
|
@href = options[:href]
|
@@ -11,7 +11,7 @@ module Sinatra
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def register(app)
|
14
|
-
app.send(method
|
14
|
+
app.send(method, href, &handler)
|
15
15
|
end
|
16
16
|
|
17
17
|
def handler
|
@@ -26,14 +26,6 @@ module Sinatra
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
29
|
-
|
30
|
-
def to_schema
|
31
|
-
{
|
32
|
-
description: description,
|
33
|
-
href: href,
|
34
|
-
method: method.to_s.upcase,
|
35
|
-
}
|
36
|
-
end
|
37
29
|
end
|
38
30
|
end
|
39
31
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module Schema
|
3
|
+
class Reference
|
4
|
+
attr_accessor :id, :ref_spec, :referenced_def, :resource
|
5
|
+
|
6
|
+
# helper to lazily delegate method to the referenced definition
|
7
|
+
def self.delegate(attribute)
|
8
|
+
define_method(attribute) do |*args|
|
9
|
+
resolve!
|
10
|
+
referenced_def.send(attribute, *args)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(resource, id, ref_spec=nil)
|
15
|
+
@id = id
|
16
|
+
@ref_spec = ref_spec || id
|
17
|
+
@resource = resource
|
18
|
+
end
|
19
|
+
|
20
|
+
def resolve!
|
21
|
+
return if referenced_def
|
22
|
+
unless @referenced_def = resource.defs[ref_spec] || Sinatra::Schema::Root.instance.find_definition(ref_spec)
|
23
|
+
raise BadReference.new(ref_spec)
|
24
|
+
end
|
25
|
+
return @referenced_def
|
26
|
+
end
|
27
|
+
|
28
|
+
delegate :cast
|
29
|
+
delegate :description
|
30
|
+
delegate :example
|
31
|
+
delegate :optional
|
32
|
+
delegate :type
|
33
|
+
delegate :valid?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -31,19 +31,6 @@ module Sinatra
|
|
31
31
|
Utils.validate_keys!(properties, res)
|
32
32
|
end
|
33
33
|
end
|
34
|
-
|
35
|
-
def to_schema
|
36
|
-
{
|
37
|
-
title: title,
|
38
|
-
description: description,
|
39
|
-
type: "object",
|
40
|
-
definitions: defs.inject({}) { |h, (id, definition)|
|
41
|
-
h[id] = definition.to_schema
|
42
|
-
h
|
43
|
-
},
|
44
|
-
links: links.map(&:to_schema)
|
45
|
-
}
|
46
|
-
end
|
47
34
|
end
|
48
35
|
end
|
49
36
|
end
|
data/lib/sinatra/schema/root.rb
CHANGED
@@ -18,16 +18,6 @@ module Sinatra
|
|
18
18
|
return unless resource = resources[resource_id.to_sym]
|
19
19
|
resource.defs[def_id.to_sym]
|
20
20
|
end
|
21
|
-
|
22
|
-
def to_schema
|
23
|
-
{
|
24
|
-
"$schema" => "http://json-schema.org/draft-04/hyper-schema",
|
25
|
-
"definitions" => resources.inject({}) { |result, (id, resource)|
|
26
|
-
result[id] = resource.to_schema
|
27
|
-
result
|
28
|
-
}
|
29
|
-
}
|
30
|
-
end
|
31
21
|
end
|
32
22
|
end
|
33
23
|
end
|
data/spec/definition_spec.rb
CHANGED
@@ -13,6 +13,19 @@ describe Sinatra::Schema::Definition do
|
|
13
13
|
assert_equal false, definition.cast("false")
|
14
14
|
end
|
15
15
|
|
16
|
+
it "casts datetime" do
|
17
|
+
definition.type = "datetime"
|
18
|
+
t = definition.cast("2014-05-01T12:13:14.15Z")
|
19
|
+
assert_equal 2014, t.year
|
20
|
+
assert_equal 5, t.month
|
21
|
+
assert_equal 1, t.day
|
22
|
+
assert_equal 12, t.hour
|
23
|
+
assert_equal 13, t.min
|
24
|
+
assert_equal 14, t.sec
|
25
|
+
assert_equal 150000, t.usec
|
26
|
+
assert_equal "UTC", t.zone
|
27
|
+
end
|
28
|
+
|
16
29
|
it "casts text" do
|
17
30
|
definition.type = "string"
|
18
31
|
assert_equal "123", definition.cast(123)
|
@@ -20,45 +33,35 @@ describe Sinatra::Schema::Definition do
|
|
20
33
|
end
|
21
34
|
|
22
35
|
describe "#valid?" do
|
23
|
-
it "
|
36
|
+
it "validates booleans" do
|
24
37
|
definition.type = "boolean"
|
25
38
|
assert definition.valid?(true)
|
26
39
|
assert definition.valid?(false)
|
27
40
|
refute definition.valid?("true")
|
28
41
|
end
|
29
42
|
|
30
|
-
it "
|
43
|
+
it "validates emails" do
|
31
44
|
definition.type = "email"
|
32
45
|
assert definition.valid?("foo@bar.com")
|
33
46
|
refute definition.valid?("foobar.com")
|
34
47
|
end
|
35
48
|
|
36
|
-
it "
|
49
|
+
it "validates text" do
|
37
50
|
definition.type = "string"
|
38
51
|
assert definition.valid?("foo")
|
39
52
|
refute definition.valid?(123)
|
40
53
|
end
|
41
54
|
|
42
|
-
it "
|
55
|
+
it "validates uuids" do
|
43
56
|
definition.type = "uuid"
|
44
57
|
assert definition.valid?(SecureRandom.uuid)
|
45
58
|
refute definition.valid?("wrong")
|
46
59
|
end
|
47
|
-
end
|
48
60
|
|
49
|
-
|
50
|
-
|
51
|
-
definition.
|
52
|
-
|
53
|
-
assert_equal "string", schema[:type]
|
54
|
-
assert_equal "email", schema[:format]
|
55
|
-
end
|
56
|
-
|
57
|
-
it "dumps uuids" do
|
58
|
-
definition.type = "uuid"
|
59
|
-
schema = definition.to_schema
|
60
|
-
assert_equal "string", schema[:type]
|
61
|
-
assert_equal "uuid", schema[:format]
|
61
|
+
it "validates date+time fields" do
|
62
|
+
definition.type = "datetime"
|
63
|
+
assert definition.valid?("1985-04-12T23:20:50.52Z")
|
64
|
+
refute definition.valid?("12/4/1985 23:20:50")
|
62
65
|
end
|
63
66
|
end
|
64
67
|
end
|
@@ -17,6 +17,12 @@ describe Sinatra::Schema::DSL::Definitions do
|
|
17
17
|
assert_equal "boolean", resource.defs[:foobar].type
|
18
18
|
end
|
19
19
|
|
20
|
+
it "adds a datetime definition to the resource" do
|
21
|
+
dsl.datetime(:foobar)
|
22
|
+
assert_equal 1, resource.defs.size
|
23
|
+
assert_equal "datetime", resource.defs[:foobar].type
|
24
|
+
end
|
25
|
+
|
20
26
|
it "adds nested definitions" do
|
21
27
|
dsl[:user].text :email
|
22
28
|
dsl[:user].bool :admin
|
@@ -25,43 +31,14 @@ describe Sinatra::Schema::DSL::Definitions do
|
|
25
31
|
assert_equal "boolean", resource.defs[:user][:admin].type
|
26
32
|
end
|
27
33
|
|
34
|
+
it "adds references" do
|
35
|
+
dsl.ref(:another_property)
|
36
|
+
assert_equal 1, resource.defs.size
|
37
|
+
assert_instance_of Sinatra::Schema::Reference, resource.defs[:another_property]
|
38
|
+
end
|
39
|
+
|
28
40
|
it "sets other options" do
|
29
41
|
dsl.text(:foobar, optional: true)
|
30
42
|
assert_equal true, resource.defs[:foobar].optional
|
31
43
|
end
|
32
|
-
|
33
|
-
describe "#ref" do
|
34
|
-
let(:definition) { Sinatra::Schema::Definition.new(id: :foobar) }
|
35
|
-
|
36
|
-
it "adds a reference to another definition in the resource" do
|
37
|
-
resource.defs[:foobar] = definition
|
38
|
-
dsl.ref :foobar
|
39
|
-
assert_equal 1, resource.defs.size
|
40
|
-
assert_equal 1, resource.properties.size
|
41
|
-
end
|
42
|
-
|
43
|
-
it "adds a reference to a definition in a different resource" do
|
44
|
-
other = Sinatra::Schema::Resource.new(path: "/others")
|
45
|
-
root.add_resource(other)
|
46
|
-
other.defs[:foobar] = definition
|
47
|
-
dsl.ref "other/foobar"
|
48
|
-
assert_equal 1, other.defs.size
|
49
|
-
assert_equal 1, resource.properties.size
|
50
|
-
end
|
51
|
-
|
52
|
-
it "raises when we can't resolve the ref" do
|
53
|
-
assert_raises(Sinatra::Schema::BadReference) do
|
54
|
-
dsl.ref :foobar
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
it "allows references to have a different id" do
|
59
|
-
other = Sinatra::Schema::Resource.new(path: "/others")
|
60
|
-
root.add_resource(other)
|
61
|
-
other.defs[:foobar] = definition
|
62
|
-
dsl.ref :my_foobar, "other/foobar"
|
63
|
-
assert resource.properties.has_key?(:my_foobar)
|
64
|
-
assert_equal :foobar, resource.properties[:my_foobar].id
|
65
|
-
end
|
66
|
-
end
|
67
44
|
end
|
data/spec/dsl/resources_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Sinatra::Schema::DSL::Resources do
|
4
|
-
let(:app) { Sinatra
|
4
|
+
let(:app) { Sinatra.new {} }
|
5
5
|
let(:dsl) { described_class.new(app, "/foobar") }
|
6
6
|
|
7
7
|
it "sets the resource description" do
|
@@ -28,4 +28,31 @@ describe Sinatra::Schema::DSL::Resources do
|
|
28
28
|
assert_equal 2, dsl.resource.properties[:user].size
|
29
29
|
end
|
30
30
|
end
|
31
|
+
|
32
|
+
describe "building links" do
|
33
|
+
it "builds a DELETE" do
|
34
|
+
link = dsl.delete
|
35
|
+
assert_equal :delete, link.method
|
36
|
+
end
|
37
|
+
|
38
|
+
it "builds a GET" do
|
39
|
+
link = dsl.get
|
40
|
+
assert_equal :get, link.method
|
41
|
+
end
|
42
|
+
|
43
|
+
it "builds a PATCH" do
|
44
|
+
link = dsl.patch
|
45
|
+
assert_equal :patch, link.method
|
46
|
+
end
|
47
|
+
|
48
|
+
it "builds a POST" do
|
49
|
+
link = dsl.post
|
50
|
+
assert_equal :post, link.method
|
51
|
+
end
|
52
|
+
|
53
|
+
it "builds a PUT" do
|
54
|
+
link = dsl.put
|
55
|
+
assert_equal :put, link.method
|
56
|
+
end
|
57
|
+
end
|
31
58
|
end
|
data/spec/integration_spec.rb
CHANGED
@@ -1,24 +1,55 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Sinatra::Schema do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
describe "resource access" do
|
5
|
+
it "still works as a regular sinatra app" do
|
6
|
+
get "/regular"
|
7
|
+
assert_equal 200, last_response.status
|
8
|
+
assert_equal "hi", last_response.body
|
9
|
+
end
|
10
|
+
|
11
|
+
it "support resource gets" do
|
12
|
+
get "/accounts"
|
13
|
+
assert_equal 200, last_response.status
|
14
|
+
assert_equal({ "email" => "foo@bar.com" },
|
15
|
+
MultiJson.decode(last_response.body))
|
16
|
+
end
|
9
17
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
18
|
+
it "support resource posts" do
|
19
|
+
post "/accounts", email: "omg"
|
20
|
+
assert_equal 200, last_response.status
|
21
|
+
assert_equal({ "email" => "omg" },
|
22
|
+
MultiJson.decode(last_response.body))
|
23
|
+
end
|
15
24
|
end
|
16
25
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
26
|
+
describe "GET /schema" do
|
27
|
+
before do
|
28
|
+
get "/schema"
|
29
|
+
@schema = MultiJson.decode(last_response.body)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "renders a 200" do
|
33
|
+
assert_equal 200, last_response.status
|
34
|
+
end
|
35
|
+
|
36
|
+
it "sets the appropriate content-type" do
|
37
|
+
assert_equal "application/schema+json",
|
38
|
+
last_response.headers["Content-Type"]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "sets $schema to hyper-schema" do
|
42
|
+
assert_equal "http://json-schema.org/draft-04/hyper-schema",
|
43
|
+
@schema["$schema"]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "generates definitions" do
|
47
|
+
assert_equal Hash, @schema["definitions"].class
|
48
|
+
assert_equal Hash, @schema["definitions"]["account"].class
|
49
|
+
assert_equal "Account", @schema["definitions"]["account"]["title"]
|
50
|
+
assert_equal "An account represents an individual signed up to use the service",
|
51
|
+
@schema["definitions"]["account"]["description"]
|
52
|
+
end
|
22
53
|
end
|
23
54
|
|
24
55
|
describe "error handling" do
|
data/spec/json_schema_spec.rb
CHANGED
@@ -1,30 +1,60 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
describe
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
3
|
+
describe Sinatra::Schema::JsonSchema do
|
4
|
+
let(:json_schema) { described_class.new(root) }
|
5
|
+
let(:root) { Sinatra::Schema::Root.instance }
|
6
|
+
let(:schema) { json_schema.dump_root }
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
describe "#dump_root" do
|
9
|
+
it "sets $schema" do
|
10
|
+
assert_equal "http://json-schema.org/draft-04/hyper-schema",
|
11
|
+
schema["$schema"]
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
it "adds a definition hash" do
|
15
|
+
assert_instance_of Hash, schema["definitions"]
|
16
|
+
end
|
16
17
|
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
describe "#dump_definition" do
|
20
|
+
let(:definition) { Sinatra::Schema::Definition.new }
|
21
|
+
|
22
|
+
it "handles simple string format" do
|
23
|
+
definition.type = "string"
|
24
|
+
schema = json_schema.dump_definition(definition)
|
25
|
+
assert_equal "string", schema[:type]
|
26
|
+
assert_nil schema[:format]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "handles the datetime format" do
|
30
|
+
definition.type = "datetime"
|
31
|
+
schema = json_schema.dump_definition(definition)
|
32
|
+
assert_equal "string", schema[:type]
|
33
|
+
assert_equal "date-time", schema[:format]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "handles the email format" do
|
37
|
+
definition.type = "email"
|
38
|
+
schema = json_schema.dump_definition(definition)
|
39
|
+
assert_equal "string", schema[:type]
|
40
|
+
assert_equal "email", schema[:format]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "handles the uuid format" do
|
44
|
+
definition.type = "uuid"
|
45
|
+
schema = json_schema.dump_definition(definition)
|
46
|
+
assert_equal "string", schema[:type]
|
47
|
+
assert_equal "uuid", schema[:format]
|
48
|
+
end
|
21
49
|
end
|
22
50
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
51
|
+
describe "#dump_link" do
|
52
|
+
let(:link) { Sinatra::Schema::Link.new }
|
53
|
+
|
54
|
+
it "renders the method in upcase" do
|
55
|
+
link.method = :get
|
56
|
+
schema = json_schema.dump_link(link)
|
57
|
+
assert_equal "GET", schema[:method]
|
58
|
+
end
|
29
59
|
end
|
30
60
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Sinatra::Schema::Reference do
|
4
|
+
let(:resource) { Sinatra::Schema::Root.instance.resources[:account] }
|
5
|
+
let(:ref) { described_class.new(resource, :foo, :email) }
|
6
|
+
let(:definition) { resource.defs[:email] }
|
7
|
+
|
8
|
+
describe "#resolve!" do
|
9
|
+
it "finds other properties in the same resource" do
|
10
|
+
ref.ref_spec = :foo
|
11
|
+
resource.defs[:foo] = definition
|
12
|
+
assert_equal definition, ref.resolve!
|
13
|
+
end
|
14
|
+
|
15
|
+
it "finds other properties elsewhere in the schema" do
|
16
|
+
ref.ref_spec = "account/email"
|
17
|
+
assert_equal definition, ref.resolve!
|
18
|
+
end
|
19
|
+
|
20
|
+
it "raises when it cannot resolve" do
|
21
|
+
ref.ref_spec = "does-not-exist"
|
22
|
+
assert_raises(Sinatra::Schema::BadReference) do
|
23
|
+
ref.resolve!
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "delegated attributes" do
|
29
|
+
it "#cast" do
|
30
|
+
assert_equal definition.cast("foo"), ref.cast("foo")
|
31
|
+
end
|
32
|
+
|
33
|
+
it "#description" do
|
34
|
+
assert_equal definition.description, ref.description
|
35
|
+
end
|
36
|
+
|
37
|
+
it "#example" do
|
38
|
+
assert_equal definition.example, ref.example
|
39
|
+
end
|
40
|
+
|
41
|
+
it "#optional" do
|
42
|
+
assert_equal definition.optional, ref.optional
|
43
|
+
end
|
44
|
+
|
45
|
+
it "#type" do
|
46
|
+
assert_equal definition.type, ref.type
|
47
|
+
end
|
48
|
+
|
49
|
+
it "#valid?" do
|
50
|
+
assert_equal definition.valid?("foo"), ref.valid?("foo")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/spec/support/test_app.rb
CHANGED
@@ -13,7 +13,7 @@ class TestApp < Sinatra::Base
|
|
13
13
|
example: "username@example.com",
|
14
14
|
format: "email"
|
15
15
|
|
16
|
-
res.
|
16
|
+
res.get do |link|
|
17
17
|
link.title "Info"
|
18
18
|
link.rel "self"
|
19
19
|
link.description "Info for account"
|
@@ -22,7 +22,7 @@ class TestApp < Sinatra::Base
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
res.
|
25
|
+
res.post do |link|
|
26
26
|
link.title "Create"
|
27
27
|
link.rel "create"
|
28
28
|
link.description "Create a new account"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sinatra-schema
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pedro Belo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -139,9 +139,11 @@ files:
|
|
139
139
|
- lib/sinatra/schema/dsl/links.rb
|
140
140
|
- lib/sinatra/schema/dsl/resources.rb
|
141
141
|
- lib/sinatra/schema/error.rb
|
142
|
+
- lib/sinatra/schema/json_schema.rb
|
142
143
|
- lib/sinatra/schema/link.rb
|
143
144
|
- lib/sinatra/schema/param_parsing.rb
|
144
145
|
- lib/sinatra/schema/param_validation.rb
|
146
|
+
- lib/sinatra/schema/reference.rb
|
145
147
|
- lib/sinatra/schema/resource.rb
|
146
148
|
- lib/sinatra/schema/root.rb
|
147
149
|
- lib/sinatra/schema/tasks/schema.rake
|
@@ -155,6 +157,7 @@ files:
|
|
155
157
|
- spec/json_schema_spec.rb
|
156
158
|
- spec/param_parsing_spec.rb
|
157
159
|
- spec/param_validation_spec.rb
|
160
|
+
- spec/reference_spec.rb
|
158
161
|
- spec/resource_spec.rb
|
159
162
|
- spec/spec_helper.rb
|
160
163
|
- spec/support/last_json.rb
|