reqres_rspec 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fff2afe79f68435c6b2427a0956a13b7fcc745df
4
- data.tar.gz: 8e5a3cc6f86c39a9298fd32eb95d3ab3173736db
3
+ metadata.gz: 6ab09afc849b19636a0c584016db5aa14da06d70
4
+ data.tar.gz: cf117741746332f50063c8bcd3a8224901e1fc6a
5
5
  SHA512:
6
- metadata.gz: 2e2d2c9c66a38dc962c9e99e7bcbfda25851ce1de6fc9481289708e14fb11edf5180c49ee1458b8ddd541230fd13d71853ee29ee6b6a8e0acb33eb68b9c1c7f2
7
- data.tar.gz: 38831de650a0ace22d13ccbcfa96b19ba9694d1c80f12954c6974be12d6e4dfd224538a042dd52ffd487ce87d227afe51385189c594c2eaa0e3fd7f3d4e36286
6
+ metadata.gz: a295bee73cb02b837727c841621946984d233c241a2f85a4700d7d6d4772aeb1809d4c84aa58533785df741b49b6272085a36a0c698f70b894800e77fd95ea72
7
+ data.tar.gz: d35cef61612ac710074bba1700ef3023636225fb9ee0328c2eecb348936f3dce3a342c90cfee668b8ea73364ffcd800a6273e31296c8fa1682f2ff018df14781
@@ -1,14 +1,198 @@
1
- require 'reqres_rspec'
2
-
3
1
  module ReqresRspec
4
- module Collector
2
+ class Collector
3
+ # Contains spec values read from rspec example, request and response
4
+ attr_accessor :records
5
+
6
+ # response headers contain many unnecessary information,
7
+ # everything from this list will be stripped
8
+ EXCLUDE_RESPONSE_HEADER_PATTERNS = %w[
9
+ X-Frame-Options
10
+ X-XSS-Protection
11
+ X-Content-Type-Options
12
+ X-UA-Compatible
13
+ ]
14
+
15
+ # request headers contain many unnecessary information,
16
+ # everything that match items from this list will be stripped
17
+ EXCLUDE_REQUEST_HEADER_PATTERNS = %w[
18
+ rack.
19
+ action_dispatch
20
+ REQUEST_METHOD
21
+ SERVER_NAME
22
+ SERVER_PORT
23
+ QUERY_STRING
24
+ SCRIPT_NAME
25
+ CONTENT_LENGTH
26
+ HTTPS
27
+ HTTP_HOST
28
+ HTTP_USER_AGENT
29
+ REMOTE_ADDR
30
+ PATH_INFO
31
+ ]
32
+
33
+ def initialize
34
+ self.records = []
35
+ end
36
+
5
37
  # collects spec data for further processing
