magicspec 0.0.1

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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.project +18 -0
  4. data/Gemfile +16 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.md +95 -0
  7. data/README.rdoc +38 -0
  8. data/Rakefile +44 -0
  9. data/bin/magicspec +6 -0
  10. data/lib/magicspec/cli.rb +73 -0
  11. data/lib/magicspec/errors.rb +6 -0
  12. data/lib/magicspec/ext/string.rb +11 -0
  13. data/lib/magicspec/generators/magicspec/.rspec +1 -0
  14. data/lib/magicspec/generators/magicspec/app/matchers/.empty_directory +5 -0
  15. data/lib/magicspec/generators/magicspec/app/pages/yale/yale_page.rb.tt +15 -0
  16. data/lib/magicspec/generators/magicspec/app/pages/yale/yale_search_result_page.rb.tt +9 -0
  17. data/lib/magicspec/generators/magicspec/app/reports/.empty_directory +5 -0
  18. data/lib/magicspec/generators/magicspec/app/reports/screenshots/.empty_directory +0 -0
  19. data/lib/magicspec/generators/magicspec/app/spec/shared/login.rb +11 -0
  20. data/lib/magicspec/generators/magicspec/app/spec/spec_helper.rb.tt +114 -0
  21. data/lib/magicspec/generators/magicspec/app/spec/support/formatters/dry_run_formatter.rb +13 -0
  22. data/lib/magicspec/generators/magicspec/app/spec/support/formatters/html_override.rb +360 -0
  23. data/lib/magicspec/generators/magicspec/app/spec/support/formatters/magicspec_formatter.rb +46 -0
  24. data/lib/magicspec/generators/magicspec/app/spec/support/matchers/custom_matcher.rb +7 -0
  25. data/lib/magicspec/generators/magicspec/app/spec/support/matchers/email_matcher.rb +1 -0
  26. data/lib/magicspec/generators/magicspec/app/spec/support/matchers/file_matcher.rb +12 -0
  27. data/lib/magicspec/generators/magicspec/app/spec/yale/yale_example_spec.rb +34 -0
  28. data/lib/magicspec/generators/magicspec/app/test_data/baidu.yml +4 -0
  29. data/lib/magicspec/generators/magicspec/app/test_data/login_user.yml +4 -0
  30. data/lib/magicspec/generators/magicspec/bin/console +7 -0
  31. data/lib/magicspec/generators/magicspec/bin/console.bat +6 -0
  32. data/lib/magicspec/generators/magicspec/bin/setup.rb.tt +12 -0
  33. data/lib/magicspec/generators/magicspec/config/config.yml +6 -0
  34. data/lib/magicspec/generators/magicspec/config/magicspec_formatter.rb +47 -0
  35. data/lib/magicspec/generators/magicspec/config/override.rb +351 -0
  36. data/lib/magicspec/magicspec_config.rb +21 -0
  37. data/lib/magicspec/magicspec_initializer.rb +33 -0
  38. data/lib/magicspec/page_override.rb +5 -0
  39. data/lib/magicspec/templates/browser_spec_template.rb.tt +22 -0
  40. data/lib/magicspec/templates/mobile_template.rb.tt +15 -0
  41. data/lib/magicspec/templates/plain_template.rb.tt +22 -0
  42. data/lib/magicspec/templates/template_page.rb.tt +11 -0
  43. data/lib/magicspec/templates/web_service_template.rb.tt +18 -0
  44. data/lib/magicspec/watir_browser.rb +7 -0
  45. data/lib/magicspec.rb +8 -0
  46. data/magicspec.gemspec +120 -0
  47. data/spec/app/pages/components/footer.rb +3 -0
  48. data/spec/app/pages/components/sub/sub_footer.rb +3 -0
  49. data/spec/app/pages/module1/module1_page.rb +6 -0
  50. data/spec/app/pages/module1/sub_test_page.rb +4 -0
  51. data/spec/app/pages/test_navigator.rb +4 -0
  52. data/spec/app/pages/test_page.rb +4 -0
  53. data/spec/config/config.yml +3 -0
  54. data/spec/config/wrong_config.yml +5 -0
  55. data/spec/lazy_initializer_spec.rb +28 -0
  56. data/spec/lazy_navigator_spec.rb +36 -0
  57. data/spec/lazy_page_spec.rb +44 -0
  58. data/spec/magicspec_config_spec.rb +28 -0
  59. data/spec/pages/components/footer.rb +2 -0
  60. data/spec/pages/test_page.rb +5 -0
  61. data/spec/spec_helper.rb +20 -0
  62. data/spec/string_spec.rb +10 -0
  63. metadata +206 -0
