api_taster 0.4.8 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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 = Route.find(params[:id])
13
- @params = Route.params_for(@route)
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
@@ -1,4 +1,10 @@
1
+ require 'redcarpet'
2
+
1
3
  module ApiTaster
2
4
  module ApplicationHelper
5
+ def markdown(text)
6
+ markdown_renderer ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML)
7
+ markdown_renderer.render(text).html_safe
8
+ end
3
9
  end
4
10
  end
@@ -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 = '<legend class="hero-legend"></legend>'
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
@@ -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 post(path, params = {})
9
- map_method(:post, path, params)
14
+ def put(path, params = {}, metadata = {})
15
+ map_method(:put, path, params, metadata)
10
16
  end
11
17
 
12
- def put(path, params = {})
13
- map_method(:put, path, params)
18
+ def delete(path, params = {}, metadata = {})
19
+ map_method(:delete, path, params, metadata)
14
20
  end
15
21
 
16
- def delete(path, params = {})
17
- map_method(:delete, path, params)
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module ApiTaster
2
- VERSION = "0.4.8"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -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
@@ -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', {
@@ -48,12 +48,12 @@ module ApiTaster
48
48
  end
49
49
 
50
50
  it "does arrays" do
51
- builder.html.should match(/name="\[user\]\[comment\]\[title\]\[\]" value="1"/)
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="\[items\]\[\]\[nested_items\]\[\]\[nested_numbers\]\[\]" value="5"/)
56
- builder.html.should match(/name="\[items\]\[\]\[nested_items\]\[\]\[name\]" value="apple"/)
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
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ module ApiTaster
4
+ describe ApplicationHelper do
5
+ it "#markdown" do
6
+ helper.markdown('__bold__').should == "<p><strong>bold</strong></p>\n"
7
+ end
8
+ end
9
+ 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
- it "#params_for" do
79
- Route.stub(:routes).and_return([{
80
- :id => 0,
81
- :path => '/dummy/:dummy_id'
82
- }, {
83
- :id => 999,
84
- :path => 'a_non_existing_dummy',
85
- :verb => 'get'
86
- }])
87
- Route.supplied_params[0] = [{ :dummy_id => 1, :hello => 'world' }]
88
-
89
- Route.params_for(Route.find(999)).should have_key(:undefined)
90
-
91
- 2.times do
92
- Route.params_for(Route.find(0)).should == [{
93
- :url_params => { :dummy_id => 1 },
94
- :post_params => { :hello => 'world' }
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.8
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-07-16 00:00:00.000000000 Z
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: 868374881218016492
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: 868374881218016492
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