6
- def Collector.collect(spec, request, response)
7
- record = {
8
- title: 'title',
38
+ def collect(spec, request, response)
39
+ self.records << {
40
+ title: spec.example.full_description,
41
+ description: get_action_description(request.env['action_dispatch.request.parameters']['controller'], request.env['action_dispatch.request.parameters']['action']),
42
+ params: get_action_params(request.env['action_dispatch.request.parameters']['controller'], request.env['action_dispatch.request.parameters']['action']),
43
+ request_path: get_symbolized_path(request),
44
+ request: {
45
+ host: request.host,
46
+ url: request.url,
47
+ path: request.path,
48
+ method: request.request_method,
49
+ query_parameters: request.env['action_dispatch.request.parameters'].reject { |p| %w[controller action].include? p },
50
+ backend_parameters: request.env['action_dispatch.request.parameters'].reject { |p| !%w[controller action].include? p },
51
+ body: request.body.read,
52
+ content_length: request.content_length,
53
+ content_type: request.content_type,
54
+ headers: read_request_headers(request),
55
+ accept: request.accept,
56
+ },
57
+ response: {
58
+ code: response.status,
59
+ body: response.body,
60
+ headers: read_response_headers(response),
61
+ }
9
62
  }
63
+ end
64
+
65
+ private
66
+
67
+ # read and cleanup response headers
68
+ # returns Hash
69
+ def read_response_headers(response)
70
+ headers = response.headers
71
+ EXCLUDE_RESPONSE_HEADER_PATTERNS.each do |pattern|
72
+ headers = headers.reject { |h| h if h.starts_with? pattern }
73
+ end
74
+ headers
75
+ end
76
+
77
+ # read and cleanup request headers
78
+ # returns Hash
79
+ def read_request_headers(request)
80
+ headers = {}
81
+ request.env.keys.each do |key|
82
+ headers.merge!(key => request.env[key]) if EXCLUDE_REQUEST_HEADER_PATTERNS.all? { |p| !key.starts_with? p }
83
+ end
84
+ headers
85
+ end
86
+
87
+ # replace each first occurrence of param's value in the request path
88
+ #
89
+ # example
90
+ # request path = /api/users/123
91
+ # id = 123
92
+ # symbolized path => /api/users/:id
93
+ #
94
+ def get_symbolized_path(request)
95
+ request_path = request.path
96
+
97
+ request.env['action_dispatch.request.parameters'].
98
+ reject { |param| %w[controller action].include? param }.
99
+ each do |key, value|
100
+ if value.is_a? String
101
+ request_path = request_path.sub(value, ":#{key}") if request_path.index(value) >= 0
102
+ end
103
+ end
104
+
105
+ request_path
106
+ end
107
+
108
+ # returns action comments taken from controller file
109
+ # example TODO
110
+ def get_action_comments(controller, action)
111
+ lines = File.readlines(File.join(Rails.root, 'app', 'controllers', "#{controller}_controller.rb"))
112
+
113
+ action_line = nil
114
+ lines.each_with_index do |line, index|
115
+ if line.match /\s*def #{action}/ # def show
116
+ action_line = index
117
+ break
118
+ end
119
+ end
120
+
121
+ if action_line
122
+ comment_lines = []
123
+ was_comment = true
124
+ while action_line > 0 && was_comment
125
+ action_line -= 1
126
+
127
+ if lines[action_line].match /\s*#/
128
+ comment_lines << lines[action_line].strip
129
+ else
130
+ was_comment = false
131
+ end
132
+ end
133
+
134
+ comment_lines.reverse
135
+ else
136
+ 'not found'
137
+ end
138
+ end
139
+
140
+ # returns description action comments
141
+ # example TODO
142
+ def get_action_description(controller, action)
143
+ comment_lines = get_action_comments(controller, action)
144
+
145
+ description = []
146
+ comment_lines.each_with_index do |line, index|
147
+ if line.match /\s*#\s*@description/ # @description blah blah
148
+ description << line.gsub(/\A\s*#\s*@description/, '').strip
149
+ comment_lines[(index + 1)..-1].each do |multiline|
150
+ if !multiline.match /\s*#\s*@params/
151
+ description << multiline.gsub(/\A\s*#\s*/, '').strip
152
+ else
153
+ break
154
+ end
155
+ end
156
+ end
157
+ end
158
+
159
+ description.join ' '
160
+ end
161
+
162
+ # returns params action comments
163
+ # example TODO
164
+ def get_action_params(controller, action)
165
+ comment_lines = get_action_comments(controller, action)
166
+
167
+ text_params = []
168
+ last_new_param_index = nil
169
+ comment_lines.each_with_index do |line, index|
170
+ if line.match /\s*#\s*@params/ # @params id required Integer blah blah
171
+ last_new_param_index = index
172
+ text_params << line.gsub(/\A\s*#\s*@params/, '').strip
173
+ elsif last_new_param_index && last_new_param_index == index - 1
174
+ text_params.last << " #{line.gsub(/\A\s*#\s*/, '').strip}"
175
+ end
176
+ end
177
+
178
+ params = []
179
+
180
+ text_params.each do |param|
181
+ match_data = param.match /(?<name>[a-z0-9A-Z_\[\]]+)?\s*(?<required>required)?\s*(?<type>Integer|Boolean|String|Text|Float|Date|DateTime|File)?\s*(?<description>.*)/
182
+
183
+ if match_data
184
+ params << {
185
+ name: match_data[:name],
186
+ required: match_data[:required],
187
+ type: match_data[:type],
188
+ description: match_data[:description],
189
+ }
190
+ else
191
+ params << { description: param }
192
+ end
193
+ end
10
194
 
11
- ReqresRspec.records << record
195
+ params
12
196
  end
13
197
  end
14
198
  end
@@ -0,0 +1,24 @@
1
+ module ReqresRspec
2
+ module Generators
3
+ class Pdf
4
+ # generates PDF file from existing HTML docs
5
+ # TODO: more info
6
+ def generate
7
+ wkhtmltopdf_path = '/Applications/wkhtmltopdf.app/Contents/MacOS/wkhtmltopdf'
8
+ html_docs_root = File.join(Rails.root, 'docs')
9
+ pdf_doc_path = File.join(Rails.root, 'docs', "spec_#{Time.now.strftime("%d-%h-%Y_%H-%M")}.pdf")
10
+
11
+ if File.exists?(wkhtmltopdf_path)
12
+ files = Dir["#{html_docs_root}/rspec_docs_*.html"]
13
+ files_arg = files.map { |f| f if f =~ /\/rspec_docs_\d+\.html/ }.compact.sort.join(' ')
14
+
15
+ `#{wkhtmltopdf_path} #{files_arg} #{pdf_doc_path}`
16
+
17
+ puts "saved to #{pdf_doc_path}" if File.exists? pdf_doc_path
18
+ else
19
+ puts 'ERROR: wkhtmltopdf app not installed! Please check http://code.google.com/p/wkhtmltopdf/ for more info'
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,3 @@
1
1
  module ReqresRspec
2
- VERSION = "0.0.1"
2
+ VERSION = '0.0.2'
3
3
  end
@@ -0,0 +1,55 @@
1
+ module ReqresRspec
2
+ module Writers
3
+ class Html
4
+ def initialize(records)
5
+ @records = records
6
+ end
7
+
8
+ def write
9
+ cleanup
10
+ generate_header
11
+ generate_specs
12
+ end
13
+
14
+ private
15
+
16
+ # deletes previous version of HTML docs
17
+ # TODO: more info
18
+ def cleanup
19
+ FileUtils.rm_rf(Dir.glob("#{Rails.root}/docs/rspec_docs_*.html"), secure: true)
20
+ end
21
+
22
+ # generates contents of HTML docs
23
+ # TODO: more info
24
+ def generate_header
25
+ tpl_path = File.join(File.dirname(__FILE__), 'templates', 'header.erb')
26
+ rendered_doc = ERB.new(File.open(tpl_path).read).result(binding)
27
+
28
+ path = File.join(Rails.root, 'docs', 'rspec_docs_00000.html')
29
+ file = File.open(path, 'w')
30
+ file.write(rendered_doc)
31
+ file.close
32
+ puts "Reqres::Writers::Html saved doc header to #{path}"
33
+ end
34
+
35
+ # generates each separate spec example doc
36
+ # TODO: more info
37
+ def generate_specs
38
+ tpl_path = File.join(File.dirname(__FILE__), 'templates', 'spec.erb')
39
+
40
+ @records.each_with_index do |record, index|
41
+ @record = record
42
+ @index = index + 1
43
+
44
+ rendered_doc = ERB.new(File.open(tpl_path).read).result(binding)
45
+
46
+ path = File.join(Rails.root, 'docs', "rspec_docs_#{('0000' + (@index).to_s)[-5, 5]}.html")
47
+ file = File.open(path, 'w')
48
+ file.write(rendered_doc)
49
+ file.close
50
+ puts "Reqres::Writers::Html saved doc spec to #{path}"
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,72 @@
1
+ <html>
2
+ <head>
3
+ <title><%= Rails.application.class.to_s.sub('::Application', '') %> API Documentation</title>
4
+ <link href='http://fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'>
5
+ <link href='http://fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css'>
6
+ <style>
7
+ body {
8
+ color: #333333;
9
+ font-family: 'Verdana', 'Droid Sans', sans-serif;
10
+ font-size: 14px;
11
+ padding: 0;
12
+ margin: 0;
13
+ }
14
+ body *{
15
+ -webkit-box-sizing: border-box;
16
+ -moz-box-sizing: border-box;
17
+ box-sizing: border-box;
18
+ }
19
+ .container {
20
+ padding: 40px;
21
+ }
22
+ h1, h2, ul {
23
+ margin: 0 0 10px;
24
+ padding: 0;
25
+ }
26
+ h1 {
27
+ font-size: 2.286em;
28
+ }
29
+ h2 {
30
+ font-size: 2em;
31
+ }
32
+ a {
33
+ color: #1d96d2;
34
+ text-decoration: none;
35
+ }
36
+ ul {
37
+ padding-left: 0;
38
+ list-style: none;
39
+ }
40
+ li{
41
+ margin-bottom: 4px;
42
+ }
43
+
44
+ ol{
45
+ margin: 0 0 10px 0;
46
+ padding: 0 10px 0 50px;
47
+ }
48
+ ol li{
49
+ white-space: pre;
50
+ border-left: 1px solid #ccc;
51
+ margin: 0;
52
+ padding: 0 4px;
53
+ }
54
+ </style>
55
+ </head>
56
+ <body>
57
+ <div class="container">
58
+ <h1><%= Rails.application.class.to_s.sub('::Application', '') %> API Documentation</h1>
59
+ <p>Generated <%= Time.now.strftime('%d %B %Y at %H:%m:%S')%></p>
60
+ <ul>
61
+ <% @records.each_with_index do |record, index| %>
62
+ <li>
63
+ <%= index+1 %>.
64
+ <a href="rspec_docs_<%= ('0000' + (index + 1).to_s)[-5, 5] %>.html">
65
+ <%= record[:title] %>
66
+ </a>
67
+ </li>
68
+ <% end %>
69
+ </ul>
70
+ </div>
71
+ </body>
72
+ </html>
@@ -0,0 +1,213 @@
1
+ <html>
2
+ <head>
3
+ <title><%= Rails.application.class.to_s.sub('::Application', '') %> API Documentation</title>
4
+ <link href='http://fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'>
5
+ <link href='http://fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css'>
6
+ <style>
7
+ body {
8
+ color: #333333;
9
+ font-family: 'Verdana', 'Droid Sans', sans-serif;
10
+ font-size: 14px;
11
+ padding: 0;
12
+ margin: 0;
13
+ }
14
+ body *{
15
+ -webkit-box-sizing: border-box;
16
+ -moz-box-sizing: border-box;
17
+ box-sizing: border-box;
18
+ }
19
+ .container {
20
+ padding: 40px;
21
+ }
22
+ h1, h2, ul {
23
+ margin: 0 0 10px;
24
+ padding: 0;
25
+ }
26
+ h1 {
27
+ font-size: 2.286em;
28
+ }
29
+ h2 {
30
+ font-size: 2em;
31
+ }
32
+ h3{
33
+ font-size: 1.57em;
34
+ margin: 14px 0 0px;
35
+ padding: 0;
36
+ }
37
+ h4{
38
+ font-size: 1.15em;
39
+ margin: 14px 0 0 0;
40
+ padding: 0;
41
+ }
42
+ h5{
43
+ font-size: 1em;
44
+ margin: 10px 0 0 0;
45
+ padding: 0;
46
+ }
47
+ a {
48
+ color: #1d96d2;
49
+ text-decoration: none;
50
+ }
51
+ .required {
52
+ color: #f00;
53
+ }
54
+ ul {
55
+ padding-left: 0;
56
+ list-style: none;
57
+ }
58
+ li{
59
+ margin-bottom: 4px;
60
+ }
61
+ .table{
62
+ border: 1px solid #cccccc;
63
+ border-radius: 4px;
64
+ overflow: hidden;
65
+ }
66
+ table{
67
+ width: 100%;
68
+ font-size: 1em;
69
+ padding: 0;
70
+ margin: 0;
71
+ border-collapse: collapse;
72
+ border-spacing: 0px;
73
+ }
74
+ tr{
75
+ border-top: 1px solid #cccccc;
76
+ }
77
+ tr:first-child{
78
+ border-top: 0
79
+ }
80
+ th, td{
81
+ margin: 0;
82
+ padding: 5px 8px;
83
+ text-align: left;
84
+ border-left: 1px solid #cccccc;
85
+ font-size: 1em;
86
+ }
87
+ th:first-child, td:first-child{
88
+ border-left: 0;
89
+ }
90
+ td{
91
+ background: #f9f9f9;
92
+ }
93
+ tr:nth-child(2n+1) td{
94
+ background: #ffffff;
95
+ }
96
+ td i{
97
+ font-style: normal;
98
+ font-weight: 300;
99
+ float: right;
100
+ }
101
+
102
+ code, ol{
103
+ display: block;
104
+ border-radius: 6px;
105
+ border: 1px solid #cccccc;
106
+ width: 100%;
107
+ background: #f5f5f5;
108
+ font-family: 'Courier New','Droid Sans Mono', monospace;
109
+ font-size: 0.85em;
110
+ font-weight: 700;
111
+ }
112
+ code{
113
+ white-space: pre-line;
114
+ padding: 10px;
115
+ font-size: 0.85em;
116
+ }
117
+ code i{
118
+ font-weight: 400;
119
+ font-style: normal;
120
+ }
121
+ ol{
122
+ margin: 0 0 10px 0;
123
+ padding: 0 10px 0 50px;
124
+ }
125
+ ol li{
126
+ white-space: pre;
127
+ border-left: 1px solid #ccc;
128
+ margin: 0;
129
+ padding: 0 4px;
130
+ }
131
+ </style>
132
+ </head>
133
+ <body>
134
+ <div class="container">
135
+ <h3>
136
+ <a href="rspec_docs_00000.html">&#9650</a>
137
+ <a id="<%= @index %>"></a><%= @index %>. <%= @record[:title] %>
138
+ </h3>
139
+
140
+ <p><%= @record[:description] %></p>
141
+
142
+ <code><%= @record[:request][:method] %> <%= @record[:request_path] %></code>
143
+
144
+ <% if @record[:params].size > 0 %>
145
+ <h5>Parameters</h5>
146
+ <div class="table">
147
+ <table>
148
+ <tr>
149
+ <th>Name</th>
150
+ <th>Type</th>
151
+ <th>Description</th>
152
+ </tr>
153
+ <% @record[:params].each do |param| %>
154
+ <tr>
155
+ <td><%= param[:name] %> <i class="required"><%= param[:required] %></i></td>
156
+ <td><%= param[:type] %></td>
157
+ <td><%= param[:description] %></td>
158
+ </tr>
159
+ <% end %>
160
+ </table>
161
+ </div>
162
+ <% end %>
163
+
164
+ <h4>Request</h4>
165
+
166
+ <% if @record[:request][:headers].keys.count > 0 %>
167
+ <h5>Headers</h5>
168
+ <code><%= @record[:request][:headers].collect { |k, v| "#{k}: <i>#{v}</i>"}.join("\n")%></code>
169
+ <% end %>
170
+
171
+ <h5>Route</h5>
172
+ <code><%= @record[:request][:method] %> <%= @record[:request][:path] %></code>
173
+
174
+ <h5>Query parameters</h5>
175
+ <code><%= @record[:request][:query_parameters] %></code>
176
+
177
+ <h5><a href="http://curl.haxx.se/docs/manpage.html">CURL</a> Example</h5>
178
+ <code>TODO</code>
179
+
180
+ <% if @record[:request][:body].present? %>
181
+ <h5>Body</h5>
182
+ <ol>
183
+ <% @record[:request][:body].split("\n").each do |line| %>
184
+ <li><%= line %></li>
185
+ <% end %>
186
+ </ol>
187
+ <% end %>
188
+
189
+ <h4>Response</h4>
190
+
191
+ <% if @record[:response][:headers].count > 0 %>
192
+ <h5>Headers</h5>
193
+ <code><%= @record[:response][:headers].collect { |k, v| "#{k}: <i>#{v}</i>"}.join("\n")%></code>
194
+ <% end %>
195
+
196
+ <h5>Status code (<a href="http://en.wikipedia.org/wiki/List_of_HTTP_status_codes">wiki</a>)</h5>
197
+ <code>HTTP <%= @record[:response][:code] %></code>
198
+
199
+ <% if @record[:response][:body].present? %>
200
+ <h5>Body</h5>
201
+ <ol>
202
+ <% JSON.pretty_generate(
203
+ JSON.parse(
204
+ @record[:response][:body]
205
+ )
206
+ ).split("\n").each do |line| %>
207
+ <li><%= line %></li>
208
+ <% end %>
209
+ </ol>
210
+ <% end %>
211
+ </div>
212
+ </body>
213
+ </html>
data/lib/reqres_rspec.rb CHANGED
@@ -1,41 +1,27 @@
1
- # to enable mattr_accessor
2
- require "active_support/core_ext/module/attribute_accessors"
1
+ require 'reqres_rspec/version'
2
+ require 'reqres_rspec/collector'
3
+ require 'reqres_rspec/writers/html'
4
+ require 'reqres_rspec/generators/pdf'
3
5
 
4
- require "reqres_rspec/version"
5
- require "reqres_rspec/collector"
6
-
7
- module ReqresRspec
8
- # Contains spec values read from rspec example, request and response
9
- mattr_accessor :records
10
-
11
- # define if all doc generation is enabled
12
- mattr_accessor :enabled
13
- end
14
-
15
- ReqresRspec.enabled = defined?(RSpec)
16
-
17
- if !ReqresRspec.enabled
18
- puts "\nWARNING: ReqresRspec is disabled\n"
19
- end
20
-
21
- if ReqresRspec.enabled
22
- # initialize
23
- ReqresRspec.records = []
6
+ if defined?(RSpec) && ENV['REQRES_RSPEC'] == '1'
7
+ collector = ReqresRspec::Collector.new
24
8
 
25
9
  RSpec.configure do |config|
26
10
  config.after(:each) do
27
11
  if defined?(request) && defined?(response)
28
12
  unless self.example.options.has_key?(:rspec_doc) && !self.example.options[:rspec_doc]
29
- ReqresRspec::Collector.collect(self, request, response)
13
+ collector.collect(self, request, response)
30
14
  end
31
15
  end
32
16
  end
33
17
 
34
18
  config.after(:suite) do
35
- if ReqresRspec.records && ReqresRspec.records.size > 0
36
- puts 'TODO: save records'
37
- puts ReqresRspec.records.inspect
19
+ if collector.records.size > 0
20
+ ReqresRspec::Writers::Html.new(collector.records).write
21
+ ReqresRspec::Generators::Pdf.new.generate
38
22
  end
39
23
  end
40
24
  end
25
+ else
26
+ puts "\nNOTICE: ReqresRspec is disabled. run RSpec with REQRES_RSPEC=1 environment var\n"
41
27
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reqres_rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - rilian
@@ -53,7 +53,11 @@ files:
53
53
  - Rakefile
54
54
  - lib/reqres_rspec.rb
55
55
  - lib/reqres_rspec/collector.rb
56
+ - lib/reqres_rspec/generators/pdf.rb
56
57
  - lib/reqres_rspec/version.rb
58
+ - lib/reqres_rspec/writers/html.rb
59
+ - lib/reqres_rspec/writers/templates/header.erb
60
+ - lib/reqres_rspec/writers/templates/spec.erb
57
61
  - reqres_rspec.gemspec
58
62
  homepage: ''
59
63
  licenses: