rapidoc 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/.gitignore +21 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +20 -0
  4. data/README.rdoc +152 -0
  5. data/Rakefile +1 -0
  6. data/lib/rapidoc.rb +62 -0
  7. data/lib/rapidoc/action_doc.rb +86 -0
  8. data/lib/rapidoc/config.rb +127 -0
  9. data/lib/rapidoc/config/rapidoc.yml +4 -0
  10. data/lib/rapidoc/controller_extractor.rb +54 -0
  11. data/lib/rapidoc/http_response.rb +61 -0
  12. data/lib/rapidoc/param_errors.rb +43 -0
  13. data/lib/rapidoc/resource_doc.rb +66 -0
  14. data/lib/rapidoc/resources_extractor.rb +42 -0
  15. data/lib/rapidoc/routes_doc.rb +96 -0
  16. data/lib/rapidoc/templates/action.html.hbs +170 -0
  17. data/lib/rapidoc/templates/assets/css/bootstrap-responsive.min.css +9 -0
  18. data/lib/rapidoc/templates/assets/css/bootstrap.min.css +9 -0
  19. data/lib/rapidoc/templates/assets/css/rapidoc.css +43 -0
  20. data/lib/rapidoc/templates/assets/img/glyphicons-halflings.png +0 -0
  21. data/lib/rapidoc/templates/assets/js/bootstrap.min.js +6 -0
  22. data/lib/rapidoc/templates/assets/js/jquery-1.9.0.min.js +4 -0
  23. data/lib/rapidoc/templates/assets/js/json2.js +486 -0
  24. data/lib/rapidoc/templates/index.html.hbs +85 -0
  25. data/lib/rapidoc/templates_generator.rb +65 -0
  26. data/lib/rapidoc/version.rb +3 -0
  27. data/lib/rapidoc/yaml_parser.rb +49 -0
  28. data/lib/tasks/railtie.rb +7 -0
  29. data/lib/tasks/rapidoc.rake +18 -0
  30. data/rapidoc.gemspec +26 -0
  31. data/spec/dummy/.gitignore +15 -0
  32. data/spec/dummy/Gemfile +42 -0
  33. data/spec/dummy/README.rdoc +261 -0
  34. data/spec/dummy/Rakefile +7 -0
  35. data/spec/dummy/app/assets/images/rails.png +0 -0
  36. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  37. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  38. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  39. data/spec/dummy/app/controllers/users_controller.rb +114 -0
  40. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  41. data/spec/dummy/app/mailers/.gitkeep +0 -0
  42. data/spec/dummy/app/models/.gitkeep +0 -0
  43. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  44. data/spec/dummy/config.ru +4 -0
  45. data/spec/dummy/config/application.rb +62 -0
  46. data/spec/dummy/config/boot.rb +6 -0
  47. data/spec/dummy/config/database.yml +25 -0
  48. data/spec/dummy/config/environment.rb +5 -0
  49. data/spec/dummy/config/environments/development.rb +37 -0
  50. data/spec/dummy/config/environments/production.rb +67 -0
  51. data/spec/dummy/config/environments/test.rb +37 -0
  52. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  53. data/spec/dummy/config/initializers/inflections.rb +15 -0
  54. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  55. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  56. data/spec/dummy/config/initializers/session_store.rb +8 -0
  57. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  58. data/spec/dummy/config/locales/en.yml +5 -0
  59. data/spec/dummy/config/routes.rb +9 -0
  60. data/spec/dummy/db/seeds.rb +7 -0
  61. data/spec/dummy/lib/assets/.gitkeep +0 -0
  62. data/spec/dummy/lib/tasks/.gitkeep +0 -0
  63. data/spec/dummy/log/.gitkeep +0 -0
  64. data/spec/dummy/public/404.html +26 -0
  65. data/spec/dummy/public/422.html +26 -0
  66. data/spec/dummy/public/500.html +25 -0
  67. data/spec/dummy/public/favicon.ico +0 -0
  68. data/spec/dummy/public/index.html +241 -0
  69. data/spec/dummy/public/robots.txt +5 -0
  70. data/spec/dummy/salida/users_create_response.json +12 -0
  71. data/spec/dummy/script/rails +6 -0
  72. data/spec/dummy/test/fixtures/.gitkeep +0 -0
  73. data/spec/dummy/test/functional/.gitkeep +0 -0
  74. data/spec/dummy/test/integration/.gitkeep +0 -0
  75. data/spec/dummy/test/performance/browsing_test.rb +12 -0
  76. data/spec/dummy/test/test_helper.rb +13 -0
  77. data/spec/dummy/test/unit/.gitkeep +0 -0
  78. data/spec/dummy/vendor/assets/javascripts/.gitkeep +0 -0
  79. data/spec/dummy/vendor/assets/stylesheets/.gitkeep +0 -0
  80. data/spec/dummy/vendor/plugins/.gitkeep +0 -0
  81. data/spec/features/action_spec.rb +212 -0
  82. data/spec/features/index_spec.rb +64 -0
  83. data/spec/lib/action_doc_spec.rb +202 -0
  84. data/spec/lib/config_spec.rb +186 -0
  85. data/spec/lib/controller_extractor_spec.rb +77 -0
  86. data/spec/lib/http_response_spec.rb +63 -0
  87. data/spec/lib/param_errors_spec.rb +69 -0
  88. data/spec/lib/rake_spec.rb +25 -0
  89. data/spec/lib/rapidoc_spec.rb +39 -0
  90. data/spec/lib/resource_doc_spec.rb +131 -0
  91. data/spec/lib/resources_extractor_spec.rb +105 -0
  92. data/spec/lib/routes_doc_spec.rb +150 -0
  93. data/spec/lib/templates_generator_spec.rb +90 -0
  94. data/spec/lib/yard_parser_spec.rb +55 -0
  95. data/spec/spec_helper.rb +10 -0
  96. metadata +307 -0
@@ -0,0 +1,4 @@
1
+ project_name: "Project Name"
2
+ base_url: "http://api.mydomain.com"
3
+ company: "My company"
4
+ year: 2013
@@ -0,0 +1,54 @@
1
+ module Rapidoc
2
+
3
+ ##
4
+ # This class open controller file and get all documentation blocks.
5
+ # Lets us check if any of this blocks is a `rapidoc` block
6
+ # and return it using `json` format.
7
+ #
8
+ # Rapidoc blocks must:
9
+ # - have YAML format
10
+ # - begin with `#=begin action` for actions description
11
+ # - begin with `#=begin resource` for resource description
12
+ # - end with `#=end`
13
+ #
14
+ class ControllerExtractor
15
+
16
+ def initialize( controller_file )
17
+ lines = IO.readlines( controller_dir( controller_file ) )
18
+ blocks = extract_blocks( lines )
19
+
20
+ @resource_info = YamlParser.extract_resource_info( lines, blocks )
21
+ @actions_info = YamlParser.extract_actions_info( lines, blocks )
22
+ end
23
+
24
+ def get_actions_info
25
+ @actions_info
26
+ end
27
+
28
+ def get_action_info( action )
29
+ @actions_info.select{ |info| info['action'] == action.to_s }.first
30
+ end
31
+
32
+ def get_resource_info
33
+ @resource_info
34
+ end
35
+
36
+ def get_controller_info
37
+ { "description" => @resource_info["description"], "actions" => @actions_info }
38
+ end
39
+
40
+ private
41
+
42
+ ##
43
+ # Gets init and end lines of each comment block
44
+ #
45
+ def extract_blocks( lines )
46
+ init_doc_lines = lines.each_index.select{ |i| lines[i].include? "=begin" }
47
+ end_doc_lines = lines.each_index.select{ |i| lines[i].include? "=end" }
48
+
49
+ blocks = init_doc_lines.each_index.map do |i|
50
+ { :init => init_doc_lines[i], :end => end_doc_lines[i] }
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+
3
+ module Rapidoc
4
+
5
+ ##
6
+ # Represent http status code with code and description.
7
+ # Also include bootstrap label for code.
8
+ #
9
+ class HttpResponse
10
+
11
+ attr_reader :code, :description, :label
12
+
13
+ def initialize( code )
14
+ @code = code
15
+ @description = get_description
16
+ @label = get_label
17
+ end
18
+
19
+ def get_description
20
+ case @code
21
+ when 200
22
+ 'OK'
23
+ when 201
24
+ 'Created'
25
+ when 204
26
+ 'No content'
27
+ when 401
28
+ 'Unauthorized'
29
+ when 403
30
+ 'Forbidden'
31
+ when 404
32
+ 'Not found'
33
+ when 422
34
+ 'Unprocessable Entity'
35
+ else
36
+ ''
37
+ end
38
+ end
39
+
40
+ def get_label
41
+ case @code
42
+ when 200
43
+ 'label label-info'
44
+ when 201
45
+ 'label label-success'
46
+ when 204
47
+ 'label label-info2'
48
+ when 401
49
+ 'label label-warning'
50
+ when 403
51
+ 'label label-warning2'
52
+ when 422
53
+ 'label label-important'
54
+ when 404
55
+ 'label label-inverse'
56
+ else
57
+ 'label'
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,43 @@
1
+ module Rapidoc
2
+
3
+ module ParamErrors
4
+
5
+ def get_error_info( object, type )
6
+ case type
7
+ when 'required'
8
+ get_required_error_info object
9
+ when 'inclusion'
10
+ get_inclusion_error_info object
11
+ else
12
+ nil
13
+ end
14
+ end
15
+
16
+ def get_required_error_info( object )
17
+ if default_errors and default_errors.include? "required"
18
+ get_default_error_info( object, "required" )
19
+ else
20
+ { "object" => object,
21
+ "message" => "blank",
22
+ "description" => "This parameter is mandatory" }
23
+ end
24
+ end
25
+
26
+ def get_inclusion_error_info( object )
27
+ if default_errors and default_errors.include? "inclusion"
28
+ get_default_error_info( object, "inclusion" )
29
+ else
30
+ { "object" => object,
31
+ "message" => "inclusion",
32
+ "description" => "This parameter is not in the collection" }
33
+ end
34
+ end
35
+
36
+ def get_default_error_info( object, type )
37
+ { 'object' => object,
38
+ 'message' => default_errors[type]['message'], # config function
39
+ 'description' => default_errors[type]['description']
40
+ }
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+
3
+ module Rapidoc
4
+
5
+ ##
6
+ # This class includes all info about a resource
7
+ # To extract info from controller file uses ControllerExtractor
8
+ # It includes an array of ActionDoc with each action information
9
+ #
10
+ class ResourceDoc
11
+ attr_reader :name, :description, :controller_file, :actions_doc
12
+
13
+ ##
14
+ # @param resource_name [String] resource name
15
+ # @param routes_doc [RoutesDoc] routes documentation
16
+ #
17
+ def initialize( resource_name, routes_actions_info )
18
+ @name = resource_name.to_s
19
+ @controller_file = name.to_s.pluralize + '_controller.rb'
20
+
21
+ generate_info routes_actions_info
22
+ end
23
+
24
+ ##
25
+ # Names with '/' caracter produce problems in html ids
26
+ #
27
+ def simple_name
28
+ return self.name.delete '/'
29
+ end
30
+
31
+ private
32
+
33
+ ##
34
+ # Create description and actions_doc
35
+ #
36
+ def generate_info( routes_info )
37
+ if routes_info
38
+ extractor = get_controller_extractor
39
+ @description = extractor.get_resource_info['description'] if extractor
40
+ @actions_doc = get_actions_doc( routes_info, extractor )
41
+ end
42
+ end
43
+
44
+ ##
45
+ # @return [ControllerExtractor] extractor that allow read controller files
46
+ # and extract action and resource info from them
47
+ #
48
+ def get_controller_extractor
49
+ if File.exists? controller_dir( @controller_file )
50
+ ControllerExtractor.new @controller_file
51
+ else
52
+ nil
53
+ end
54
+ end
55
+
56
+ ##
57
+ # @return [Array] all the resource ActionDoc
58
+ #
59
+ def get_actions_doc( routes_actions_info, extractor )
60
+ routes_actions_info.map do |route_info|
61
+ controller_info = extractor ? extractor.get_action_info( route_info[:action] ) : nil
62
+ ActionDoc.new( route_info, controller_info, examples_dir )
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,42 @@
1
+ module Rapidoc
2
+
3
+ ##
4
+ # This module get resources info.
5
+ #
6
+ # Info of each route include:
7
+ # - method
8
+ # - action
9
+ # - url
10
+ # - controller_file
11
+ #
12
+ module ResourcesExtractor
13
+
14
+ ##
15
+ # Reads 'rake routes' output and gets the routes info
16
+ # @return [RoutesDoc] class with routes info
17
+ #
18
+ def get_routes_doc
19
+ routes_doc = RoutesDoc.new
20
+ routes = Dir.chdir( ::Rails.root.to_s ) { `rake routes` }
21
+
22
+ routes.split("\n").each do |entry|
23
+ routes_doc.add_route( entry )
24
+ end
25
+
26
+ routes_doc
27
+ end
28
+
29
+ ##
30
+ # Create new ResourceDoc for each resource extracted from RoutesDoc
31
+ # @return [Array] ResourceDoc array
32
+ #
33
+ def get_resources
34
+ routes_doc = get_routes_doc
35
+ resources_names = routes_doc.get_resources_names - resources_black_list
36
+
37
+ resources_names.map do |resource|
38
+ ResourceDoc.new( resource, routes_doc.get_actions_route_info( resource ) )
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,96 @@
1
+ # encoding: utf-8
2
+
3
+ module Rapidoc
4
+
5
+ ##
6
+ # This class let us manage resources and actions extracted from 'rake routes'
7
+ #
8
+ class RoutesDoc
9
+ def initialize
10
+ @resources_routes = {}
11
+ end
12
+
13
+ def add_route( route )
14
+ if route.split.size == 4
15
+ method, url, controller_action = route.split.slice(1, 3)
16
+ else
17
+ method, url, controller_action = route.split
18
+ end
19
+
20
+ add_resource_route( method, url, controller_action )
21
+ end
22
+
23
+ def get_resources_names
24
+ @resources_routes.keys.sort
25
+ end
26
+
27
+ def get_resource_actions_names( resource )
28
+ @resources_routes[resource.to_sym].map{ |route| route[:action] }.uniq
29
+ end
30
+
31
+ def get_actions_route_info( resource )
32
+ get_resource_actions_names( resource ).map do |action|
33
+ get_action_route_info( resource, action )
34
+ end
35
+ end
36
+
37
+ def get_action_route_info( resource, action )
38
+ urls = []
39
+ controllers = []
40
+ methods = []
41
+
42
+ # compact and generate action info from all routes info of resource
43
+ @resources_routes[resource.to_sym].each do |route|
44
+ if route[:action] == action.to_s
45
+ urls.push route[:url]
46
+ controllers.push route[:controller]
47
+ methods.push route[:method]
48
+ end
49
+ end
50
+
51
+ return {
52
+ resource: resource.to_s,
53
+ action: action.to_s,
54
+ method: methods.uniq.first,
55
+ urls: urls.uniq,
56
+ controllers: controllers.uniq
57
+ }
58
+ end
59
+
60
+ private
61
+
62
+ ##
63
+ # Add new route info to resource routes array with correct format
64
+ #
65
+ def add_resource_route( method, url, controller_action )
66
+ #resource = get_resource_name( url )
67
+ resource = controller_action.split('#').first
68
+ info = {
69
+ resource: resource,
70
+ action: controller_action.split('#').last,
71
+ method: method,
72
+ url: url ,
73
+ controller: controller_action.split('#').first
74
+ }
75
+
76
+ @resources_routes[resource.to_sym] ||= []
77
+ @resources_routes[resource.to_sym].push( info )
78
+ end
79
+
80
+ ##
81
+ # Extract resource name from url
82
+ #
83
+ def get_resource_name( url )
84
+ new_url = url.gsub( '(.:format)', '' )
85
+
86
+ return $1 if new_url =~ /\/(\w+)\/:id$/ # /users/:id (users)
87
+ return $1 if new_url =~ /\/(\w+)\/:id\/edit$/ # /users/:id/edit (users)
88
+ return $1 if new_url =~ /^\/(\w+)$/ # /users (users)
89
+ return $1 if new_url =~ /\/:\w*id\/(\w+)$/ # /users/:id/images (images)
90
+ return $1 if new_url =~ /\/:\w*id\/(\w+)\/\w+$/ # /users/:id/config/edit (users)
91
+ return $1 if new_url =~ /^\/(\w+)\/\w+$/ # /users/edit (users)
92
+ return $1 if new_url =~ /\/(\w+)\/\w+\/\w+$/ # /users/password/edit (users)
93
+ return url
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,170 @@
1
+ <!DOCTYPE html>
2
+ <html lang="es">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Rapidoc - Action</title>
6
+ <meta name="description" content="Artículo en GenbetaDev sobre Bootstrap 2.0">
7
+ <meta name="author" content="Rafael Jurado González" >
8
+ <link href="../assets/css/bootstrap.min.css" rel="stylesheet">
9
+ <link href="../assets/css/rapidoc.css" rel="stylesheet">
10
+ <link href="../assets/css/bootstrap-responsive.min.css" rel="stylesheet">
11
+ <script src="../assets/js/jquery-1.9.0.min.js"></script>
12
+ <script src="../assets/js/bootstrap.min.js"></script>
13
+ <script src="../assets/js/json2.js"></script>
14
+ <style>
15
+ body { padding-top: 60px; }
16
+ </style>
17
+ </head>
18
+ <body onload="prueba()">
19
+
20
+ <div class="navbar navbar-fixed-top">
21
+ <div class="navbar-inner">
22
+ <div class="container">
23
+ <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
24
+ <span class="icon-bar"></span>
25
+ <span class="icon-bar"></span>
26
+ <span class="icon-bar"></span>
27
+ </a>
28
+ <a class="brand" href="#">{{info.project_name}}</a>
29
+ <div class="nav-collapse">
30
+ <ul class="nav">
31
+ <li><a href="../index.html">Resources</a></li>
32
+ <li class="active"><a href="#">Action</a></li>
33
+ <li><a target="_blank" href="https://github.com/drinor/rapidoc.git">Contact</a></li>
34
+ </ul>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ </div>
39
+
40
+ <div class="container">
41
+ <div class="page-header">
42
+ <h1>{{action.resource}}</h1>
43
+ </div>
44
+
45
+ <ul class="nav nav-tabs" id="myTab">
46
+ <li class="active"><a href="#home">Home</a></li>
47
+ {{#if action.params}}<li><a href="#params">Params</a></li>{{/if}}
48
+ {{#if action.errors}}<li><a href="#errors">Errors</a></li>{{/if}}
49
+ {{#if action.example_req}}<li><a href="#request">Request</a></li>{{/if}}
50
+ {{#if action.example_res}}<li><a href="#response">Response</a></li>{{/if}}
51
+ </ul>
52
+
53
+ <div class="tab-content">
54
+ <div class="tab-pane active my_tab" id="home">
55
+ <p>
56
+ {{action.description}}
57
+ </p>
58
+ <table class="table table-hover action-options">
59
+ <tr>
60
+ <td>Action</td><td>{{action.action}}</td>
61
+ </tr>
62
+ <tr>
63
+ <td>Method</td><td>{{action.action_method}}</td>
64
+ </tr>
65
+ <tr>
66
+ <td class="routes">Required authentication</td>
67
+ <td>
68
+ {{#if action.authentication}}
69
+ <i class="icon-ok"></i>
70
+ {{else}}
71
+ <i class="icon-remove"></i>
72
+ {{/if}}
73
+ </td>
74
+ </tr>
75
+ <tr>
76
+ <td>Response formats</td><td>{{action.response_formats}}</td>
77
+ </tr>
78
+ </table>
79
+
80
+
81
+ <h4 class="pad_top">Resource URL</h4>
82
+ <pre>{{action.urls}}</pre>
83
+
84
+ <h4 class="pad_top">Response Status</h4>
85
+ <table class="table table-hover">
86
+ {{#each action.http_responses}}
87
+ <tr>
88
+ <td class="routes"><span class="{{this.label}}">{{this.code}}</span></td>
89
+ <td>{{this.description}}</td>
90
+ </tr>
91
+ {{/each}}
92
+ </table>
93
+ </div>
94
+
95
+ {{#if action.params}}
96
+ <div class="tab-pane my_tab" id="params">
97
+ <table class="table table-hover" id="table-params">
98
+ {{#each action.params}}
99
+ <tr>
100
+ <td>
101
+ <strong>{{this.name}}</strong></br>
102
+ {{#if this.required}}
103
+ <span class="small">required</span>
104
+ {{else}}
105
+ <span class="small">optional</span>
106
+ {{/if}}
107
+ </td>
108
+ <td>
109
+ {{#if this.inclusion}}
110
+ [ {{this.inclusion}} ]
111
+ {{else}}
112
+ {{this.type}}
113
+ {{/if}}
114
+ </td>
115
+ <td>{{this.description}}</td>
116
+ </tr>
117
+ {{/each}}
118
+ <tr><td></td><td><td></td></tr>
119
+ </table>
120
+ </div>
121
+ {{/if}}
122
+
123
+ {{#if action.errors}}
124
+ <div class="tab-pane my_tab" id="errors">
125
+ <table class="table table-hover" id="table-errors">
126
+ <tr>
127
+ <th>Object</th>
128
+ <th>Message</th>
129
+ <th>Description</th>
130
+ </tr>
131
+ {{#each action.errors}}
132
+ <tr>
133
+ <td>{{this.object}}</td>
134
+ <td>{{this.message}}</td>
135
+ <td>{{this.description}}</td>
136
+ </tr>
137
+ {{/each}}
138
+ </table>
139
+ </div>
140
+ {{/if}}
141
+
142
+ {{#if action.example_req}}
143
+ <div class="tab-pane my_tab" id="request">
144
+ <pre>{{action.example_req }}</pre>
145
+ </div>
146
+ {{/if}}
147
+
148
+ {{#if action.example_res}}
149
+ <div class="tab-pane my_tab" id="response">
150
+ <pre>{{action.example_res }}</pre>
151
+ </div>
152
+ {{/if}}
153
+ </div>
154
+
155
+ <script>
156
+ $(function () {
157
+ $('#myTab a:first').tab('show');
158
+ })
159
+
160
+ $('#myTab a').click(function (e) {
161
+ e.preventDefault();
162
+ $(this).tab('show');
163
+ })
164
+ </script>
165
+ <footer>
166
+ <p>{{info.company}} {{info.year}}</p>
167
+ </footer>
168
+ </div>
169
+ </body>
170
+ </html>