@@ -0,0 +1,360 @@
1
+ require 'rspec'
2
+ require 'rspec/core/formatters/base_text_formatter'
3
+ require 'rspec/core/formatters/html_printer'
4
+ require 'gmail'
5
+ require 'sys/uname'
6
+
7
+ include Sys
8
+
9
+ #https://github.com/GICodeWarrior/threaded-rspec
10
+
11
+ module RSpec
12
+ module Core
13
+ module Formatters
14
+ class HtmlPrinter
15
+ def print_html_start
16
+ @output.puts YALE_HTML_HEADER
17
+ @output.puts YALE_REPORT_HEADER
18
+ end
19
+
20
+ def print_example_group_start( group_id, description, number_of_parents )
21
+ @output.puts "<div id=\"div_group_#{group_id}\" class=\"example_group passed\">"
22
+ @output.puts " <dl #{indentation_style(number_of_parents)}>"
23
+ @output.puts " <dt id=\"example_group_#{group_id}\" class=\"passed\">#{h(description)}</dt>"
24
+ end
25
+
26
+ def print_summary( was_dry_run, duration, example_count, failure_count, pending_count )
27
+ # TODO - kill dry_run?
28
+ if was_dry_run
29
+ totals = "This was a dry-run"
30
+ else
31
+ totals = "#{example_count} example#{'s' unless example_count == 1}, "
32
+ totals << "#{failure_count} failure#{'s' unless failure_count == 1}"
33
+ totals << ", #{pending_count} pending" if pending_count > 0
34
+ end
35
+
36
+ formatted_duration = sprintf("%.5f", duration)
37
+
38
+ @output.puts "<script type=\"text/javascript\">document.getElementById('duration').innerHTML = \"Finished in <strong>#{formatted_duration} seconds</strong>\";</script>"
39
+ @output.puts "<script type=\"text/javascript\">document.getElementById('totals').innerHTML = \"#{totals}\";</script>"
40
+ @output.puts "<script type=\"text/javascript\">document.getElementById('browser').innerHTML = \"#{Uname.sysname}\";</script>"
41
+ @output.puts "</div>"
42
+ @output.puts "</div>"
43
+ @output.puts "</body>"
44
+ @output.puts "</html>"
45
+
46
+ # Send a note to all the emails!
47
+ gmail = Gmail.new("yaleqa@gmail.com","")
48
+ gmail.deliver do
49
+ to $emails
50
+ subject "Test is complete!"
51
+ html_part do
52
+ content_type 'text/html; charset=UTF-8'
53
+ body "<p>Done!</p><p>#{totals}</p>"
54
+ end
55
+ #add_file "/path/to/some_image.jpg"
56
+ end
57
+ end
58
+
59
+ YALE_REPORT_HEADER = <<-EOF
60
+ <div class="rspec-report container">
61
+ <div id="rspec-header">
62
+ <div id="label">
63
+ <h1>Yale Automation Report</h1>
64
+ </div>
65
+
66
+ <div id="display-filters">
67
+ <input id="passed_checkbox" name="passed_checkbox" type="checkbox" checked="checked" onchange="apply_filters()" value="1" /> <label for="passed_checkbox">Passed</label>
68
+ <input id="failed_checkbox" name="failed_checkbox" type="checkbox" checked="checked" onchange="apply_filters()" value="2" /> <label for="failed_checkbox">Failed</label>
69
+ <input id="pending_checkbox" name="pending_checkbox" type="checkbox" checked="checked" onchange="apply_filters()" value="3" /> <label for="pending_checkbox">Pending</label>
70
+ </div>
71
+
72
+ <div id="summary">
73
+ <p id="totals">&#160;</p>
74
+ <p id="duration">&#160;</p>
75
+ </div>
76
+ <div id="chart">
77
+ <div id="container" style="min-width: 200px; height: 200px; margin: 0 auto"></div>
78
+ <script>
79
+ $(function () {
80
+ var chart;
81
+
82
+ $(document).ready(function () {
83
+
84
+ // Build the chart
85
+ $('#container').highcharts({
86
+ chart: {
87
+ plotBackgroundColor: null,
88
+ plotBorderWidth: null,
89
+ plotShadow: false
90
+ },
91
+ title: {
92
+ text: 'Test Results'
93
+ },
94
+ tooltip: {
95
+ pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>'
96
+ },
97
+ plotOptions: {
98
+ pie: {
99
+ allowPointSelect: true,
100
+ cursor: 'pointer',
101
+ dataLabels: {
102
+ enabled: false
103
+ },
104
+ showInLegend: true
105
+ }
106
+ },
107
+ series: [{
108
+ type: 'pie',
109
+ name: 'Browser share',
110
+ data: [
111
+ ['Pass', 10],
112
+ ['Fail', 15],
113
+ {
114
+ name: 'Not Implemented',
115
+ y: 5,
116
+ sliced: true,
117
+ selected: true
118
+ }
119
+ ]
120
+ }]
121
+ });
122
+ });
123
+
124
+ });
125
+ </script>
126
+ </div>
127
+ <div id="deets">
128
+ <table class="table table-bordered table-condensed table-responsive">
129
+ <tr>
130
+ <td>Browser</td>
131
+ <td id="browser"></td>
132
+ </tr>
133
+ <tr>
134
+ <td>OS</td>
135
+ <td id="OS">Mac</td>
136
+ </tr>
137
+ <tr>
138
+ <td>Date</td>
139
+ <td id="Date">#{Time.now.to_s}</td>
140
+ </tr>
141
+ <tr>
142
+ <td>Time</td>
143
+ <td id="Time">12 PM</td>
144
+ </tr>
145
+ </table>
146
+ </div>
147
+ </div>
148
+ <div class="results">
149
+ EOF
150
+
151
+ YALE_GLOBAL_STYLES = <<-EOF
152
+ #rspec-header {
153
+ background: #65C400; color: #fff; height: 250px;
154
+ margin: 10px 10px 10px 10px;
155
+ }
156
+
157
+ .rspec-report h1 {
158
+ margin: 0px 10px 0px 10px;
159
+ padding: 10px;
160
+ font-family: "Lucida Grande", Helvetica, sans-serif;
161
+ font-size: 1.8em;
162
+ position: absolute;
163
+ }
164
+
165
+ #deets {
166
+ float:left;
167
+ margin: 60px 10px 0px 20px;
168
+ position: absolute;
169
+ }
170
+
171
+ #chart {
172
+ float:right;
173
+ margin: 10px 10px 0px 60px;
174
+ }
175
+
176
+ #label {
177
+ float:left;
178
+ }
179
+
180
+ #display-filters {
181
+ float:left;
182
+ padding: 28px 0 0 40%;
183
+ font-family: "Lucida Grande", Helvetica, sans-serif;
184
+ }
185
+
186
+ #summary {
187
+ float:right;
188
+ padding: 5px 10px;
189
+ font-family: "Lucida Grande", Helvetica, sans-serif;
190
+ text-align: right;
191
+ }
192
+
193
+ #summary p {
194
+ margin: 0 0 0 2px;
195
+ }
196
+
197
+ #summary #totals {
198
+ font-size: 1.2em;
199
+ }
200
+
201
+ .example_group {
202
+ margin: 0 10px 5px;
203
+ background: #fff;
204
+ }
205
+
206
+ dl {
207
+ margin: 0; padding: 0 0 5px;
208
+ font: normal 11px "Lucida Grande", Helvetica, sans-serif;
209
+ }
210
+
211
+ dt {
212
+ padding: 3px;
213
+ background: #65C400;
214
+ color: #fff;
215
+ font-weight: bold;
216
+ }
217
+
218
+ dd {
219
+ margin: 5px 0 5px 5px;
220
+ padding: 3px 3px 3px 18px;
221
+ }
222
+
223
+ dd .duration {
224
+ padding-left: 5px;
225
+ text-align: right;
226
+ right: 0px;
227
+ float:right;
228
+ }
229
+
230
+ dd.example.passed {
231
+ border-left: 5px solid #65C400;
232
+ border-bottom: 1px solid #65C400;
233
+ background: #DBFFB4; color: #3D7700;
234
+ }
235
+
236
+ dd.example.not_implemented {
237
+ border-left: 5px solid #FAF834;
238
+ border-bottom: 1px solid #FAF834;
239
+ background: #FCFB98; color: #131313;
240
+ }
241
+
242
+ dd.example.pending_fixed {
243
+ border-left: 5px solid #0000C2;
244
+ border-bottom: 1px solid #0000C2;
245
+ color: #0000C2; background: #D3FBFF;
246
+ }
247
+
248
+ dd.example.failed {
249
+ border-left: 5px solid #C20000;
250
+ border-bottom: 1px solid #C20000;
251
+ color: #C20000; background: #FFFBD3;
252
+ }
253
+
254
+ dt.not_implemented {
255
+ color: #000000; background: #FAF834;
256
+ }
257
+
258
+ dt.pending_fixed {
259
+ color: #FFFFFF; background: #C40D0D;
260
+ }
261
+
262
+ dt.failed {
263
+ color: #FFFFFF; background: #C40D0D;
264
+ }
265
+
266
+ #rspec-header.not_implemented {
267
+ color: #000000; background: #FAF834;
268
+ }
269
+
270
+ #rspec-header.pending_fixed {
271
+ color: #FFFFFF; background: #C40D0D;
272
+ }
273
+
274
+ #rspec-header.failed {
275
+ color: #FFFFFF; background: #C40D0D;
276
+ }
277
+
278
+ .backtrace {
279
+ color: #000;
280
+ font-size: 12px;
281
+ }
282
+
283
+ a {
284
+ color: #BE5C00;
285
+ }
286
+
287
+ /* Ruby code, style similar to vibrant ink */
288
+ .ruby {
289
+ font-size: 12px;
290
+ font-family: monospace;
291
+ color: white;
292
+ background-color: black;
293
+ padding: 0.1em 0 0.2em 0;
294
+ }
295
+
296
+ .ruby .keyword { color: #FF6600; }
297
+ .ruby .constant { color: #339999; }
298
+ .ruby .attribute { color: white; }
299
+ .ruby .global { color: white; }
300
+ .ruby .module { color: white; }
301
+ .ruby .class { color: white; }
302
+ .ruby .string { color: #66FF00; }
303
+ .ruby .ident { color: white; }
304
+ .ruby .method { color: #FFCC00; }
305
+ .ruby .number { color: white; }
306
+ .ruby .char { color: white; }
307
+ .ruby .comment { color: #9933CC; }
308
+ .ruby .symbol { color: white; }
309
+ .ruby .regex { color: #44B4CC; }
310
+ .ruby .punct { color: white; }
311
+ .ruby .escape { color: white; }
312
+ .ruby .interp { color: white; }
313
+ .ruby .expr { color: white; }
314
+
315
+ .ruby .offending { background-color: gray; }
316
+ .ruby .linenum {
317
+ width: 75px;
318
+ padding: 0.1em 1em 0.2em 0;
319
+ color: #000000;
320
+ background-color: #FFFBD3;
321
+ }
322
+ EOF
323
+
324
+ YALE_HTML_HEADER = <<-EOF
325
+ <!DOCTYPE html>
326
+ <html lang='en'>
327
+ <head>
328
+ <title>RSpec results</title>
329
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
330
+ <meta http-equiv="Expires" content="-1" />
331
+ <meta http-equiv="Pragma" content="no-cache" />
332
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
333
+ <script src='http://code.jquery.com/jquery-1.10.1.min.js'></script>"
334
+ <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
335
+ <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
336
+ <script src='http://code.highcharts.com/highcharts.js'></script>
337
+ <style type="text/css">
338
+ body {
339
+ margin: 0;
340
+ padding: 0;
341
+ background: #fff;
342
+ font-size: 80%;
343
+ }
344
+ </style>
345
+ <script type="text/javascript">
346
+ // <![CDATA[
347
+ #{GLOBAL_SCRIPTS}
348
+ // ]]>
349
+ </script>
350
+ <style type="text/css">
351
+ #{YALE_GLOBAL_STYLES}
352
+ </style>
353
+ </head>
354
+ <body>
355
+ EOF
356
+
357
+ end
358
+ end
359
+ end
360
+ end
@@ -0,0 +1,46 @@
1
+ require 'erb'
2
+ require 'rspec/core/formatters/html_formatter'
3
+ #require './override.rb'
4
+
5
+ module Lazyman
6
+ class LazymanFormatter < ::RSpec::Core::Formatters::HtmlFormatter
7
+ def initialize(output)
8
+ output = File.new(File.expand_path(File.join('.', 'app', 'reports', "#{Time.now.strftime("%Y%m%d_%H%M%S")}.html")), 'w')
9
+ super(output)
10
+ @printer.class.send(:define_method, 'puts') do |what|
11
+ @output.puts what
12
+ end #define_method
13
+ end
14
+
15
+ def example_failed(example)
16
+ super(example)
17
+ if $navi
18
+ failed_url = $navi.url rescue $navi.current_url
19
+ @printer.puts "<a target=\"_blank\" href=\"#{failed_url}\">failed url is [#{failed_url}]</a>"
20
+ @printer.puts '<br />'
21
+ @printer.puts '<p>Extra problem text</p>'
22
+ @printer.flush
23
+ end #if
24
+ end
25
+
26
+ def example_passed(example)
27
+ super(example)
28
+ @printer.flush
29
+ end
30
+
31
+ def extra_failure_content(failure)
32
+ browser = example_group.before_all_ivars[:@browser] || @browser
33
+ # If we'ere dealing with a browser get a screenshot on failure
34
+ if browser
35
+ browser.screenshot.save File.expand_path(File.join('.', 'app', 'reports','screenshots', "#{Time.now.strftime("%Y%m%d_%H%M%S")}.png"))
36
+
37
+ # Need to figure out how to remotely upload
38
+ content = []
39
+ content << "<span>"
40
+ content << "<a target='_blank' href='http://127.0.0.1:8020/o365/app/reports/screenshots/20131031_103612.png'>screenshot</a>"
41
+ content << "</span>"
42
+ super + content.join($/)
43
+ end
44
+ end
45
+ end #LazymanFormatter
46
+ end #Lazyman
@@ -0,0 +1,7 @@
1
+ require 'rspec/expectations'
2
+
3
+ RSpec::Matchers.define :be_a_multiple_of do |expected|
4
+ match do |actual|
5
+ actual % expected == 0
6
+ end
7
+ end
@@ -0,0 +1 @@
1
+ # Make a custom email matcher
@@ -0,0 +1,12 @@
1
+ require 'rspec/expectations'
2
+
3
+ # Not quite sure what the usage is for this yet?
4
+ RSpec::Matchers.define(:be_same_file_as) do |exected_file_path|
5
+ match do |actual_file_path|
6
+ md5_hash(actual_file_path).should == md5_hash(exected_file_path)
7
+ end
8
+
9
+ def md5_hash(file_path)
10
+ Digest::MD5.hexdigest(File.read(file_path))
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ #encoding: utf-8
2
+ require File.expand_path 'app/spec/spec_helper'
3
+
4
+ $config["browsers"].each do |b|
5
+ describe 'The Yale home page', :yale do
6
+
7
+ before(:all) do
8
+ @browser = Watir::Browser.new b
9
+ @yale_page = YalePage.new(@browser)
10
+ @yale_page.goto
11
+ end
12
+
13
+ it 'has the correct title' do
14
+ expect(@yale_page.title).to eq 'Yale University'
15
+ end
16
+
17
+ it 'has the correct text' do
18
+ expect(@yale_page.text).to include 'Harvard'
19
+ end
20
+
21
+ it 'has the correct links'
22
+
23
+ describe 'The Yale search', :yale do
24
+ term = "stuff"
25
+ it "has the correct results for '#{term}'" do
26
+ result_page = @yale_page.search_for term
27
+ expect(result_page.term_found?(term)).to eq true
28
+ end
29
+ end
30
+ after(:all) do
31
+ @browser.close rescue nil
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ ---
2
+ one:
3
+ keyword=: '<%= 1.day.ago %>'
4
+ search_element: 'click'
@@ -0,0 +1,4 @@
1
+ ---
2
+ demo:
3
+ name: demo
4
+ password: demo
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ spec_helper = File.expand_path(File.join('.', 'app', 'cases', 'spec_helper.rb'))
4
+
5
+ setup = File.join(File.dirname(__FILE__), 'setup.rb')
6
+
7
+ exec "irb -r rspec -r #{spec_helper} -r #{setup} -r irb/completion --simple-prompt --readline"
@@ -0,0 +1,6 @@
1
+ @ECHO OFF
2
+ IF NOT "%~f0" == "~f0" GOTO :WinNT
3
+ @"ruby.exe" "" %1 %2 %3 %4 %5 %6 %7 %8 %9
4
+ GOTO :EOF
5
+ :WinNT
6
+ @"ruby.exe" "%~dpn0" %*
@@ -0,0 +1,12 @@
1
+ def test_data file
2
+ content = ''
3
+ file_path = File.expand_path(File.join('.', 'app', 'test_data', "#{file}.yml"))
4
+ raise "Can not find #{file}.yml" unless File.exists?(file_path)
5
+ File.open(file_path, 'r') do |handle|
6
+ content = handle.read
7
+ end
8
+ Psych.load ERB.new(content).result(binding)
9
+ end
10
+ <% require 'active_support/all' %>
11
+ $navi = <%= @name.camelize %>Navigator.new $config
12
+
@@ -0,0 +1,6 @@
1
+ ---
2
+ browsers:
3
+ - firefox
4
+ - chrome
5
+ host: www.yale.edu
6
+ tags: yale
@@ -0,0 +1,47 @@
1
+ require 'erb'
2
+ require 'rspec/core/formatters/html_formatter'
3
+ require './config/override'
4
+
5
+ module Lazyman
6
+ class LazymanFormatter < ::RSpec::Core::Formatters::HtmlFormatter
7
+ def initialize(output)
8
+ output = File.new(File.expand_path(File.join('.', 'app', 'reports', "#{Time.now.strftime("%Y%m%d_%H%M%S")}.html")), 'w')
9
+ super(output)
10
+ @printer.class.send(:define_method, 'puts') do |what|
11
+ @output.puts what
12
+ end #define_method
13
+ end
14
+
15
+ def example_failed(example)
16
+ super(example)
17
+ if $navi
18
+ failed_url = $navi.url rescue $navi.current_url
19
+ @printer.puts "<a target=\"_blank\" href=\"#{failed_url}\">failed url is [#{failed_url}]</a>"
20
+ @printer.puts '<br />'
21
+ @printer.puts '<p>Extra problem text</p>'
22
+ @printer.flush
23
+ end #if
24
+ end
25
+
26
+ def example_passed(example)
27
+ super(example)
28
+ @printer.puts "<p>I'm here.</p>"
29
+ @printer.flush
30
+ end
31
+
32
+ def extra_failure_content(failure)
33
+ browser = example_group.before_all_ivars[:@browser] || @browser
34
+ # If we'ere dealing with a browser get a screenshot on failure
35
+ if browser
36
+ browser.screenshot.save File.expand_path(File.join('.', 'app', 'reports','screenshots', "#{Time.now.strftime("%Y%m%d_%H%M%S")}.png"))
37
+
38
+ # Need to figure out how to remotely upload
39
+ content = []
40
+ content << "<span>"
41
+ content << "<a target='_blank' href='http://127.0.0.1:8020/o365/app/reports/screenshots/20131031_103612.png'>screenshot</a>"
42
+ content << "</span>"
43
+ super + content.join($/)
44
+ end
45
+ end
46
+ end #LazymanFormatter
47
+ end #Lazyman