grape-apiary 0.0.2 → 0.0.3
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/.rspec +2 -0
- data/.travis.yml +1 -1
- data/Gemfile +14 -0
- data/Guardfile +12 -0
- data/README.md +21 -7
- data/Rakefile +5 -1
- data/grape-apiary.gemspec +3 -0
- data/lib/grape-apiary.rb +12 -1
- data/lib/grape-apiary/blueprint.rb +59 -3
- data/lib/grape-apiary/config.rb +30 -0
- data/lib/grape-apiary/parameter.rb +10 -7
- data/lib/grape-apiary/resource.rb +18 -7
- data/lib/grape-apiary/sample_generator.rb +28 -2
- data/lib/grape-apiary/templates/blueprint.md.erb +3 -0
- data/lib/grape-apiary/version.rb +1 -1
- data/spec/grape-apiary/blueprint_spec.rb +47 -17
- data/spec/grape-apiary/config_spec.rb +34 -0
- data/spec/grape-apiary/resource_spec.rb +21 -0
- data/spec/grape-apiary/route_spec.rb +1 -1
- data/spec/grape-apiary/sample_generator_spec.rb +38 -0
- data/spec/spec_helper.rb +5 -2
- data/spec/support/config_context.rb +2 -16
- data/spec/support/sample_api.rb +8 -2
- metadata +50 -5
- data/lib/grape-apiary/routes.rb +0 -57
- data/spec/grape-apiary/routes_spec.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b15d2669d75c2f5461216dc111cb0680cf4ef03
|
4
|
+
data.tar.gz: 7fae72c31f7d7b15050f170af438bae065fd62e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b746ab57f5703d7f0791a8c35bf257c9d2e87c8d9a8f2b5db4d347793a065e59b277a0bc1f16325dded4c03f80d8488afbab340bfbc0859150029b15b9643c07
|
7
|
+
data.tar.gz: 33f12c650f2518b7c5fb0570278bb70f4e684d5f524091862e73a3554884823d4b9763ad733d92b36d3f9e6ba85c415137aeae605cd38e0b344c387842940a8c
|
data/.rspec
ADDED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -2,3 +2,17 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in grape-apiary.gemspec
|
4
4
|
gemspec
|
5
|
+
|
6
|
+
gem 'grape', github: 'intridea/grape'
|
7
|
+
|
8
|
+
group :development, :test do
|
9
|
+
gem 'coveralls', '~> 0.7'
|
10
|
+
gem 'rspec', '~> 2.14'
|
11
|
+
gem 'bundler', '~> 1.5'
|
12
|
+
gem 'rake', '~> 10.0'
|
13
|
+
gem 'rubocop', '~> 0.18'
|
14
|
+
gem 'pry', '~> 0.9'
|
15
|
+
gem 'guard', '~> 2.4'
|
16
|
+
gem 'guard-rspec', '~> 4.2'
|
17
|
+
gem 'guard-bundler', '~> 2.0'
|
18
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# More info at https://github.com/guard/guard#readme
|
2
|
+
|
3
|
+
guard 'rspec', :version => 2 do
|
4
|
+
watch(%r{^spec/.+_spec\.rb$})
|
5
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
6
|
+
watch('spec/spec_helper.rb') { "spec/" }
|
7
|
+
end
|
8
|
+
|
9
|
+
guard 'bundler' do
|
10
|
+
watch('Gemfile')
|
11
|
+
watch(/^.+\.gemspec/)
|
12
|
+
end
|
data/README.md
CHANGED
@@ -6,12 +6,17 @@
|
|
6
6
|
[](https://gemnasium.com/connexio-labs/grape-apiary)
|
7
7
|
[](http://badge.fury.io/rb/grape-apiary)
|
8
8
|
|
9
|
-
Auto generates an [Apiary Blueprint](http://apiary.io) from the docuementation that is created by your Grape API.
|
9
|
+
Auto generates an [Apiary Blueprint](http://apiary.io) from the docuementation that is created by your [Grape](https://github.com/intridea/grape) API.
|
10
|
+
|
11
|
+
### NOTE
|
12
|
+
|
13
|
+
This is an early implementation that makes some assumptions about your API (follows a standard REST pattern) that works with our implementation of Grape API's. There is a new an [unreleased feature in Grape](https://github.com/intridea/grape#parameter-documentation) that allows for appending additional documentation. This project is dependent on this feature in order to create example JSON requests and responses.
|
10
14
|
|
11
15
|
## Installation
|
12
16
|
|
13
17
|
Add this line to your application's Gemfile:
|
14
18
|
|
19
|
+
gem 'grape', github: 'intridea/grape' # see note above
|
15
20
|
gem 'grape-apiary'
|
16
21
|
|
17
22
|
And then execute:
|
@@ -24,9 +29,11 @@ Or install it yourself as:
|
|
24
29
|
|
25
30
|
## Usage
|
26
31
|
|
32
|
+
Add some metadata about your API and then execute the `generate` method on the `GrapeApiary::Blueprint` class.
|
33
|
+
|
27
34
|
### Configuration
|
28
35
|
|
29
|
-
Configure details about your api in an initializers or similar
|
36
|
+
Configure details about your api in an initializers or similar:
|
30
37
|
|
31
38
|
```ruby
|
32
39
|
GrapeApiary.config do |config|
|
@@ -36,21 +43,22 @@ GrapeApiary.config do |config|
|
|
36
43
|
config.name = 'Awesome API'
|
37
44
|
# a description for your api
|
38
45
|
config.description = 'The awesome description'
|
46
|
+
# the type to use for generated sample id's (`integer` or `uuid`)
|
47
|
+
config.example_id_type = :uuid
|
39
48
|
# resources you do not want documented
|
40
49
|
config.resource_exclusion = [:admin, :swagger_doc]
|
41
50
|
end
|
42
51
|
|
43
|
-
# headers you want documented
|
52
|
+
# request headers you want documented
|
44
53
|
GrapeApiary.config.request_headers = [
|
45
54
|
{ 'Accept-Charset' => 'utf-8' },
|
46
|
-
{ 'Connection' => 'keep-alive' }
|
47
|
-
{ 'Content-Type' => 'application/json' }
|
55
|
+
{ 'Connection' => 'keep-alive' }
|
48
56
|
]
|
49
57
|
|
58
|
+
# response headers you want documented
|
50
59
|
GrapeApiary.config.response_headers = [
|
51
60
|
{ 'Content-Length' => '21685' },
|
52
|
-
{ 'Connection' => 'keep-alive' }
|
53
|
-
{ 'Content-Type' => 'application/json' }
|
61
|
+
{ 'Connection' => 'keep-alive' }
|
54
62
|
]
|
55
63
|
```
|
56
64
|
|
@@ -63,6 +71,12 @@ GrapeApiary::Blueprint.new(AwesomeAPI).generate
|
|
63
71
|
|
64
72
|
## TODO
|
65
73
|
|
74
|
+
* Add a rake task to simplify generation
|
75
|
+
* Add support for listing all of a resources attributes at the resource level as a markdown table
|
76
|
+
* Handle ever changing sample id's (don't want git diff's after every generation)
|
77
|
+
* Add option to change or remove the sample id field (eg. `_id` vs `id`)
|
78
|
+
* What if someone does not use JSON?!?
|
79
|
+
* Creat sample response for list endpoints (array)
|
66
80
|
|
67
81
|
## Contributing
|
68
82
|
|
data/Rakefile
CHANGED
data/grape-apiary.gemspec
CHANGED
@@ -26,4 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency 'rake', '~> 10.0'
|
27
27
|
spec.add_development_dependency 'rubocop', '~> 0.18'
|
28
28
|
spec.add_development_dependency 'pry', '~> 0.9'
|
29
|
+
spec.add_development_dependency 'guard', '~> 2.4'
|
30
|
+
spec.add_development_dependency 'guard-rspec', '~> 4.2'
|
31
|
+
spec.add_development_dependency 'guard-bundler', '~> 2.0'
|
29
32
|
end
|
data/lib/grape-apiary.rb
CHANGED
@@ -5,7 +5,6 @@ require 'grape-apiary/parameter'
|
|
5
5
|
require 'grape-apiary/sample_generator'
|
6
6
|
require 'grape-apiary/route'
|
7
7
|
require 'grape-apiary/resource'
|
8
|
-
require 'grape-apiary/routes'
|
9
8
|
require 'grape-apiary/blueprint'
|
10
9
|
|
11
10
|
module GrapeApiary
|
@@ -15,3 +14,15 @@ module GrapeApiary
|
|
15
14
|
block_given? ? yield(Config) : Config
|
16
15
|
end
|
17
16
|
end
|
17
|
+
|
18
|
+
class UnsupportedIDType < StandardError
|
19
|
+
def message
|
20
|
+
'Unsupported id type, supported types are [integer, uuid, bson]'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class BSONNotDefinied < StandardError
|
25
|
+
def message
|
26
|
+
'BSON type id requested but bson library is not present'
|
27
|
+
end
|
28
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module GrapeApiary
|
2
2
|
class Blueprint
|
3
|
-
attr_reader :api_class, :template
|
3
|
+
attr_reader :api_class, :template
|
4
|
+
|
5
|
+
delegate(*GrapeApiary::Config::SETTINGS, to: 'GrapeApiary::Config')
|
4
6
|
|
5
7
|
def initialize(api_class)
|
6
8
|
@api_class = api_class
|
7
|
-
@template = File.read('./lib/grape-apiary/templates/blueprint.md.erb')
|
8
|
-
@binding = Routes.new(api_class).routes_binding
|
9
9
|
end
|
10
10
|
|
11
11
|
def generate
|
@@ -13,6 +13,62 @@ module GrapeApiary
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def write
|
16
|
+
fail 'Not yet supported'
|
17
|
+
end
|
18
|
+
|
19
|
+
def template
|
20
|
+
@template ||= begin
|
21
|
+
directory = File.dirname(File.expand_path(__FILE__))
|
22
|
+
path = File.join(directory, './templates/blueprint.md.erb')
|
23
|
+
|
24
|
+
File.read(path)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def routes
|
29
|
+
@routes ||= api_class.routes.map do |route|
|
30
|
+
GrapeApiary::Route.new(route)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def resources
|
35
|
+
@resources ||= begin
|
36
|
+
grouped_routes = routes.group_by(&:route_name).reject do |name, routes|
|
37
|
+
resource_exclusion.include?(name.to_sym)
|
38
|
+
end
|
39
|
+
|
40
|
+
grouped_routes.map { |name, routes| Resource.new(name, routes) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def formatted_request_headers
|
45
|
+
formatted_headers(GrapeApiary::Config.request_headers)
|
46
|
+
end
|
47
|
+
|
48
|
+
def formatted_response_headers
|
49
|
+
formatted_headers(GrapeApiary::Config.response_headers)
|
50
|
+
end
|
51
|
+
|
52
|
+
def show_request_sample?(route)
|
53
|
+
%w(PUT POST).include?(route.route_method)
|
54
|
+
end
|
55
|
+
|
56
|
+
def routes_binding
|
57
|
+
binding
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def formatted_headers(headers)
|
63
|
+
spacer = "\n" + (' ' * 12)
|
64
|
+
|
65
|
+
strings = headers.map do |header|
|
66
|
+
key, value = *header.first
|
67
|
+
|
68
|
+
"#{key}: #{value}"
|
69
|
+
end
|
70
|
+
|
71
|
+
strings.join(spacer)
|
16
72
|
end
|
17
73
|
end
|
18
74
|
end
|
data/lib/grape-apiary/config.rb
CHANGED
@@ -6,6 +6,7 @@ module GrapeApiary
|
|
6
6
|
:description,
|
7
7
|
:request_headers,
|
8
8
|
:response_headers,
|
9
|
+
:example_id_type,
|
9
10
|
:resource_exclusion
|
10
11
|
]
|
11
12
|
|
@@ -23,6 +24,35 @@ module GrapeApiary
|
|
23
24
|
def resource_exclusion
|
24
25
|
@resource_exclusion ||= []
|
25
26
|
end
|
27
|
+
|
28
|
+
def supported_id_types
|
29
|
+
[:integer, :uuid, :bson]
|
30
|
+
end
|
31
|
+
|
32
|
+
def example_id_type=(value)
|
33
|
+
fail UnsupportedIDType unless supported_id_types.include?(value)
|
34
|
+
|
35
|
+
if value.to_sym == :bson && !Object.const_defined?('BSON')
|
36
|
+
fail BSONNotDefinied
|
37
|
+
end
|
38
|
+
|
39
|
+
@example_id_type = value
|
40
|
+
end
|
41
|
+
|
42
|
+
def example_id_type
|
43
|
+
@example_id_type ||= :integer
|
44
|
+
end
|
45
|
+
|
46
|
+
def generate_id
|
47
|
+
case example_id_type
|
48
|
+
when :integer
|
49
|
+
SecureRandom.random_number(1000)
|
50
|
+
when :uuid
|
51
|
+
SecureRandom.uuid
|
52
|
+
when :bson
|
53
|
+
BSON::ObjectId.new.to_s
|
54
|
+
end
|
55
|
+
end
|
26
56
|
end
|
27
57
|
end
|
28
58
|
end
|
@@ -2,8 +2,9 @@ module GrapeApiary
|
|
2
2
|
class Parameter
|
3
3
|
attr_reader :route, :name, :settings
|
4
4
|
|
5
|
-
delegate :route_model, :route_namespace,
|
6
|
-
delegate :requirement, :type, :
|
5
|
+
delegate :route_model, :route_namespace, to: :route
|
6
|
+
delegate :requirement, :type, :documentation, :desc, to: :settings
|
7
|
+
delegate :example, to: :documentation, allow_nil: true
|
7
8
|
|
8
9
|
def initialize(route, name, options)
|
9
10
|
@route = route
|
@@ -30,11 +31,13 @@ module GrapeApiary
|
|
30
31
|
model = name.include?('_id') ? name.gsub('_id', '') : route.route_model
|
31
32
|
|
32
33
|
{
|
33
|
-
required:
|
34
|
-
requirement:
|
35
|
-
type:
|
36
|
-
desc:
|
37
|
-
|
34
|
+
required: true,
|
35
|
+
requirement: 'required',
|
36
|
+
type: 'uuid',
|
37
|
+
desc: "the `id` of the `#{model}`",
|
38
|
+
documentation: {
|
39
|
+
example: GrapeApiary::Config.generate_id
|
40
|
+
}
|
38
41
|
}
|
39
42
|
end
|
40
43
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
module GrapeApiary
|
2
2
|
class Resource
|
3
|
-
attr_reader :key, :name, :routes
|
3
|
+
attr_reader :key, :name, :routes, :sample_generator
|
4
4
|
|
5
5
|
def initialize(key, routes)
|
6
|
-
@key
|
7
|
-
@name
|
8
|
-
@routes
|
6
|
+
@key = key
|
7
|
+
@name = key.humanize
|
8
|
+
@routes = routes
|
9
|
+
@sample_generator = SampleGenerator.new(self)
|
9
10
|
end
|
10
11
|
|
11
12
|
def title
|
@@ -32,15 +33,25 @@ module GrapeApiary
|
|
32
33
|
end
|
33
34
|
|
34
35
|
def sample_request
|
35
|
-
|
36
|
+
sample_generator.request
|
36
37
|
end
|
37
38
|
|
38
39
|
def sample_response
|
39
|
-
|
40
|
+
sample_generator.response
|
40
41
|
end
|
41
42
|
|
42
43
|
def unique_params
|
43
|
-
#
|
44
|
+
# TODO: this is a hack, assuming that the resource has a POST or PUT
|
45
|
+
# route that defines all of the parameters that would define the resource
|
46
|
+
potential = routes.select do |route|
|
47
|
+
%w(POST PUT).include?(route.route_method)
|
48
|
+
end
|
49
|
+
|
50
|
+
if potential.present?
|
51
|
+
potential.first.route_params
|
52
|
+
else
|
53
|
+
[]
|
54
|
+
end
|
44
55
|
end
|
45
56
|
end
|
46
57
|
end
|
@@ -2,19 +2,45 @@ module GrapeApiary
|
|
2
2
|
class SampleGenerator
|
3
3
|
attr_reader :resource
|
4
4
|
|
5
|
+
delegate :unique_params, to: :resource
|
6
|
+
|
5
7
|
def initialize(resource)
|
6
8
|
@resource = resource
|
7
9
|
end
|
8
10
|
|
9
11
|
def sample
|
12
|
+
@sample ||= begin
|
13
|
+
array = resource.unique_params.map do |resource|
|
14
|
+
[resource.name, resource.example]
|
15
|
+
end
|
16
|
+
|
17
|
+
Hash[array]
|
18
|
+
end
|
10
19
|
end
|
11
20
|
|
12
21
|
def request
|
13
|
-
|
22
|
+
return unless sample.present?
|
23
|
+
|
24
|
+
# format json spaces for blueprint markdown
|
25
|
+
JSON.pretty_generate(sample)
|
26
|
+
.gsub('{', (' ' * 14) + '{')
|
27
|
+
.gsub('}', (' ' * 14) + '}')
|
28
|
+
.gsub(/\ {2}\"/, (' ' * 16) + '"')
|
14
29
|
end
|
15
30
|
|
16
31
|
def response
|
17
|
-
|
32
|
+
return unless sample.present?
|
33
|
+
|
34
|
+
hash = sample.reverse_merge(id: GrapeApiary::Config.generate_id)
|
35
|
+
# sample = [sample] if list?(route)
|
36
|
+
|
37
|
+
# format json spaces for blueprint markdown
|
38
|
+
JSON.pretty_generate(hash)
|
39
|
+
.gsub('[', (' ' * 12) + '[')
|
40
|
+
.gsub(']', (' ' * 12) + ']')
|
41
|
+
.gsub('{', (' ' * 14) + '{')
|
42
|
+
.gsub('}', (' ' * 14) + '}')
|
43
|
+
.gsub(/\ {2}\"/, (' ' * 16) + '"')
|
18
44
|
end
|
19
45
|
end
|
20
46
|
end
|
@@ -27,11 +27,14 @@ Actions on the <%= resource.name %> resource
|
|
27
27
|
|
28
28
|
<%= resource.sample_request %>
|
29
29
|
<% end %>
|
30
|
+
<%= route.request_description %>
|
30
31
|
+ Headers
|
31
32
|
|
32
33
|
<%= formatted_response_headers %>
|
33
34
|
|
34
35
|
+ Body
|
36
|
+
|
37
|
+
<%= resource.sample_response %>
|
35
38
|
<% end %>
|
36
39
|
<% end %>
|
37
40
|
<% end %>
|
data/lib/grape-apiary/version.rb
CHANGED
@@ -3,26 +3,28 @@ require 'spec_helper'
|
|
3
3
|
describe GrapeApiary::Blueprint do
|
4
4
|
include_context 'configuration'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
6
|
+
before do
|
7
|
+
GrapeApiary.config do |config|
|
8
|
+
config.host = host
|
9
|
+
config.name = name
|
10
|
+
config.description = description
|
11
|
+
config.resource_exclusion = [:admin]
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
GrapeApiary.config.request_headers = [
|
15
|
+
{ 'Accept-Charset' => 'utf-8' },
|
16
|
+
{ 'Connection' => 'keep-alive' }
|
17
|
+
]
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
GrapeApiary.config.response_headers = [
|
20
|
+
{ 'Content-Length' => '21685' },
|
21
|
+
{ 'Connection' => 'keep-alive' }
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
subject { GrapeApiary::Blueprint.new(SampleApi) }
|
25
26
|
|
27
|
+
context '#generate' do
|
26
28
|
let(:klass) { SampleApi }
|
27
29
|
|
28
30
|
subject { GrapeApiary::Blueprint.new(klass).generate }
|
@@ -47,4 +49,32 @@ describe GrapeApiary::Blueprint do
|
|
47
49
|
expect(subject).to include('# Group Widgets')
|
48
50
|
end
|
49
51
|
end
|
52
|
+
|
53
|
+
it 'exposes configuration settings' do
|
54
|
+
GrapeApiary::Config::SETTINGS.each do |setting|
|
55
|
+
expect(subject.send(setting)).to eq(GrapeApiary.config.send(setting))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'exposes the raw routes of the given api' do
|
60
|
+
expect(subject.routes).to eq(SampleApi.routes)
|
61
|
+
end
|
62
|
+
|
63
|
+
context '#resources' do
|
64
|
+
let(:unique_routes) { subject.routes.map(&:route_name).uniq }
|
65
|
+
|
66
|
+
let(:included_routes) do
|
67
|
+
unique_routes.reject do |name|
|
68
|
+
GrapeApiary.config.resource_exclusion.include?(name.to_sym)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'aggregates routes into resources' do
|
73
|
+
expect(subject.resources.first).to be_a(GrapeApiary::Resource)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'excluded resources based on configuration' do
|
77
|
+
expect(subject.resources.map(&:key)).to eq(included_routes)
|
78
|
+
end
|
79
|
+
end
|
50
80
|
end
|
@@ -48,4 +48,38 @@ describe GrapeApiary::Config do
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|
51
|
+
|
52
|
+
context 'sample id generation' do
|
53
|
+
it 'allows for setting the type for id generation' do
|
54
|
+
subject.example_id_type = :uuid
|
55
|
+
|
56
|
+
expect(subject.example_id_type).to eq(:uuid)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'guards against unsupported types' do
|
60
|
+
expect do
|
61
|
+
subject.example_id_type = :foo
|
62
|
+
end.to raise_error(UnsupportedIDType)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'checks for the bson library if requested' do
|
66
|
+
expect { subject.example_id_type = :bson }.to raise_error(BSONNotDefinied)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'generates a valid uuid' do
|
70
|
+
test = /[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}/i
|
71
|
+
|
72
|
+
subject.example_id_type = :uuid
|
73
|
+
|
74
|
+
expect(subject.generate_id).to match(test)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'generates a valid integer' do
|
78
|
+
test = /^[0-9]{1,10}$/
|
79
|
+
|
80
|
+
subject.example_id_type = :integer
|
81
|
+
|
82
|
+
expect(subject.generate_id.to_s).to match(test)
|
83
|
+
end
|
84
|
+
end
|
51
85
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GrapeApiary::Resource do
|
4
|
+
include_context 'configuration'
|
5
|
+
|
6
|
+
subject { GrapeApiary::Resource.new('foo', []) }
|
7
|
+
|
8
|
+
context 'sample' do
|
9
|
+
it 'request generation is delegated to a generator' do
|
10
|
+
expect(subject.sample_generator).to receive(:request)
|
11
|
+
|
12
|
+
subject.sample_request
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'response generation is delegated to a generator' do
|
16
|
+
expect(subject.sample_generator).to receive(:response)
|
17
|
+
|
18
|
+
subject.sample_response
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GrapeApiary::SampleGenerator do
|
4
|
+
include_context 'configuration'
|
5
|
+
|
6
|
+
before do
|
7
|
+
GrapeApiary.config do |config|
|
8
|
+
config.host = host
|
9
|
+
config.name = name
|
10
|
+
config.description = description
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:blueprint) { GrapeApiary::Blueprint.new(SampleApi) }
|
15
|
+
let(:resource) { blueprint.resources.first }
|
16
|
+
|
17
|
+
subject { GrapeApiary::SampleGenerator.new(resource) }
|
18
|
+
|
19
|
+
it 'creates a sample hash from a resource' do
|
20
|
+
expect(subject.sample).to be_a(Hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
context '#request' do
|
24
|
+
it 'creates a sample request in JSON form' do
|
25
|
+
expect { JSON.parse(subject.request) }.to_not raise_error
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context '#response' do
|
30
|
+
it 'creates a sample response in JSON form' do
|
31
|
+
expect { JSON.parse(subject.response) }.to_not raise_error
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'includes a sample id' do
|
35
|
+
expect(JSON.parse(subject.response)['id']).to_not be(nil)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
1
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'support'))
|
3
4
|
|
5
|
+
require 'grape/apiary'
|
6
|
+
|
7
|
+
require 'rubygems'
|
4
8
|
require 'bundler'
|
5
9
|
Bundler.setup :default, :test
|
6
10
|
|
7
11
|
require 'coveralls'
|
8
12
|
Coveralls.wear!
|
9
13
|
|
10
|
-
require 'grape/apiary'
|
11
14
|
require 'rspec'
|
12
15
|
require 'pry'
|
13
16
|
|
@@ -5,24 +5,10 @@ shared_context 'configuration' do
|
|
5
5
|
let(:resource_exclusion) { [:admin, :swagger_docs] }
|
6
6
|
|
7
7
|
let(:request_headers) do
|
8
|
-
[
|
9
|
-
{ 'Accept-Charset' => 'utf-8' },
|
10
|
-
{ 'Connection' => 'keep-alive' },
|
11
|
-
{ 'Content-Type' => 'application/json' }
|
12
|
-
]
|
8
|
+
[{ 'Accept-Charset' => 'utf-8' }]
|
13
9
|
end
|
14
10
|
|
15
11
|
let(:response_headers) do
|
16
|
-
[
|
17
|
-
{ 'Content-Length' => '21685' },
|
18
|
-
{ 'Connection' => 'keep-alive' },
|
19
|
-
{ 'Content-Type' => 'application/json' }
|
20
|
-
]
|
21
|
-
end
|
22
|
-
|
23
|
-
let(:app) do
|
24
|
-
def app
|
25
|
-
SampleApi
|
26
|
-
end
|
12
|
+
[{ 'Connection' => 'keep-alive' }]
|
27
13
|
end
|
28
14
|
end
|
data/spec/support/sample_api.rb
CHANGED
@@ -10,8 +10,14 @@ class SampleApi < Grape::API
|
|
10
10
|
|
11
11
|
desc 'create a widget'
|
12
12
|
params do
|
13
|
-
requires :name,
|
14
|
-
|
13
|
+
requires :name,
|
14
|
+
type: 'string',
|
15
|
+
desc: 'the widgets name',
|
16
|
+
documentation: { example: 'super widget' }
|
17
|
+
optional :description,
|
18
|
+
type: 'string',
|
19
|
+
desc: 'the widgets name',
|
20
|
+
documentation: { example: 'the best widget ever made' }
|
15
21
|
end
|
16
22
|
post '/' do
|
17
23
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grape-apiary
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Allen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: grape
|
@@ -108,6 +108,48 @@ dependencies:
|
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0.9'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: guard
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '2.4'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '2.4'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: guard-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '4.2'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '4.2'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: guard-bundler
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '2.0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '2.0'
|
111
153
|
description: Auto generates an Apiary (http://apiary.io) Blueprint from the docuementation
|
112
154
|
that is created by your Grape API
|
113
155
|
email:
|
@@ -117,9 +159,11 @@ extensions: []
|
|
117
159
|
extra_rdoc_files: []
|
118
160
|
files:
|
119
161
|
- ".gitignore"
|
162
|
+
- ".rspec"
|
120
163
|
- ".rubocop.yml"
|
121
164
|
- ".travis.yml"
|
122
165
|
- Gemfile
|
166
|
+
- Guardfile
|
123
167
|
- LICENSE.txt
|
124
168
|
- README.md
|
125
169
|
- Rakefile
|
@@ -130,15 +174,15 @@ files:
|
|
130
174
|
- lib/grape-apiary/parameter.rb
|
131
175
|
- lib/grape-apiary/resource.rb
|
132
176
|
- lib/grape-apiary/route.rb
|
133
|
-
- lib/grape-apiary/routes.rb
|
134
177
|
- lib/grape-apiary/sample_generator.rb
|
135
178
|
- lib/grape-apiary/templates/blueprint.md.erb
|
136
179
|
- lib/grape-apiary/version.rb
|
137
180
|
- lib/grape/apiary.rb
|
138
181
|
- spec/grape-apiary/blueprint_spec.rb
|
139
182
|
- spec/grape-apiary/config_spec.rb
|
183
|
+
- spec/grape-apiary/resource_spec.rb
|
140
184
|
- spec/grape-apiary/route_spec.rb
|
141
|
-
- spec/grape-apiary/
|
185
|
+
- spec/grape-apiary/sample_generator_spec.rb
|
142
186
|
- spec/spec_helper.rb
|
143
187
|
- spec/support/config_context.rb
|
144
188
|
- spec/support/sample_api.rb
|
@@ -169,8 +213,9 @@ summary: Allows for generating an Apiary Blueprint for you Grape API
|
|
169
213
|
test_files:
|
170
214
|
- spec/grape-apiary/blueprint_spec.rb
|
171
215
|
- spec/grape-apiary/config_spec.rb
|
216
|
+
- spec/grape-apiary/resource_spec.rb
|
172
217
|
- spec/grape-apiary/route_spec.rb
|
173
|
-
- spec/grape-apiary/
|
218
|
+
- spec/grape-apiary/sample_generator_spec.rb
|
174
219
|
- spec/spec_helper.rb
|
175
220
|
- spec/support/config_context.rb
|
176
221
|
- spec/support/sample_api.rb
|
data/lib/grape-apiary/routes.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
module GrapeApiary
|
2
|
-
class Routes
|
3
|
-
attr_reader :api_class
|
4
|
-
|
5
|
-
delegate(*GrapeApiary::Config::SETTINGS, to: 'GrapeApiary::Config')
|
6
|
-
|
7
|
-
def initialize(api_class)
|
8
|
-
@api_class = api_class
|
9
|
-
end
|
10
|
-
|
11
|
-
def routes
|
12
|
-
@routes ||= api_class.routes.map do |route|
|
13
|
-
GrapeApiary::Route.new(route)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def resources
|
18
|
-
@resources ||= begin
|
19
|
-
grouped_routes = routes.group_by(&:route_name).reject do |name, routes|
|
20
|
-
resource_exclusion.include?(name.to_sym)
|
21
|
-
end
|
22
|
-
|
23
|
-
grouped_routes.map { |name, routes| Resource.new(name, routes) }
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def formatted_request_headers
|
28
|
-
formatted_headers(GrapeApiary::Config.request_headers)
|
29
|
-
end
|
30
|
-
|
31
|
-
def formatted_response_headers
|
32
|
-
formatted_headers(GrapeApiary::Config.response_headers)
|
33
|
-
end
|
34
|
-
|
35
|
-
def show_request_sample?(route)
|
36
|
-
%w(PUT POST).include?(route.route_method)
|
37
|
-
end
|
38
|
-
|
39
|
-
def routes_binding
|
40
|
-
binding
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def formatted_headers(headers)
|
46
|
-
spacer = "\n" + (' ' * 12)
|
47
|
-
|
48
|
-
strings = headers.map do |header|
|
49
|
-
key, value = *header.first
|
50
|
-
|
51
|
-
"#{key}: #{value}"
|
52
|
-
end
|
53
|
-
|
54
|
-
strings.join(spacer)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe GrapeApiary::Routes do
|
4
|
-
include_context 'configuration'
|
5
|
-
|
6
|
-
before do
|
7
|
-
GrapeApiary.config do |config|
|
8
|
-
config.host = host
|
9
|
-
config.name = name
|
10
|
-
config.description = description
|
11
|
-
config.resource_exclusion = [:admin]
|
12
|
-
end
|
13
|
-
|
14
|
-
GrapeApiary.config.request_headers = [
|
15
|
-
{ 'Accept-Charset' => 'utf-8' },
|
16
|
-
{ 'Connection' => 'keep-alive' }
|
17
|
-
]
|
18
|
-
|
19
|
-
GrapeApiary.config.response_headers = [
|
20
|
-
{ 'Content-Length' => '21685' },
|
21
|
-
{ 'Connection' => 'keep-alive' }
|
22
|
-
]
|
23
|
-
end
|
24
|
-
|
25
|
-
subject { GrapeApiary::Routes.new(SampleApi) }
|
26
|
-
|
27
|
-
it 'exposes configuration settings' do
|
28
|
-
GrapeApiary::Config::SETTINGS.each do |setting|
|
29
|
-
expect(subject.send(setting)).to eq(GrapeApiary.config.send(setting))
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'exposes the raw routes of the given api' do
|
34
|
-
expect(subject.routes).to eq(SampleApi.routes)
|
35
|
-
end
|
36
|
-
|
37
|
-
context '#resources' do
|
38
|
-
let(:unique_routes) { subject.routes.map(&:route_name).uniq }
|
39
|
-
|
40
|
-
let(:included_routes) do
|
41
|
-
unique_routes.reject do |name|
|
42
|
-
GrapeApiary.config.resource_exclusion.include?(name.to_sym)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'aggregates routes into resources' do
|
47
|
-
expect(subject.resources.first).to be_a(GrapeApiary::Resource)
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'excluded resources based on configuration' do
|
51
|
-
expect(subject.resources.map(&:key)).to eq(included_routes)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|