had 0.1.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.
@@ -0,0 +1,16 @@
1
+ module Had
2
+ module Formatters
3
+ class JSON < Base
4
+ private
5
+ def write
6
+ path = File.join(output_path, 'reqres_rspec.json')
7
+ File.write(path, ::JSON.pretty_generate(records))
8
+ logger.info "Reqres::Writers::#{self.class.name} saved doc spec to #{path}"
9
+ end
10
+
11
+ def cleanup_pattern
12
+ 'reqres_rspec.json'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ module Had
2
+ module Formatters
3
+ extend self
4
+
5
+ def process(records)
6
+ formatters = Had.configuration.formatters
7
+ raise 'No formatters defined' if formatters.empty?
8
+
9
+ formatters.each do |fmt|
10
+ case fmt
11
+ when 'html'
12
+ HTML.new(records).process
13
+ when 'json'
14
+ JSON.new(records).process
15
+ else
16
+ begin
17
+ klass = Object.const_get(fmt)
18
+ unless klass.public_instance_methods.include?(:process)
19
+ raise "Formatter #{fmt} should respond to `process` method"
20
+ end
21
+ klass.new(records).process
22
+ rescue NameError => e
23
+ if e.message =~ /(uninitialized constant|wrong constant name) #{fmt}$/
24
+ raise "Formatter #{fmt} does not exists"
25
+ else
26
+ raise e
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,77 @@
1
+ <html>
2
+ <head>
3
+ <title><%= Had.configuration.title %></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
+ @page { size: 24.5cm 35cm } /* http://www.princexml.com/doc/9.0/page-size/ */
8
+
9
+ body {
10
+ color: #333333;
11
+ font-family: 'Verdana', 'Droid Sans', sans-serif;
12
+ font-size: 14px;
13
+ padding: 15px;
14
+ margin: 0;
15
+ }
16
+ body *{
17
+ -webkit-box-sizing: border-box;
18
+ -moz-box-sizing: border-box;
19
+ box-sizing: border-box;
20
+ }
21
+ .container {
22
+ padding: 0;
23
+ width: 100%;
24
+ }
25
+ h1, h2, ol {
26
+ margin: 0 20px 10px;
27
+ padding: 0;
28
+ }
29
+ h1 {
30
+ font-size: 2.286em;
31
+ }
32
+ h2 {
33
+ font-size: 2em;
34
+ }
35
+ a {
36
+ color: #1d96d2;
37
+ text-decoration: none;
38
+ }
39
+ ol {
40
+ counter-reset: item;
41
+ }
42
+ ol > li {
43
+ counter-increment: item;
44
+ }
45
+ ol ol > li {
46
+ display: block;
47
+ }
48
+ ol ol > li:before {
49
+ content: counters(item, ".") ". ";
50
+ margin-left: -20px;
51
+ }
52
+
53
+ </style>
54
+ </head>
55
+ <body>
56
+ <div class="container">
57
+ <h1><%= Had.configuration.title %></h1>
58
+ <p>Generated <%= Time.now.strftime('%d %B %Y at %H:%m:%S')%> with <a href="https://github.com/nsheremet/had">had</a></p>
59
+ <ol>
60
+ <% @records.group_by { |x| x[:group] }.sort.each do |group, records| %>
61
+ <li>
62
+ <h3><%= group %></h3>
63
+ <ol>
64
+ <% records.each do |record| %>
65
+ <li>
66
+ <a href="rspec_doc_<%= record[:filename] %>.html">
67
+ <%= record[:title] %>
68
+ </a>
69
+ </li>
70
+ <% end %>
71
+ </ol>
72
+ </li>
73
+ <% end %>
74
+ </ol>
75
+ </div>
76
+ </body>
77
+ </html>
@@ -0,0 +1,24 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title><%= Had.configuration.title %></title>
5
+ <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
6
+ <link href='http://fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'>
7
+ <link href='http://fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css'>
8
+ <style type="text/css">
9
+ body {
10
+ margin:0;
11
+ padding:0;
12
+ border:0;
13
+ width:100%;
14
+ background:#fff;
15
+ min-width:600px;
16
+ font-size:90%;
17
+ }
18
+ </style>
19
+ </head>
20
+ <frameset cols="350,*" frameborder="1" border="1" bordercolor="#999999" framespacing="1">
21
+ <frame name="panel" src="./panel.html">
22
+ <frame name="specs" src="./rspec_doc_table_of_content.html">
23
+ </frameset>
24
+ </html>
@@ -0,0 +1,108 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title><%= Had.configuration.title %></title>
5
+ <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
6
+ <link href='http://fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'>
7
+ <link href='http://fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css'>
8
+ <style type="text/css">
9
+ body {
10
+ color: #333333;
11
+ font-family: 'Verdana', 'Droid Sans', sans-serif;
12
+ font-size: 12px;
13
+ padding: 0;
14
+ margin: 0;
15
+ }
16
+ body *{
17
+ -webkit-box-sizing: border-box;
18
+ -moz-box-sizing: border-box;
19
+ box-sizing: border-box;
20
+ }
21
+ h1, h2, h3 {
22
+ padding: 10px 5px;
23
+ margin: 0;
24
+ background: #dedede;
25
+ }
26
+ h1 {
27
+ font-size: 1.5em;
28
+ }
29
+ h2 {
30
+ font-size: 1.3em;
31
+ padding-bottom:5px;
32
+ }
33
+ h3 {
34
+ font-size: 0.95em;
35
+ padding-bottom:0px;
36
+ }
37
+ ul, ul li {
38
+ list-style-type: none;
39
+ padding:0px;
40
+ margin:0px;
41
+ }
42
+ ul li {
43
+ border-bottom:1px solid lightgray;
44
+ background: #fefefe;
45
+ }
46
+ ul li:first-child {
47
+ border-top:1px solid lightgray;
48
+ }
49
+ ul li:nth-child(2n+1) {
50
+ background: #eeeeee;
51
+ }
52
+ ul li a {
53
+ padding:5px;
54
+ display: block;
55
+ }
56
+ ul li a:hover {
57
+ background: #dedede;
58
+ }
59
+ a {
60
+ color: #1d96d2;
61
+ text-decoration: none;
62
+ }
63
+ span.method {
64
+ border-radius: 3px;
65
+ background-color: darkgray;
66
+ color: white;
67
+ padding: 3px;
68
+ font-size: 90%;
69
+ width: 56px;
70
+ display: inline-block;
71
+ text-align: center;
72
+ }
73
+ span.method.get {
74
+ background-color: darkgreen;
75
+ }
76
+ span.method.post {
77
+ background-color: darkorange;
78
+ }
79
+ span.method.put, span.method.patch {
80
+ background-color: darkorange;
81
+ }
82
+ span.method.delete {
83
+ background-color: darkred;
84
+ }
85
+ </style>
86
+ </head>
87
+ <body>
88
+ <h1><a href="rspec_doc_table_of_content.html" target="specs">
89
+ <%= Had.configuration.title %>
90
+ </a></h1>
91
+ <% @records.group_by{|h| h[:group]}.each do |group, items| %>
92
+ <h2><%= group %></h2>
93
+ <% items.group_by{|h| h[:request][:symbolized_path]}.each do |path, records| %>
94
+ <h3><%= path %></h3>
95
+ <ul>
96
+ <% records.each do |record| %>
97
+ <li>
98
+ <a href="rspec_doc_<%= record[:filename] %>.html" target="specs">
99
+ <span class="method <%= record[:request][:method].downcase %>"><%= record[:request][:method] %></span>
100
+ <%= record[:title] %><br />
101
+ </a>
102
+ </li>
103
+ <% end %>
104
+ </ul>
105
+ <% end %>
106
+ <% end %>
107
+ </body>
108
+ </html>
@@ -0,0 +1,264 @@
1
+ <html>
2
+ <head>
3
+ <title><%= Had.configuration.title %></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
+ @page { size: 24.5cm 35cm } /* http://www.princexml.com/doc/9.0/page-size/ */
8
+
9
+ body {
10
+ color: #333333;
11
+ font-family: 'Verdana', 'Droid Sans', sans-serif;
12
+ font-size: 14px;
13
+ padding: 15px;
14
+ margin: 0;
15
+ }
16
+ body *{
17
+ -webkit-box-sizing: border-box;
18
+ -moz-box-sizing: border-box;
19
+ box-sizing: border-box;
20
+ }
21
+ .container {
22
+ padding: 0;
23
+ width: 100%;
24
+ }
25
+ h1, h2, ul {
26
+ margin: 0 0 10px;
27
+ padding: 0;
28
+ }
29
+ h1 {
30
+ font-size: 2.286em;
31
+ }
32
+ h2 {
33
+ font-size: 2em;
34
+ }
35
+ h3{
36
+ font-size: 1.57em;
37
+ margin: 14px 0 0px;
38
+ padding: 0;
39
+ }
40
+ h4{
41
+ font-size: 1.15em;
42
+ margin: 14px 0 0 0;
43
+ padding: 0;
44
+ }
45
+ h5{
46
+ font-size: 1em;
47
+ margin: 10px 0 0 0;
48
+ padding: 0;
49
+ }
50
+ a {
51
+ color: #1d96d2;
52
+ text-decoration: none;
53
+ }
54
+ .required {
55
+ color: #f00;
56
+ }
57
+ ul {
58
+ padding-left: 0;
59
+ list-style: none;
60
+ }
61
+ li{
62
+ margin-bottom: 4px;
63
+ }
64
+ .table{
65
+ border: 1px solid #cccccc;
66
+ border-radius: 4px;
67
+ overflow: hidden;
68
+ }
69
+ table.params{
70
+ width: 100%;
71
+ font-size: 1em;
72
+ padding: 0;
73
+ margin: 0;
74
+ border-collapse: collapse;
75
+ border-spacing: 0px;
76
+ }
77
+ table.params tr{
78
+ border-top: 1px solid #cccccc;
79
+ }
80
+ table.params tr:first-child{
81
+ border-top: 0
82
+ }
83
+ table.params th, td{
84
+ margin: 0;
85
+ padding: 5px 8px;
86
+ text-align: left;
87
+ border-left: 1px solid #cccccc;
88
+ font-size: 1em;
89
+ }
90
+ table.params th:first-child, td:first-child{
91
+ border-left: 0;
92
+ }
93
+ table.params td{
94
+ background: #f9f9f9;
95
+ }
96
+ table.params tr:nth-child(2n+1) td{
97
+ background: #ffffff;
98
+ }
99
+ table.params td i{
100
+ font-style: normal;
101
+ font-weight: 300;
102
+ float: right;
103
+ }
104
+
105
+ code, ol{
106
+ display: block;
107
+ border-radius: 6px;
108
+ border: 1px solid #cccccc;
109
+ width: 100%;
110
+ background: #f5f5f5;
111
+ font-family: 'Courier New','Droid Sans Mono', monospace;
112
+ font-size: 0.85em;
113
+ font-weight: 700;
114
+ }
115
+ code{
116
+ white-space: pre-line;
117
+ word-wrap: break-word;
118
+ padding: 10px;
119
+ font-size: 0.85em;
120
+ }
121
+ code i{
122
+ font-weight: 400;
123
+ font-style: normal;
124
+ }
125
+ .CodeRay td {
126
+ width: 99%;
127
+ }
128
+ .CodeRay td.line-numbers {
129
+ width: 1%;
130
+ }
131
+ ol{
132
+ margin: 0 0 10px 0;
133
+ padding: 0 10px 0 50px;
134
+ }
135
+ ol li{
136
+ white-space: pre;
137
+ word-wrap: break-word;
138
+ border-left: 1px solid #ccc;
139
+ margin: 0;
140
+ padding: 0 4px;
141
+ }
142
+ ol li:first-child {
143
+ padding-top: 5px;
144
+ }
145
+ ol li:last-child {
146
+ padding-bottom: 5px;
147
+ }
148
+ </style>
149
+ </head>
150
+ <body>
151
+ <div class="container">
152
+ <h3>
153
+ <a href="rspec_doc_table_of_content.html" target="specs">&#9650</a>
154
+ <%= @record[:title] %>
155
+ </h3>
156
+
157
+ <p>
158
+ <%= @record[:description].gsub("\n", ('<br>'.respond_to?(:html_safe) && '<br>'.html_safe) || '<br>') %>
159
+ </p>
160
+
161
+ <code><%= @record[:request][:method] %> <%= @record[:request][:symbolized_path] %></code>
162
+
163
+ <% if @record[:params].size > 0 %>
164
+ <h5>Parameters</h5>
165
+ <div class="table">
166
+ <table class="params">
167
+ <tr>
168
+ <th>Name</th>
169
+ <th>Type</th>
170
+ <th>Description</th>
171
+ </tr>
172
+ <% @record[:params].each do |param| %>
173
+ <tr>
174
+ <td>
175
+ <%= param[:name] %>
176
+ <% if param[:required].present? %>
177
+ <i class="required"><%= param[:required] %></i>
178
+ <% end %>
179
+ </td>
180
+ <td><%= param[:type] %></td>
181
+ <td><%= param[:description].gsub("\n", ('<br>'.respond_to?(:html_safe) && '<br>'.html_safe) || '<br>') %></td>
182
+ </tr>
183
+ <% end %>
184
+ </table>
185
+ </div>
186
+ <% end %>
187
+
188
+ <h4>Request</h4>
189
+
190
+ <% if @record[:request][:headers].keys.count > 0 %>
191
+ <h5>Headers</h5>
192
+ <code><%= @record[:request][:headers].collect { |k, v| "#{k}: <i>#{v}</i>"}.join("\n")%></code>
193
+ <% end %>
194
+
195
+ <h5>Route</h5>
196
+ <code><%= @record[:request][:method] %> <%= @record[:request][:path] %></code>
197
+
198
+ <h5>Query parameters</h5>
199
+ <% if @record[:request][:query_parameters].empty? || @record[:request][:query_parameters] == 'not available' %>
200
+ <code>empty</code>
201
+ <% else %>
202
+ <% lines = ::JSON.pretty_generate(@record[:request][:query_parameters]) %>
203
+ <code>
204
+ <%= CodeRay.scan(lines, :json).div(line_numbers: :table) %>
205
+ </code>
206
+ <% end %>
207
+
208
+ <!--<h5><a href="http://curl.haxx.se/docs/manpage.html">CURL</a> Example</h5>
209
+ <code>TODO</code>-->
210
+
211
+ <h5>Body</h5>
212
+ <% if @record[:request][:body].present? %>
213
+ <% lines =
214
+ begin
215
+ ::JSON.pretty_generate(
216
+ ::JSON.parse(
217
+ @record[:request][:body]
218
+ )
219
+ )
220
+ rescue
221
+ @record[:request][:body]
222
+ end %>
223
+ <code>
224
+ <%= CodeRay.scan(lines, :json).div(line_numbers: :table) %>
225
+ </code>
226
+ <% else %>
227
+ <code>empty</code>
228
+ <% end %>
229
+
230
+ <h4>Response</h4>
231
+
232
+ <% if @record[:response][:headers].count > 0 %>
233
+ <h5>Headers</h5>
234
+ <code><%= @record[:response][:headers].collect { |k, v| "#{k}: <i>#{v}</i>"}.join("\n")%></code>
235
+ <% end %>
236
+
237
+ <h5>Status code (<a href="http://en.wikipedia.org/wiki/List_of_HTTP_status_codes">wiki</a>)</h5>
238
+ <code>HTTP <%= @record[:response][:code] %></code>
239
+
240
+ <% if @record[:response][:body].present? %>
241
+ <h5>Body</h5>
242
+
243
+ <% if (@record[:response][:headers]['Content-Type'] == 'application/pdf' rescue true) %>
244
+ <code>PDF document</code>
245
+ <% else %>
246
+ <% lines =
247
+ begin
248
+ ::JSON.pretty_generate(
249
+ ::JSON.parse(
250
+ @record[:response][:body]
251
+ )
252
+ )
253
+ rescue
254
+ @record[:response][:body]
255
+ end
256
+ %>
257
+ <code>
258
+ <%= CodeRay.scan(lines, @record[:response][:format]).div(line_numbers: :table) %>
259
+ </code>
260
+ <% end %>
261
+ <% end %>
262
+ </div>
263
+ </body>
264
+ </html>
data/lib/had/utils.rb ADDED
@@ -0,0 +1,25 @@
1
+ unless String.respond_to?(:underscore)
2
+ class String
3
+ def underscore
4
+ self.gsub(/::/, '/').
5
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
6
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
7
+ tr("-", "_").
8
+ downcase
9
+ end
10
+ end
11
+ end
12
+
13
+ unless Hash.respond_to?(:deep_merge)
14
+ class ::Hash
15
+ def deep_merge(second)
16
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
17
+ self.merge(second, &merger)
18
+ end
19
+
20
+ def deep_merge!(second)
21
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
22
+ self.merge!(second, &merger)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module Had
2
+ VERSION = '0.1.0'
3
+ end
data/lib/had.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'pry'
2
+ require 'had/version'
3
+ require 'had/utils'
4
+ require 'had/configuration'
5
+ require 'had/collector'
6
+ require 'had/formatters'
7
+ require 'had/formatters/base'
8
+ require 'had/formatters/html'
9
+ require 'had/formatters/json'
10
+
11
+ if defined?(RSpec) && ENV['HAD_RUN'] == '1'
12
+ collector = Had::Collector.new
13
+
14
+ RSpec.configure do |config|
15
+ config.after(:each) do |example|
16
+
17
+ if defined?(Hanami) && process_example?(self.class.example.metadata, example)
18
+ self_request = self.action rescue false
19
+ self_response = response rescue false
20
+ end
21
+
22
+ if defined?(self_request) && self_request && defined?(self_response) && self_response
23
+ begin
24
+ collector.collect(self, example, self_request, self_response)
25
+ rescue Rack::Test::Error
26
+ #
27
+ rescue NameError
28
+ raise $!
29
+ end
30
+ end
31
+ end
32
+
33
+ config.after(:suite) do
34
+ if collector.records.size > 0
35
+ collector.sort
36
+ Had::Formatters.process(collector.records)
37
+ end
38
+ end
39
+ end
40
+
41
+ def process_example?(meta_data, example)
42
+ !(meta_data[:skip_had] || example.metadata[:skip_had])
43
+ end
44
+ end