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 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