rack-spec 0.1.3 → 0.1.4
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/CHANGELOG.md +4 -0
- data/README.md +87 -19
- data/lib/rack/spec.rb +2 -1
- data/lib/rack/spec/{base_validator.rb → base_request_handler.rb} +12 -1
- data/lib/rack/spec/error.rb +1 -1
- data/lib/rack/spec/mock.rb +93 -0
- data/lib/rack/spec/request_validation.rb +1 -1
- data/lib/rack/spec/response_validation.rb +13 -21
- data/lib/rack/spec/version.rb +1 -1
- data/spec/fixtures/schema.json +35 -0
- data/spec/rack/spec/mock_spec.rb +122 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87132891a38319e6296f71af99cfb46bb54d9041
|
4
|
+
data.tar.gz: 2a9700230c057c9894140ffad108f90fb6a314b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7ce5b2e4e7385afbcfc7cd0b3cb2b83a5b8492d4e91d43d0ec4cf79714aca7c3aaffad2e239e22bf2bba68f626182b1510107c7c063cc29144c0e91238a3fb1
|
7
|
+
data.tar.gz: 489751d27fb8458b88939c3b71e46ebcd89d48fbd7212f39bfc21de8e8e5f41b391020248f5a781e251a2fd847b849ea28a682b8af063ffe93329a00ebde2e06
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -9,49 +9,117 @@ schema = JSON.parse(str)
|
|
9
9
|
use Rack::Spec::ErrorHandler
|
10
10
|
use Rack::Spec::RequestValidation, schema: schema
|
11
11
|
use Rack::Spec::ResponseValidation, schema: schema if ENV["RACK_ENV"] == "test"
|
12
|
+
use Rack::Spec::Mock, schema: schema if ENV["RACK_ENV"] == "mock"
|
12
13
|
```
|
13
14
|
|
14
|
-
###
|
15
|
+
### Rack::Spec::RequestValidation
|
16
|
+
Validates request and raises errors below.
|
17
|
+
|
18
|
+
* Rack::Spec::RequestValidation::InvalidContentType
|
19
|
+
* Rack::Spec::RequestValidation::InvalidJson
|
20
|
+
* Rack::Spec::RequestValidation::InvalidParameter
|
21
|
+
* Rack::Spec::RequestValidation::LinkNotFound
|
22
|
+
|
15
23
|
```sh
|
16
|
-
$ curl http://localhost:9292/
|
17
|
-
{
|
24
|
+
$ curl http://localhost:9292/users
|
25
|
+
{
|
26
|
+
"id": "link_not_found",
|
27
|
+
"message": "Not found"
|
28
|
+
}
|
18
29
|
|
19
30
|
$ curl http://localhost:9292/apps -H "Content-Type: application/json" -d "invalid-json"
|
20
|
-
{
|
31
|
+
{
|
32
|
+
"id": "invalid_json",
|
33
|
+
"message": "Request body wasn't valid JSON"
|
34
|
+
}
|
21
35
|
|
22
36
|
$ curl http://localhost:9292/apps -H "Content-Type: text/plain" -d "{}"
|
23
|
-
{
|
37
|
+
{
|
38
|
+
"id": "invalid_content_type",
|
39
|
+
"message": "Invalid content type"
|
40
|
+
}
|
24
41
|
|
25
42
|
$ curl http://localhost:9292/apps -H "Content-Type: application/json" -d '{"name":"x"}'
|
26
|
-
{
|
43
|
+
{
|
44
|
+
"id": "invalid_parameter",
|
45
|
+
"message": "Invalid request.\n#/name: failed schema #/definitions/app/links/0/schema/properties/name: Expected string to match pattern \"/^[a-z][a-z0-9-]{3,50}$/\", value was: x."
|
46
|
+
}
|
27
47
|
```
|
28
48
|
|
29
|
-
### Rack::Spec::
|
30
|
-
Validates request and raises
|
31
|
-
|
32
|
-
* Rack::Spec::RequestValidation::InvalidContentType
|
33
|
-
* Rack::Spec::RequestValidation::InvalidJson
|
34
|
-
* Rack::Spec::RequestValidation::InvalidParameter
|
35
|
-
* Rack::Spec::RequestValidation::LinkNotFound
|
36
|
-
|
37
|
-
### Rack::Spec::RequestValidation
|
38
|
-
Validates response and raises following errors:
|
49
|
+
### Rack::Spec::ResponseValidation
|
50
|
+
Validates request and raises errors below.
|
39
51
|
|
40
52
|
* Rack::Spec::RequestValidation::InvalidResponseContentType
|
41
53
|
* Rack::Spec::RequestValidation::InvalidResponseType
|
42
54
|
|
55
|
+
```sh
|
56
|
+
$ curl http://localhost:9292/apps
|
57
|
+
{
|
58
|
+
"id": "invalid_response_content_type",
|
59
|
+
"message": "Response Content-Type wasn't for JSON"
|
60
|
+
}
|
61
|
+
|
62
|
+
$ curl http://localhost:9292/apps
|
63
|
+
{
|
64
|
+
"id": "invalid_response_type",
|
65
|
+
"message": "#: failed schema #/definitions/app: Expected data to be of type \"object\"; value was: [\"message\", \"dummy\"]."
|
66
|
+
}
|
67
|
+
```
|
68
|
+
|
69
|
+
### Rack::Spec::Mock
|
70
|
+
Generates dummy response by using example property in JSON schema.
|
71
|
+
|
72
|
+
```sh
|
73
|
+
$ curl http://localhost:9292/apps/1
|
74
|
+
[
|
75
|
+
{
|
76
|
+
"id": "01234567-89ab-cdef-0123-456789abcdef",
|
77
|
+
"name": "example"
|
78
|
+
}
|
79
|
+
]
|
80
|
+
|
81
|
+
$ curl http://localhost:9292/apps/01234567-89ab-cdef-0123-456789abcdef
|
82
|
+
{
|
83
|
+
"id": "01234567-89ab-cdef-0123-456789abcdef",
|
84
|
+
"name": "example"
|
85
|
+
}
|
86
|
+
|
87
|
+
$ curl http://localhost:9292/apps/1 -d '{"name":"example"}'
|
88
|
+
{
|
89
|
+
"id": "01234567-89ab-cdef-0123-456789abcdef",
|
90
|
+
"name": "example"
|
91
|
+
}
|
92
|
+
|
93
|
+
$ curl http://localhost:9292/recipes
|
94
|
+
{
|
95
|
+
"id": "example_not_found",
|
96
|
+
"message": "No example found for #/definitions/recipe/id"
|
97
|
+
}
|
98
|
+
```
|
99
|
+
|
43
100
|
### Rack::Spec::ErrorHandler
|
44
101
|
Returns appropriate error response including following properties when RequestValidation raises error.
|
45
102
|
|
46
|
-
*
|
47
|
-
*
|
103
|
+
* message: Human readable message
|
104
|
+
* id: Error type identifier listed below
|
105
|
+
* invalid_content_type
|
106
|
+
* invalid_json
|
107
|
+
* invalid_parameter
|
108
|
+
* invalid_response_content_type
|
109
|
+
* invalid_response_type
|
110
|
+
* link_not_found
|
111
|
+
|
112
|
+
Here is a tree of all possible errors defined in Rack::Spec.
|
48
113
|
|
49
|
-
### Errors
|
50
114
|
```
|
51
115
|
StandardError
|
52
116
|
|
|
53
117
|
Rack::Spec::Error
|
54
118
|
|
|
119
|
+
|--Rack::Spec::Mock::Error
|
120
|
+
| |
|
121
|
+
| `--Rack::Spec::RequestValidation::ExampleNotFound
|
122
|
+
|
|
55
123
|
|--Rack::Spec::RequestValidation::Error
|
56
124
|
| |
|
57
125
|
| |--Rack::Spec::RequestValidation::InvalidContentType
|
data/lib/rack/spec.rb
CHANGED
@@ -2,9 +2,10 @@ require "json"
|
|
2
2
|
require "json_schema"
|
3
3
|
require "multi_json"
|
4
4
|
|
5
|
-
require "rack/spec/
|
5
|
+
require "rack/spec/base_request_handler"
|
6
6
|
require "rack/spec/error"
|
7
7
|
require "rack/spec/error_handler"
|
8
|
+
require "rack/spec/mock"
|
8
9
|
require "rack/spec/request_validation"
|
9
10
|
require "rack/spec/response_validation"
|
10
11
|
require "rack/spec/schema"
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Rack
|
2
2
|
module Spec
|
3
|
-
class
|
3
|
+
# Base class for providing some utility methods to handle Rack env and JSON Schema
|
4
|
+
class BaseRequestHandler
|
4
5
|
# Utility wrapper method
|
5
6
|
def self.call(**args)
|
6
7
|
new(**args).call
|
@@ -48,6 +49,16 @@ module Rack
|
|
48
49
|
def has_link_for_current_action?
|
49
50
|
!!link
|
50
51
|
end
|
52
|
+
|
53
|
+
# @return [JsonSchema::Schema] Schema for current link, specified by targetSchema or parent schema
|
54
|
+
def schema_for_current_link
|
55
|
+
link.target_schema || link.parent
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [true, false] True if response is intended to be list data
|
59
|
+
def has_list_data?
|
60
|
+
link.rel == "instances" && !link.target_schema
|
61
|
+
end
|
51
62
|
end
|
52
63
|
end
|
53
64
|
end
|
data/lib/rack/spec/error.rb
CHANGED
@@ -0,0 +1,93 @@
|
|
1
|
+
module Rack
|
2
|
+
module Spec
|
3
|
+
class Mock
|
4
|
+
# Behaves as a rack-middleware
|
5
|
+
# @param app [Object] Rack application
|
6
|
+
# @param schema [Hash] Schema object written in JSON schema format
|
7
|
+
# @raise [JsonSchema::SchemaError]
|
8
|
+
def initialize(app, schema: nil)
|
9
|
+
@app = app
|
10
|
+
@schema = Schema.new(schema)
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param env [Hash] Rack env
|
14
|
+
def call(env)
|
15
|
+
RequestHandler.call(app: @app, env: env, schema: @schema)
|
16
|
+
end
|
17
|
+
|
18
|
+
class RequestHandler < BaseRequestHandler
|
19
|
+
# @param app [Object] Rack application
|
20
|
+
def initialize(app: nil, **args)
|
21
|
+
@app = app
|
22
|
+
super(**args)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns dummy response if JSON schema is defined for the current link
|
26
|
+
# @return [Array] Rack response
|
27
|
+
def call
|
28
|
+
if has_link_for_current_action?
|
29
|
+
dummy_response
|
30
|
+
else
|
31
|
+
@app.call(@env)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def dummy_response
|
38
|
+
[dummy_status, dummy_headers, [dummy_body]]
|
39
|
+
end
|
40
|
+
|
41
|
+
def dummy_status
|
42
|
+
method == "POST" ? 201 : 200
|
43
|
+
end
|
44
|
+
|
45
|
+
def dummy_headers
|
46
|
+
{ "Content-Type" => "application/json" }
|
47
|
+
end
|
48
|
+
|
49
|
+
def dummy_body
|
50
|
+
document = ResponseGenerator.call(schema_for_current_link)
|
51
|
+
document = [document] if has_list_data?
|
52
|
+
MultiJson.encode(document, pretty: true) + "\n"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class ResponseGenerator
|
57
|
+
# Generates example response Hash from given schema
|
58
|
+
# @return [Hash]
|
59
|
+
# @example
|
60
|
+
# Rack::Spec::Mock::ResponseGenerator(schema) #=> { "id" => 1, "name" => "example" }
|
61
|
+
def self.call(schema)
|
62
|
+
schema.properties.inject({}) do |result, (key, value)|
|
63
|
+
result.merge(
|
64
|
+
key => case
|
65
|
+
when !value.properties.empty?
|
66
|
+
call(value)
|
67
|
+
when !value.data["example"].nil?
|
68
|
+
value.data["example"]
|
69
|
+
when value.type.include?("null")
|
70
|
+
nil
|
71
|
+
else
|
72
|
+
raise ExampleNotFound, "No example found for #{schema.pointer}/#{key}"
|
73
|
+
end
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Error < Error
|
80
|
+
end
|
81
|
+
|
82
|
+
class ExampleNotFound < Error
|
83
|
+
def id
|
84
|
+
"example_not_found"
|
85
|
+
end
|
86
|
+
|
87
|
+
def status
|
88
|
+
500
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -18,7 +18,7 @@ module Rack
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
class Validator <
|
21
|
+
class Validator < BaseRequestHandler
|
22
22
|
# @param env [Hash] Rack env
|
23
23
|
# @param response [Array] Rack response
|
24
24
|
# @param schema [JsonSchema::Schema] Schema object
|
@@ -28,14 +28,16 @@ module Rack
|
|
28
28
|
@schema = schema
|
29
29
|
end
|
30
30
|
|
31
|
-
# Raises an error if any error detected
|
31
|
+
# Raises an error if any error detected, skipping validation for non-defined link
|
32
32
|
# @raise [Rack::Spec::ResponseValidation::InvalidResponse]
|
33
33
|
def call
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
if has_link_for_current_action?
|
35
|
+
case
|
36
|
+
when !has_json_content_type?
|
37
|
+
raise InvalidResponseContentType
|
38
|
+
when !valid?
|
39
|
+
raise InvalidResponseType, validator.errors
|
40
|
+
end
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
@@ -46,7 +48,7 @@ module Rack
|
|
46
48
|
|
47
49
|
# @return [true, false] True if given data is valid to the JSON schema
|
48
50
|
def valid?
|
49
|
-
|
51
|
+
validator.validate(example_item)
|
50
52
|
end
|
51
53
|
|
52
54
|
# @return [Hash] Choose an item from response data, to be validated
|
@@ -58,11 +60,6 @@ module Rack
|
|
58
60
|
end
|
59
61
|
end
|
60
62
|
|
61
|
-
# @return [true, false] True if response is intended to be list data
|
62
|
-
def has_list_data?
|
63
|
-
link.rel == "instances" && !link.target_schema
|
64
|
-
end
|
65
|
-
|
66
63
|
# @return [Array, Hash] Response body data, decoded from JSON
|
67
64
|
def data
|
68
65
|
MultiJson.decode(body)
|
@@ -74,11 +71,6 @@ module Rack
|
|
74
71
|
@validator ||= JsonSchema::Validator.new(schema_for_current_link)
|
75
72
|
end
|
76
73
|
|
77
|
-
# @return [JsonSchema::Schema] Schema for current link, specified by targetSchema or parent schema
|
78
|
-
def schema_for_current_link
|
79
|
-
link.target_schema || link.parent
|
80
|
-
end
|
81
|
-
|
82
74
|
# @return [Hash] Response headers
|
83
75
|
def headers
|
84
76
|
@response[1]
|
@@ -86,9 +78,9 @@ module Rack
|
|
86
78
|
|
87
79
|
# @return [String] Response body
|
88
80
|
def body
|
89
|
-
|
90
|
-
|
91
|
-
|
81
|
+
result = ""
|
82
|
+
@response[2].each {|str| result << str }
|
83
|
+
result
|
92
84
|
end
|
93
85
|
end
|
94
86
|
|
data/lib/rack/spec/version.rb
CHANGED
data/spec/fixtures/schema.json
CHANGED
@@ -95,11 +95,46 @@
|
|
95
95
|
"$ref": "#/definitions/app/definitions/name"
|
96
96
|
}
|
97
97
|
}
|
98
|
+
},
|
99
|
+
"recipe": {
|
100
|
+
"$schema": "http://json-schema.org/draft-04/hyper-schema",
|
101
|
+
"description": "cooking Recipe",
|
102
|
+
"title": "Recipe",
|
103
|
+
"type": [
|
104
|
+
"object"
|
105
|
+
],
|
106
|
+
"definitions": {
|
107
|
+
"id": {
|
108
|
+
"description": "unique identifier of recipe",
|
109
|
+
"format": "uuid",
|
110
|
+
"readOnly": true,
|
111
|
+
"type": [
|
112
|
+
"string"
|
113
|
+
]
|
114
|
+
}
|
115
|
+
},
|
116
|
+
"links": [
|
117
|
+
{
|
118
|
+
"description": "List recipes",
|
119
|
+
"href": "/recipes",
|
120
|
+
"method": "GET",
|
121
|
+
"rel": "instances",
|
122
|
+
"title": "list"
|
123
|
+
}
|
124
|
+
],
|
125
|
+
"properties": {
|
126
|
+
"id": {
|
127
|
+
"$ref": "#/definitions/recipe/definitions/id"
|
128
|
+
}
|
129
|
+
}
|
98
130
|
}
|
99
131
|
},
|
100
132
|
"properties": {
|
101
133
|
"app": {
|
102
134
|
"$ref": "#/definitions/app"
|
135
|
+
},
|
136
|
+
"recipe": {
|
137
|
+
"$ref": "#/definitions/recipe"
|
103
138
|
}
|
104
139
|
},
|
105
140
|
"type": [
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Rack::Spec::Mock do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
let(:app) do
|
7
|
+
local_schema = schema
|
8
|
+
Rack::Builder.app do
|
9
|
+
use Rack::Spec::ErrorHandler
|
10
|
+
use Rack::Spec::Mock, schema: local_schema
|
11
|
+
run ->(env) do
|
12
|
+
[
|
13
|
+
200,
|
14
|
+
{},
|
15
|
+
["dummy"],
|
16
|
+
]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:schema) do
|
22
|
+
str = File.read(schema_path)
|
23
|
+
JSON.parse(str)
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:schema_path) do
|
27
|
+
File.expand_path("../../../fixtures/schema.json", __FILE__)
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:response) do
|
31
|
+
last_response
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:env) do
|
35
|
+
{}
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:params) do
|
39
|
+
{}
|
40
|
+
end
|
41
|
+
|
42
|
+
subject do
|
43
|
+
send(verb, path, params, env)
|
44
|
+
response.status
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#call" do
|
48
|
+
context "with list API" do
|
49
|
+
let(:verb) do
|
50
|
+
:get
|
51
|
+
end
|
52
|
+
|
53
|
+
let(:path) do
|
54
|
+
"/apps"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns Array dummy response" do
|
58
|
+
should == 200
|
59
|
+
response.body.should be_json_as(
|
60
|
+
[
|
61
|
+
{
|
62
|
+
id: schema["definitions"]["app"]["definitions"]["id"]["example"],
|
63
|
+
name: schema["definitions"]["app"]["definitions"]["name"]["example"],
|
64
|
+
}
|
65
|
+
]
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "with info API" do
|
71
|
+
let(:verb) do
|
72
|
+
:get
|
73
|
+
end
|
74
|
+
|
75
|
+
let(:path) do
|
76
|
+
"/apps/1"
|
77
|
+
end
|
78
|
+
|
79
|
+
it "returns dummy response" do
|
80
|
+
should == 200
|
81
|
+
response.body.should be_json_as(
|
82
|
+
{
|
83
|
+
id: schema["definitions"]["app"]["definitions"]["id"]["example"],
|
84
|
+
name: schema["definitions"]["app"]["definitions"]["name"]["example"],
|
85
|
+
}
|
86
|
+
)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "with POST API" do
|
91
|
+
let(:verb) do
|
92
|
+
:post
|
93
|
+
end
|
94
|
+
|
95
|
+
let(:path) do
|
96
|
+
"/apps"
|
97
|
+
end
|
98
|
+
|
99
|
+
it "returns dummy response with 201" do
|
100
|
+
should == 201
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "without example" do
|
105
|
+
let(:verb) do
|
106
|
+
:get
|
107
|
+
end
|
108
|
+
|
109
|
+
let(:path) do
|
110
|
+
"/recipes"
|
111
|
+
end
|
112
|
+
|
113
|
+
it "returns example_not_found error" do
|
114
|
+
should == 500
|
115
|
+
response.body.should be_json_as(
|
116
|
+
id: "example_not_found",
|
117
|
+
message: "No example found for #/definitions/recipe/id",
|
118
|
+
)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-spec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryo Nakamura
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-06-
|
11
|
+
date: 2014-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json_schema
|
@@ -151,15 +151,17 @@ files:
|
|
151
151
|
- Rakefile
|
152
152
|
- lib/rack-spec.rb
|
153
153
|
- lib/rack/spec.rb
|
154
|
-
- lib/rack/spec/
|
154
|
+
- lib/rack/spec/base_request_handler.rb
|
155
155
|
- lib/rack/spec/error.rb
|
156
156
|
- lib/rack/spec/error_handler.rb
|
157
|
+
- lib/rack/spec/mock.rb
|
157
158
|
- lib/rack/spec/request_validation.rb
|
158
159
|
- lib/rack/spec/response_validation.rb
|
159
160
|
- lib/rack/spec/schema.rb
|
160
161
|
- lib/rack/spec/version.rb
|
161
162
|
- rack-spec.gemspec
|
162
163
|
- spec/fixtures/schema.json
|
164
|
+
- spec/rack/spec/mock_spec.rb
|
163
165
|
- spec/rack/spec_spec.rb
|
164
166
|
- spec/spec_helper.rb
|
165
167
|
homepage: https://github.com/r7kamura/rack-spec
|
@@ -188,6 +190,7 @@ specification_version: 4
|
|
188
190
|
summary: JSON Schema based Rack middlewares
|
189
191
|
test_files:
|
190
192
|
- spec/fixtures/schema.json
|
193
|
+
- spec/rack/spec/mock_spec.rb
|
191
194
|
- spec/rack/spec_spec.rb
|
192
195
|
- spec/spec_helper.rb
|
193
196
|
has_rdoc:
|