api_taster 0.4.8 → 0.5.0
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.
- data/README.md +22 -0
- data/api_taster.gemspec +1 -0
- data/app/controllers/api_taster/routes_controller.rb +3 -2
- data/app/helpers/api_taster/application_helper.rb +6 -0
- data/app/views/api_taster/routes/show.html.erb +7 -0
- data/lib/api_taster/form_builder.rb +2 -2
- data/lib/api_taster/mapper.rb +24 -9
- data/lib/api_taster/route.rb +17 -0
- data/lib/api_taster/version.rb +1 -1
- data/spec/controllers/api_taster/routes_controller_spec.rb +2 -0
- data/spec/dummy/config/routes.rb +1 -4
- data/spec/form_builder_spec.rb +3 -3
- data/spec/helpers/api_taster/application_helper_spec.rb +9 -0
- data/spec/mapper_spec.rb +26 -1
- data/spec/route_spec.rb +36 -18
- metadata +22 -4
data/README.md
CHANGED
@@ -34,6 +34,7 @@ In `routes.rb`, define parameters for each API endpoint after the normal routes
|
|
34
34
|
```ruby
|
35
35
|
if Rails.env.development?
|
36
36
|
ApiTaster.routes do
|
37
|
+
desc 'Get a __list__ of users'
|
37
38
|
get '/users'
|
38
39
|
|
39
40
|
post '/users', {
|
@@ -78,6 +79,27 @@ ApiTaster.routes do
|
|
78
79
|
end
|
79
80
|
```
|
80
81
|
|
82
|
+
### Commenting API Endpoints
|
83
|
+
|
84
|
+
Before each route definitions, you may use `desc` to add some comments. Markdown is supported.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
desc 'Get a __list__ of users'
|
88
|
+
get '/users'
|
89
|
+
```
|
90
|
+
|
91
|
+
### Metadata for API Endpoints
|
92
|
+
|
93
|
+
For each route definition, you may supply an optional third parameter (hash) as metadata:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
get '/users', {}, { :meta => 'data' }
|
97
|
+
```
|
98
|
+
|
99
|
+
The metadata option is useful for passing in arbitrary data for a route definition. For example, you could specify response expectations so that your test suite could tap into them.
|
100
|
+
|
101
|
+
Metadata for every route definition are stored in `ApiTaster::Route.metadata`. Please read the source code to find out how to get metadata for a particular route.
|
102
|
+
|
81
103
|
### Missing Route Definitions Detection
|
82
104
|
|
83
105
|
Instead of manually finding out which route definitions you need, API Taster provides a warning page that shows you all the missing definitions.
|
data/api_taster.gemspec
CHANGED
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.add_dependency 'jquery-rails'
|
19
19
|
s.add_dependency 'sass-rails'
|
20
20
|
s.add_dependency 'bootstrap-sass', '~> 2.0.3'
|
21
|
+
s.add_dependency 'redcarpet'
|
21
22
|
|
22
23
|
s.add_development_dependency 'rake'
|
23
24
|
s.add_development_dependency 'simplecov'
|
@@ -9,8 +9,9 @@ module ApiTaster
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def show
|
12
|
-
@route
|
13
|
-
@params
|
12
|
+
@route = Route.find(params[:id])
|
13
|
+
@params = Route.params_for(@route)
|
14
|
+
@comment = Route.comment_for(@route)
|
14
15
|
end
|
15
16
|
|
16
17
|
def missing_definitions
|
@@ -8,6 +8,13 @@
|
|
8
8
|
<% if @params.is_a?(Hash) && @params.has_key?(:undefined) %>
|
9
9
|
<%= render 'undefined_route', :route => @params[:undefined] %>
|
10
10
|
<% else %>
|
11
|
+
|
12
|
+
<% if @comment %>
|
13
|
+
<div class="well">
|
14
|
+
<%= markdown @comment %>
|
15
|
+
</div>
|
16
|
+
<% end %>
|
17
|
+
|
11
18
|
<% @params.each do |param| %>
|
12
19
|
<%= form_tag @route[:path], :method => @route[:verb], :class => 'well form-horizontal', :remote => true do %>
|
13
20
|
|
@@ -8,12 +8,12 @@ module ApiTaster
|
|
8
8
|
|
9
9
|
def initialize(params)
|
10
10
|
flush_output_buffer
|
11
|
-
@_buffer = '
|
11
|
+
@_buffer = ''
|
12
12
|
add_to_buffer(params)
|
13
13
|
end
|
14
14
|
|
15
15
|
def html
|
16
|
-
@_buffer
|
16
|
+
"<legend class=\"hero-legend\"></legend>#{@_buffer}"
|
17
17
|
end
|
18
18
|
|
19
19
|
private
|
data/lib/api_taster/mapper.rb
CHANGED
@@ -1,25 +1,31 @@
|
|
1
1
|
module ApiTaster
|
2
2
|
class Mapper
|
3
|
+
cattr_accessor :last_desc
|
4
|
+
|
3
5
|
class << self
|
4
|
-
def get(path, params = {})
|
5
|
-
map_method(:get, path, params)
|
6
|
+
def get(path, params = {}, metadata = {})
|
7
|
+
map_method(:get, path, params, metadata)
|
8
|
+
end
|
9
|
+
|
10
|
+
def post(path, params = {}, metadata = {})
|
11
|
+
map_method(:post, path, params, metadata)
|
6
12
|
end
|
7
13
|
|
8
|
-
def
|
9
|
-
map_method(:
|
14
|
+
def put(path, params = {}, metadata = {})
|
15
|
+
map_method(:put, path, params, metadata)
|
10
16
|
end
|
11
17
|
|
12
|
-
def
|
13
|
-
map_method(:
|
18
|
+
def delete(path, params = {}, metadata = {})
|
19
|
+
map_method(:delete, path, params, metadata)
|
14
20
|
end
|
15
21
|
|
16
|
-
def
|
17
|
-
|
22
|
+
def desc(text)
|
23
|
+
self.last_desc = text
|
18
24
|
end
|
19
25
|
|
20
26
|
private
|
21
27
|
|
22
|
-
def map_method(method, path, params)
|
28
|
+
def map_method(method, path, params, metadata)
|
23
29
|
route = Route.find_by_verb_and_path(method, path)
|
24
30
|
|
25
31
|
if route.nil?
|
@@ -31,6 +37,15 @@ module ApiTaster
|
|
31
37
|
else
|
32
38
|
Route.supplied_params[route[:id]] ||= []
|
33
39
|
Route.supplied_params[route[:id]] << ApiTaster.global_params.merge(params)
|
40
|
+
|
41
|
+
unless last_desc.nil?
|
42
|
+
Route.comments[route[:id]] = last_desc
|
43
|
+
self.last_desc = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
if metadata.any?
|
47
|
+
Route.metadata[route[:id]] = metadata
|
48
|
+
end
|
34
49
|
end
|
35
50
|
end
|
36
51
|
end
|
data/lib/api_taster/route.rb
CHANGED
@@ -5,6 +5,8 @@ module ApiTaster
|
|
5
5
|
cattr_accessor :mappings
|
6
6
|
cattr_accessor :supplied_params
|
7
7
|
cattr_accessor :obsolete_definitions
|
8
|
+
cattr_accessor :comments
|
9
|
+
cattr_accessor :metadata
|
8
10
|
|
9
11
|
class << self
|
10
12
|
|
@@ -12,6 +14,8 @@ module ApiTaster
|
|
12
14
|
self.route_set = Rails.application.routes
|
13
15
|
self.supplied_params = {}
|
14
16
|
self.obsolete_definitions = []
|
17
|
+
self.comments = {}
|
18
|
+
self.metadata = {}
|
15
19
|
|
16
20
|
normalise_routes!
|
17
21
|
|
@@ -31,6 +35,7 @@ module ApiTaster
|
|
31
35
|
end
|
32
36
|
|
33
37
|
route_set.routes.each do |route|
|
38
|
+
next if route.app.is_a?(ActionDispatch::Routing::Mapper::Constraints)
|
34
39
|
next if route.app.is_a?(Sprockets::Environment)
|
35
40
|
next if route.app == ApiTaster::Engine
|
36
41
|
|
@@ -71,6 +76,18 @@ module ApiTaster
|
|
71
76
|
supplied_params[route[:id]].collect { |input| split_input(input, route) }
|
72
77
|
end
|
73
78
|
|
79
|
+
def comment_for(route)
|
80
|
+
unless undefined_route?(route)
|
81
|
+
self.comments[route[:id].to_i]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def metadata_for(route)
|
86
|
+
unless undefined_route?(route)
|
87
|
+
self.metadata[route[:id].to_i]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
74
91
|
def defined_definitions
|
75
92
|
routes.reject { |route| undefined_route?(route) }
|
76
93
|
end
|
data/lib/api_taster/version.rb
CHANGED
@@ -21,11 +21,13 @@ module ApiTaster
|
|
21
21
|
it "#show" do
|
22
22
|
Route.stub(:find).and_return(Route.new)
|
23
23
|
Route.stub(:params_for).and_return([])
|
24
|
+
Route.stub(:comment_for).and_return([])
|
24
25
|
|
25
26
|
get :show, :id => 1, :use_route => :api_taster
|
26
27
|
|
27
28
|
assigns(:route).should be_kind_of(Route)
|
28
29
|
assigns(:params).should be_kind_of(Array)
|
30
|
+
assigns(:comment).should be_kind_of(Array)
|
29
31
|
end
|
30
32
|
|
31
33
|
it "#missing_definitions" do
|
data/spec/dummy/config/routes.rb
CHANGED
@@ -10,14 +10,11 @@ Rails.application.routes.draw do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
ApiTaster.routes do
|
13
|
-
ApiTaster.global_params = {
|
14
|
-
:version => 1
|
15
|
-
}
|
16
|
-
|
17
13
|
get '/i_dont_exist_anymore', {
|
18
14
|
:hello => 'world'
|
19
15
|
}
|
20
16
|
|
17
|
+
desc 'Get a __list__ of users.'
|
21
18
|
get '/users'
|
22
19
|
|
23
20
|
post '/users', {
|
data/spec/form_builder_spec.rb
CHANGED
@@ -48,12 +48,12 @@ module ApiTaster
|
|
48
48
|
end
|
49
49
|
|
50
50
|
it "does arrays" do
|
51
|
-
builder.html.should match(/name="
|
51
|
+
builder.html.should match(/name="user\[comment\]\[title\]\[\]" value="1"/)
|
52
52
|
end
|
53
53
|
|
54
54
|
it "does nested arrays" do
|
55
|
-
builder.html.should match(/name="
|
56
|
-
builder.html.should match(/name="
|
55
|
+
builder.html.should match(/name="items\[\]\[nested_items\]\[\]\[nested_numbers\]\[\]" value="5"/)
|
56
|
+
builder.html.should match(/name="items\[\]\[nested_items\]\[\]\[name\]" value="apple"/)
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
data/spec/mapper_spec.rb
CHANGED
@@ -46,9 +46,10 @@ module ApiTaster
|
|
46
46
|
|
47
47
|
before(:all) do
|
48
48
|
ApiTaster.routes do
|
49
|
+
desc "Dummy user ID"
|
49
50
|
get '/dummy_users/:id', :id => 1
|
50
51
|
post '/dummy_users'
|
51
|
-
post '/dummy_users', { :hello => 'world' }
|
52
|
+
post '/dummy_users', { :hello => 'world' }, { :meta => 'data' }
|
52
53
|
put '/dummy_users/:id', :id => 2
|
53
54
|
delete '/dummy_users/:id', :id => 3
|
54
55
|
end
|
@@ -79,5 +80,29 @@ module ApiTaster
|
|
79
80
|
|
80
81
|
Route.supplied_params[route[:id]].should == [{ :id => 3 }]
|
81
82
|
end
|
83
|
+
|
84
|
+
it "describes a route" do
|
85
|
+
route = Route.find_by_verb_and_path(:get, '/dummy_users/:id')
|
86
|
+
|
87
|
+
Route.comment_for(route).should == "Dummy user ID"
|
88
|
+
end
|
89
|
+
|
90
|
+
it "doesn't describe a route" do
|
91
|
+
route = Route.find_by_verb_and_path(:post, '/dummy_users')
|
92
|
+
|
93
|
+
Route.comment_for(route).should be_nil
|
94
|
+
end
|
95
|
+
|
96
|
+
it "meta data for a route" do
|
97
|
+
route = Route.find_by_verb_and_path(:post, '/dummy_users')
|
98
|
+
|
99
|
+
Route.metadata_for(route).should == { :meta => 'data' }
|
100
|
+
end
|
101
|
+
|
102
|
+
it "empty meta data for a route" do
|
103
|
+
route = Route.find_by_verb_and_path(:get, '/dummy_users')
|
104
|
+
|
105
|
+
Route.metadata_for(route).should == nil
|
106
|
+
end
|
82
107
|
end
|
83
108
|
end
|
data/spec/route_spec.rb
CHANGED
@@ -75,24 +75,42 @@ module ApiTaster
|
|
75
75
|
Route.find_by_verb_and_path(:delete, '/home').should == nil
|
76
76
|
end
|
77
77
|
|
78
|
-
|
79
|
-
|
80
|
-
:
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
Route.params_for(Route.find(
|
93
|
-
|
94
|
-
|
95
|
-
|
78
|
+
context "get data of a route" do
|
79
|
+
before do
|
80
|
+
Route.stub(:routes).and_return([{
|
81
|
+
:id => 0,
|
82
|
+
:path => '/dummy/:dummy_id'
|
83
|
+
}, {
|
84
|
+
:id => 999,
|
85
|
+
:path => 'a_non_existing_dummy',
|
86
|
+
:verb => 'get'
|
87
|
+
}])
|
88
|
+
Route.supplied_params[0] = [{ :dummy_id => 1, :hello => 'world' }]
|
89
|
+
end
|
90
|
+
|
91
|
+
it "#params_for" do
|
92
|
+
Route.params_for(Route.find(999)).should have_key(:undefined)
|
93
|
+
|
94
|
+
2.times do
|
95
|
+
Route.params_for(Route.find(0)).should == [{
|
96
|
+
:url_params => { :dummy_id => 1 },
|
97
|
+
:post_params => { :hello => 'world' }
|
98
|
+
}]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
it "#comment_for" do
|
103
|
+
markdown_comment = "Heading\n=======\n * List item 1\n * List item 2"
|
104
|
+
Route.comments[0] = markdown_comment
|
105
|
+
|
106
|
+
Route.comment_for(Route.find(0)).should == markdown_comment
|
107
|
+
end
|
108
|
+
|
109
|
+
it "#metadata_for" do
|
110
|
+
metadata = { :hello => 'world' }
|
111
|
+
Route.metadata[0] = metadata
|
112
|
+
|
113
|
+
Route.metadata_for(Route.find(0)).should == metadata
|
96
114
|
end
|
97
115
|
end
|
98
116
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: api_taster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-08-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -75,6 +75,22 @@ dependencies:
|
|
75
75
|
- - ~>
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: 2.0.3
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: redcarpet
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
78
94
|
- !ruby/object:Gem::Dependency
|
79
95
|
name: rake
|
80
96
|
requirement: !ruby/object:Gem::Requirement
|
@@ -230,6 +246,7 @@ files:
|
|
230
246
|
- spec/dummy/log/.gitkeep
|
231
247
|
- spec/dummy/script/rails
|
232
248
|
- spec/form_builder_spec.rb
|
249
|
+
- spec/helpers/api_taster/application_helper_spec.rb
|
233
250
|
- spec/mapper_spec.rb
|
234
251
|
- spec/route_spec.rb
|
235
252
|
- spec/spec_helper.rb
|
@@ -466,7 +483,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
466
483
|
version: '0'
|
467
484
|
segments:
|
468
485
|
- 0
|
469
|
-
hash:
|
486
|
+
hash: -1701071876129671469
|
470
487
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
471
488
|
none: false
|
472
489
|
requirements:
|
@@ -475,7 +492,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
475
492
|
version: '0'
|
476
493
|
segments:
|
477
494
|
- 0
|
478
|
-
hash:
|
495
|
+
hash: -1701071876129671469
|
479
496
|
requirements: []
|
480
497
|
rubyforge_project:
|
481
498
|
rubygems_version: 1.8.24
|
@@ -727,6 +744,7 @@ test_files:
|
|
727
744
|
- spec/dummy/tmp/cache/sass/fced329c33ec1fff188d99efbc4d1fd1c556b66a/layout.css.scssc
|
728
745
|
- spec/dummy/tmp/pids/server.pid
|
729
746
|
- spec/form_builder_spec.rb
|
747
|
+
- spec/helpers/api_taster/application_helper_spec.rb
|
730
748
|
- spec/mapper_spec.rb
|
731
749
|
- spec/route_spec.rb
|
732
750
|
- spec/spec_helper.rb
|