rack-schema 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rack-schema.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Kyle Hargraves
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,102 @@
1
+ # Rack::Schema
2
+
3
+ Validate your application's responses against [JSON Schemas][json-schema].
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rack-schema'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rack-schema
18
+
19
+ ## Usage
20
+ Mount `Rack::Schema` as middleware in one of the normal manners:
21
+
22
+ ~~~~ruby
23
+ # using config.ru:
24
+ use Rack::Schema
25
+ run MyApp
26
+
27
+ # or application.rb:
28
+ config.middleware.use Rack::Schema
29
+ ~~~~
30
+
31
+ Your application can now return an HTTP [Link header][link-header]
32
+ with a `rel` attribute value of `describedby`, and `Rack::Schema` will
33
+ automatically attempt to validate responses against the specified
34
+ schema (using the [json-schema gem][hoxworth]). An example `Link`
35
+ header:
36
+
37
+ Link: <http://example.com/schemas/response>; rel="describedby"
38
+
39
+ If your schema applies only to a part of the JSON response, you can
40
+ use the `anchor` attribute to specify a JSON path to the relevant value:
41
+
42
+ Link: <http://example.com/schemas/widget>; rel="describedby"; anchor="#/widget"
43
+
44
+ This is actually a mis-use of the `anchor` attribute, which would
45
+ typically be used to specify an anchor within the *linked* document,
46
+ rather than the document being described. JSON schemas already support
47
+ the use of the hash fragment on its URI, however, so I've
48
+ re-appropriated it. Suggestions for a more compliant tactic are
49
+ welcome.
50
+
51
+ If your response is actually a collection of objects that should all
52
+ validate against the same schema, use the `collection` attribute:
53
+
54
+ # Assert that the response is an array, and each object within it is a valid widget.
55
+ Link: <http://example.com/schemas/widget>; rel="describedby"; collection="collection"
56
+
57
+ # Assert that the object at '#/widgets' is an array, and each object within it is a valid widget.
58
+ Link: <http://example.com/schemas/widget>; rel="describedby"; anchor="#/widgets"; collection="collection"
59
+
60
+ If the `Link` header contains multiple applicable links, they will
61
+ all be used to validate the response:
62
+
63
+ # Assert that '#/teams' is an array of valid teams, and '#/score' is a valid score.
64
+ Link: <http://example.com/schemas/team>; rel="describedby"; anchor="#/teams"; collection="collection",
65
+ <http://example.com/schemas/score>; rel="describedby"; anchor="#/score"
66
+
67
+ ## Configuration
68
+ By default, `rack-schema` will also instruct the validator to validate
69
+ your schema itself *as* a schema. To disable that behavior:
70
+
71
+ ~~~~ruby
72
+ use Rack::Schema, validate_schemas: false
73
+ ~~~~
74
+
75
+ If you are running the `rack-schema` response validator in a
76
+ production environment -- which you probably *shouldn't* be doing --
77
+ you don't want to actually expose the `describedby` link header
78
+ entries to the world, you can tell `rack-schema` to remove them from
79
+ the responses after using them:
80
+
81
+ ~~~~ruby
82
+ use Rack::Schema, swallow_links: true
83
+ ~~~~
84
+
85
+ With `swallow_links` on, only the *describedby* links will be removed;
86
+ your pagination or similar links will not be disturbed.
87
+
88
+
89
+ ## Potential Features?
90
+
91
+ 1. Validate incoming JSON bodies, but I just don't need that right now.
92
+ And it's unclear how we'd determine what schemas to use, or what we'd
93
+ do with the errors.
94
+
95
+ ## See Also
96
+
97
+ 1. [HTTP Link Header][link-header]
98
+ 2. [json-schema gem][hoxworth]
99
+
100
+ [json-schema]: http://json-schema.org
101
+ [link-header]: http://tools.ietf.org/html/rfc5988#section-5
102
+ [hoxworth]: https://github.com/hoxworth/json-schema
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ desc "Run the test suite"
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
@@ -0,0 +1,83 @@
1
+ require "rack/schema/version"
2
+ require "json-schema"
3
+ require "link_header"
4
+ require "multi_json"
5
+
6
+ module Rack
7
+ class Schema
8
+ ValidationError = Class.new(StandardError)
9
+
10
+ def initialize(app, options = {}, &handler)
11
+ @app = app
12
+
13
+ @handler = handler
14
+ @handler ||= proc { |errors, env, (status, headers, body)|
15
+ json = ''
16
+ body.each { |s| json.concat s }
17
+ raise ValidationError.new({ errors: errors, body: json }) if errors.any?
18
+ }
19
+
20
+ @options = {
21
+ validate_schemas: true,
22
+ swallow_links: false
23
+ }.merge(options)
24
+ end
25
+
26
+ def call(env)
27
+ status, headers, body = @app.call(env)
28
+
29
+ link_header = LinkHeader.parse(headers['Link'])
30
+ schema_links = link_header.links.select do |link|
31
+ link.attrs['rel'] == 'describedby'
32
+ end
33
+
34
+ errors = validate(body, schema_links)
35
+ swallow(headers, link_header) if swallow_links?
36
+ response = [status, headers, body]
37
+ @handler.call(errors, env, response) || response
38
+ end
39
+
40
+ private
41
+
42
+ def validate(body, schema_links)
43
+ schema_links.each_with_object [] do |link, acc|
44
+ json = at_anchor(body, link.attrs['anchor'])
45
+
46
+ errs = JSON::Validator.fully_validate link.href, json, {
47
+ validate_schemas: @options[:validate_schemas],
48
+ list: link.attrs.key?('collection')
49
+ }
50
+
51
+ acc.push [link.to_s, errs] if errs.any?
52
+ end
53
+ end
54
+
55
+ def swallow_links?
56
+ @options[:swallow_links] == true
57
+ end
58
+
59
+ def swallow(headers, link_header)
60
+ link_header.links.reject! { |link| link.attrs['rel'] == 'describedby' }
61
+ if link_header.links.any?
62
+ headers['Link'] = link_header.to_s
63
+ else
64
+ headers.delete 'Link'
65
+ end
66
+ end
67
+
68
+ def at_anchor(body, anchor)
69
+ flat = ''
70
+ body.each { |s| flat.concat s }
71
+
72
+ return flat if anchor.nil? || anchor == '#' || anchor == '#/'
73
+
74
+ fragments = anchor.sub(/\A#\//, '').split('/')
75
+ fragments.reduce MultiJson.load(flat) do |value, fragment|
76
+ case value
77
+ when Hash then value.fetch(fragment, nil)
78
+ when Array then value.fetch(fragment.to_i, nil)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ class Schema
3
+ VERSION = "0.5.0"
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rack/schema/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rack-schema"
8
+ spec.version = Rack::Schema::VERSION
9
+ spec.authors = ["Kyle Hargraves"]
10
+ spec.email = ["pd@krh.me"]
11
+ spec.description = %q{Validate rack responses against schema named in the Link header}
12
+ spec.summary = %q{Allows you to strictly validate each of your application's API responses against a declared JSON schema.}
13
+ spec.homepage = "http://github.com/pd/rack-schema"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "rack"
22
+ spec.add_dependency "multi_json"
23
+ spec.add_dependency "json-schema"
24
+ spec.add_dependency "link_header"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.3"
27
+ spec.add_development_dependency "oj"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "rack-test"
30
+ spec.add_development_dependency "rspec"
31
+ spec.add_development_dependency "simplecov"
32
+ spec.add_development_dependency "pry"
33
+ end
@@ -0,0 +1,231 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rack::Schema, 'URIs, anchors, collections oh my!' do
4
+ Invalid = Class.new(StandardError)
5
+
6
+ def app
7
+ Rack::Builder.new do
8
+ use Rack::Schema do |errors, env, (status, headers, body)|
9
+ raise Invalid if errors.any?
10
+ headers['X-Schema'] = 'valid'
11
+ [status, headers, body]
12
+ end
13
+
14
+ run SpecHelpers::EchoApp
15
+ end
16
+ end
17
+
18
+ def assert_valid!
19
+ expect(last_response.headers['X-Schema']).to eq('valid')
20
+ end
21
+
22
+ def expect_invalid(&block)
23
+ expect(&block).to raise_error(Invalid)
24
+ end
25
+
26
+ context 'invalid JSON body' do
27
+ specify 'ignored without Link header' do
28
+ echo headers, '!!!'
29
+ assert_valid!
30
+
31
+ echo headers, nil
32
+ assert_valid!
33
+ end
34
+
35
+ specify 'with Link header present, we might have to parse the body' do
36
+ headers['Link'] = described_by schema_uri('object')
37
+ expect_invalid { echo headers, '!!!' }
38
+ expect_invalid { echo headers, nil }
39
+ end
40
+ end
41
+
42
+ context 'validate entire body' do
43
+ before do
44
+ headers['Link'] = described_by schema_uri('object')
45
+ end
46
+
47
+ specify 'pass' do
48
+ echo headers, {}
49
+ assert_valid!
50
+ end
51
+
52
+ specify 'fail by type' do
53
+ expect_invalid { echo headers, 'a string' }
54
+ expect_invalid { echo headers, [{}, {}] }
55
+ end
56
+ end
57
+
58
+ context 'validate at anchor' do
59
+ before do
60
+ headers['Link'] = described_by schema_uri('widget'), '#/widget'
61
+ end
62
+
63
+ specify 'pass at anchor' do
64
+ echo headers, { 'widget' => { 'name' => 'foo' } }
65
+ assert_valid!
66
+ end
67
+
68
+ specify 'fail; object was at root instead of anchor' do
69
+ expect_invalid do
70
+ echo headers, { 'name' => 'foo' }
71
+ end
72
+ end
73
+
74
+ specify 'fail; no such anchor' do
75
+ expect_invalid do
76
+ echo headers, { 'Widget' => { 'name' => 'foo' } }
77
+ end
78
+ end
79
+
80
+ specify 'fail; anchor pointed to collection' do
81
+ expect_invalid do
82
+ echo headers, { 'widget' => [{'name' => 'foo'}, {'name' => 'bar'}] }
83
+ end
84
+ end
85
+ end
86
+
87
+ context 'validate at array element' do
88
+ before do
89
+ headers['Link'] = described_by schema_uri('widget'), '#/1'
90
+ end
91
+
92
+ specify 'pass' do
93
+ echo headers, ['anything', {'name' => 'real widget'}]
94
+ assert_valid!
95
+ end
96
+
97
+ specify 'fail; invalid' do
98
+ expect_invalid do
99
+ echo headers, ['anything', {'nope' => 'not okay'}]
100
+ end
101
+ end
102
+
103
+ specify 'fail; not an array' do
104
+ expect_invalid do
105
+ echo headers, {'some' => 'object'}
106
+ end
107
+ end
108
+
109
+ specify 'fail; wrong element' do
110
+ expect_invalid do
111
+ echo headers, [{'name' => 'widget'}, 'oops, backwards!']
112
+ end
113
+ end
114
+ end
115
+
116
+ ['#', '#/'].each do |anchor|
117
+ context "validate at anchor #{anchor.inspect}" do
118
+ before do
119
+ headers['Link'] = described_by schema_uri('widget'), anchor
120
+ end
121
+
122
+ specify 'pass' do
123
+ echo headers, { 'name' => 'foo' }
124
+ assert_valid!
125
+ end
126
+
127
+ specify 'fail' do
128
+ expect_invalid do
129
+ echo headers, { 'name' => true }
130
+ end
131
+
132
+ expect_invalid do
133
+ echo headers, { 'widget' => { 'name' => 'foo' } }
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ context 'validate collection' do
140
+ before do
141
+ headers['Link'] = described_by schema_uri('widget'), nil, :collection
142
+ end
143
+
144
+ specify 'pass' do
145
+ echo headers, [{'name' => 'foo'}, {'name' => 'bar'}]
146
+ assert_valid!
147
+ end
148
+
149
+ specify 'pass; empty array' do
150
+ echo headers, []
151
+ assert_valid!
152
+ end
153
+
154
+ specify 'fail; not an array' do
155
+ expect_invalid do
156
+ echo headers, {'name' => 'foo'}
157
+ end
158
+ end
159
+
160
+ specify 'fail; invalid entry' do
161
+ expect_invalid do
162
+ echo headers, [{'name' => 'foo'}, {'invalid' => true}]
163
+ end
164
+ end
165
+ end
166
+
167
+ context 'validate collection at anchor' do
168
+ before do
169
+ headers['Link'] = described_by schema_uri('widget'), '#/widgets', :collection
170
+ end
171
+
172
+ specify 'pass' do
173
+ echo headers, { 'widgets' => [{'name' => 'foo'}, {'name' => 'bar'}] }
174
+ assert_valid!
175
+ end
176
+
177
+ specify 'pass; empty array' do
178
+ echo headers, { 'widgets' => [] }
179
+ assert_valid!
180
+ end
181
+
182
+ specify 'fail; no such anchor' do
183
+ expect_invalid do
184
+ echo headers, { 'Widgets' => [{'name' => 'foo'}] }
185
+ end
186
+ end
187
+
188
+ specify 'fail; invalid entries' do
189
+ expect_invalid do
190
+ echo headers, { 'widgets' => [{'bogus' => 'object'}] }
191
+ end
192
+ end
193
+ end
194
+
195
+ context 'multiple schemas' do
196
+ before do
197
+ headers['Link'] = [described_by(schema_uri('response'), '#'),
198
+ described_by(schema_uri('widget'), '#/widgets', :collection),
199
+ described_by(schema_uri('auction'), '#/auction')].join(", ")
200
+ end
201
+
202
+ let(:body) do
203
+ { 'links' => [{'rel' => 'self'}],
204
+ 'widgets' => [{'name' => 'foo'}],
205
+ 'auction' => {'bids' => 10, 'price' => 100}
206
+ }
207
+ end
208
+
209
+ specify 'pass all' do
210
+ echo headers, body
211
+ assert_valid!
212
+ end
213
+
214
+ specify 'fail all' do
215
+ expect_invalid do
216
+ echo headers, {}
217
+ end
218
+
219
+ expect_invalid do
220
+ echo headers, {'links' => 'oops', 'widgets' => ['so', 'wrong'], 'auction' => nil}
221
+ end
222
+ end
223
+
224
+ specify 'fail one' do
225
+ body['links'] = []
226
+ expect_invalid do
227
+ echo headers, body
228
+ end
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rack::Schema do
4
+ context 'default handler' do
5
+ def app
6
+ Rack::Builder.new do
7
+ use Rack::Schema
8
+ run SpecHelpers::EchoApp
9
+ end
10
+ end
11
+
12
+ it 'raises Rack::Schema::ValidationError if there were errors' do
13
+ headers['Link'] = described_by schema_uri('widget')
14
+ expect {
15
+ echo headers, {}
16
+ }.to raise_error(Rack::Schema::ValidationError)
17
+ end
18
+
19
+ it 'otherwise does not alter the response' do
20
+ headers['Link'] = described_by schema_uri('widget')
21
+ echo headers, {'name' => 'foo'}
22
+
23
+ expect(last_response.headers).to have_key('Link')
24
+ expect(last_response.body).to eql(MultiJson.dump({'name' => 'foo'}))
25
+ end
26
+ end
27
+
28
+ context 'swallow_links: true' do
29
+ def app
30
+ Rack::Builder.new do
31
+ use Rack::Schema, swallow_links: true
32
+ run SpecHelpers::EchoApp
33
+ end
34
+ end
35
+
36
+ it 'removes only rel=describedby Links' do
37
+ headers['Link'] = [described_by(schema_uri('widget')),
38
+ '<http://another/link>; rel="next"'].join(", ")
39
+ echo headers, { 'name' => 'foo' }
40
+
41
+ expect(last_response.headers).to have_key('Link')
42
+ expect(last_response.headers['Link']).to eql('<http://another/link>; rel="next"')
43
+ end
44
+
45
+ it 'removes the Link header if empty' do
46
+ headers['Link'] = described_by schema_uri('widget')
47
+ echo headers, { 'name' => 'foo' }
48
+ expect(last_response.headers).not_to have_key('Link')
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-03/schema#",
3
+ "title": "An auction to sell a widget",
4
+ "additionalProperties": false,
5
+ "properties": {
6
+ "bids": { "type": "integer" },
7
+ "price": { "type": "integer", "required": true }
8
+ }
9
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-03/schema#",
3
+ "title": "Any ol' object",
4
+ "type": "object"
5
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-03/schema#",
3
+ "title": "Common schema for service JSON responses",
4
+
5
+ "properties": {
6
+ "links": {
7
+ "type": "array",
8
+ "items": {
9
+ "properties": { "rel": { "type": "string" } }
10
+ },
11
+ "minItems": 1
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-03/schema#",
3
+ "title": "A widget",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "properties": {
7
+ "name": { "type": "string", "required": true }
8
+ }
9
+ }
@@ -0,0 +1,59 @@
1
+ require 'bundler/setup'
2
+
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+
6
+ require 'rspec'
7
+ require 'rack/test'
8
+ require 'rack/schema'
9
+ require 'pry'
10
+
11
+ # `json' needs to be here because simplecov assumes that if
12
+ # ::JSON is defined, it means we have ruby's JSON loaded; otherwise,
13
+ # they use MultiJson. silly.
14
+ require "oj"
15
+ require "json"
16
+
17
+ module SpecHelpers
18
+ def echo(headers, body, status = 200)
19
+ env = {
20
+ 'echo.body' => MultiJson.dump(body),
21
+ 'echo.headers' => headers,
22
+ 'echo.status' => status
23
+ }
24
+ get '/', {}, env
25
+ end
26
+
27
+ def headers
28
+ @headers ||= {
29
+ 'Content-Type' => 'application/json'
30
+ }
31
+ end
32
+
33
+ def schema_uri(name)
34
+ "file://#{schema_file(name)}"
35
+ end
36
+
37
+ def schema_file(name)
38
+ File.expand_path("../schemas/#{name}.json", __FILE__)
39
+ end
40
+
41
+ def described_by(uri, anchor = nil, collection = nil)
42
+ header = "<#{uri}>; rel=\"describedby\""
43
+ header.concat "; anchor=\"#{anchor}\"" if anchor
44
+ header.concat "; collection=\"collection\"" if collection
45
+ header
46
+ end
47
+
48
+ module EchoApp
49
+ def self.call(env)
50
+ body = [env['echo.body']] # body should respond to :each
51
+ [env['echo.status'], env['echo.headers'], body]
52
+ end
53
+ end
54
+ end
55
+
56
+ RSpec.configure do |config|
57
+ config.include Rack::Test::Methods
58
+ config.include SpecHelpers
59
+ end
metadata ADDED
@@ -0,0 +1,246 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-schema
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kyle Hargraves
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: multi_json
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: json-schema
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: link_header
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: bundler
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '1.3'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '1.3'
94
+ - !ruby/object:Gem::Dependency
95
+ name: oj
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rake
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: rack-test
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: rspec
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: simplecov
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: pry
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ description: Validate rack responses against schema named in the Link header
191
+ email:
192
+ - pd@krh.me
193
+ executables: []
194
+ extensions: []
195
+ extra_rdoc_files: []
196
+ files:
197
+ - .gitignore
198
+ - Gemfile
199
+ - LICENSE.txt
200
+ - README.md
201
+ - Rakefile
202
+ - lib/rack/schema.rb
203
+ - lib/rack/schema/version.rb
204
+ - rack-schema.gemspec
205
+ - spec/link_header_spec.rb
206
+ - spec/rack_schema_spec.rb
207
+ - spec/schemas/auction.json
208
+ - spec/schemas/object.json
209
+ - spec/schemas/response.json
210
+ - spec/schemas/widget.json
211
+ - spec/spec_helper.rb
212
+ homepage: http://github.com/pd/rack-schema
213
+ licenses:
214
+ - MIT
215
+ post_install_message:
216
+ rdoc_options: []
217
+ require_paths:
218
+ - lib
219
+ required_ruby_version: !ruby/object:Gem::Requirement
220
+ none: false
221
+ requirements:
222
+ - - ! '>='
223
+ - !ruby/object:Gem::Version
224
+ version: '0'
225
+ required_rubygems_version: !ruby/object:Gem::Requirement
226
+ none: false
227
+ requirements:
228
+ - - ! '>='
229
+ - !ruby/object:Gem::Version
230
+ version: '0'
231
+ requirements: []
232
+ rubyforge_project:
233
+ rubygems_version: 1.8.23
234
+ signing_key:
235
+ specification_version: 3
236
+ summary: Allows you to strictly validate each of your application's API responses
237
+ against a declared JSON schema.
238
+ test_files:
239
+ - spec/link_header_spec.rb
240
+ - spec/rack_schema_spec.rb
241
+ - spec/schemas/auction.json
242
+ - spec/schemas/object.json
243
+ - spec/schemas/response.json
244
+ - spec/schemas/widget.json
245
+ - spec/spec_helper.rb
246
+ has_rdoc: