lazylead 0.9.2 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/Rakefile +1 -2
  4. data/lazylead.gemspec +14 -14
  5. data/lib/lazylead/confluence.rb +1 -2
  6. data/lib/lazylead/os.rb +55 -0
  7. data/lib/lazylead/task/accuracy/accuracy.rb +2 -3
  8. data/lib/lazylead/task/accuracy/attachment.rb +1 -0
  9. data/lib/lazylead/task/accuracy/environment.rb +3 -8
  10. data/lib/lazylead/task/accuracy/logs_link.rb +55 -0
  11. data/lib/lazylead/task/accuracy/records.rb +1 -1
  12. data/lib/lazylead/task/accuracy/required.rb +42 -0
  13. data/lib/lazylead/task/accuracy/screenshots.rb +5 -6
  14. data/lib/lazylead/task/accuracy/servers.rb +7 -7
  15. data/lib/lazylead/task/accuracy/testcase.rb +27 -20
  16. data/lib/lazylead/task/fix_version.rb +5 -8
  17. data/lib/lazylead/task/loading.rb +2 -2
  18. data/lib/lazylead/task/svn/diff.rb +9 -9
  19. data/lib/lazylead/task/svn/grep.rb +8 -71
  20. data/lib/lazylead/task/svn/svn.rb +107 -0
  21. data/lib/lazylead/task/svn/touch.rb +5 -7
  22. data/lib/lazylead/version.rb +1 -1
  23. data/lib/messages/illegal_assignee_change.erb +1 -2
  24. data/lib/messages/loading.erb +1 -1
  25. data/lib/messages/svn_diff.erb +29 -29
  26. data/lib/messages/svn_diff_attachment.erb +5 -7
  27. data/lib/messages/svn_grep.erb +34 -32
  28. data/readme.md +4 -3
  29. data/test/lazylead/system/jira_test.rb +8 -0
  30. data/test/lazylead/task/accuracy/accuracy_test.rb +1 -1
  31. data/test/lazylead/task/accuracy/logs_link_test.rb +50 -0
  32. data/test/lazylead/task/accuracy/onlyll_test.rb +1 -1
  33. data/test/lazylead/task/accuracy/screenshots_test.rb +2 -4
  34. data/test/lazylead/task/accuracy/testcase_test.rb +16 -0
  35. data/test/lazylead/task/alert/alertif_test.rb +2 -1
  36. data/test/lazylead/task/assignment_test.rb +2 -1
  37. data/test/lazylead/task/created_recently_test.rb +2 -1
  38. data/test/lazylead/task/duedate_test.rb +2 -2
  39. data/test/lazylead/task/fix_version_test.rb +2 -1
  40. data/test/lazylead/task/loading_test.rb +5 -2
  41. data/test/lazylead/task/micromanager_test.rb +1 -1
  42. data/test/lazylead/task/missing_comment_test.rb +1 -1
  43. data/test/lazylead/task/svn/diff_test.rb +1 -1
  44. data/test/lazylead/task/svn/grep_test.rb +2 -1
  45. data/test/test.rb +4 -3
  46. metadata +33 -27
@@ -39,15 +39,12 @@ module Lazylead
39
39
  def run(sys, postman, opts)
40
40
  allowed = opts.slice("allowed", ",")
41
41
  silent = opts.key? "silent"
42
- issues = sys.issues(
43
- opts["jql"], opts.jira_defaults.merge(expand: "changelog")
44
- )
42
+ issues = sys.issues(opts["jql"], opts.jira_defaults.merge(expand: "changelog"))
43
+ .map { |i| Version.new(i, allowed, silent) }
44
+ .select(&:changed?)
45
+ .each(&:add_label)
45
46
  return if issues.empty?
46
- postman.send opts.merge(
47
- versions: issues.map { |i| Version.new(i, allowed, silent) }
48
- .select(&:changed?)
49
- .each(&:add_label)
50
- )
47
+ postman.send opts.merge(versions: issues)
51
48
  end
52
49
  end
53
50
 
@@ -72,8 +72,8 @@ module Lazylead
72
72
  "#{id} has #{total} tasks"
73
73
  end
74
74
 
75
- def sprints(*label)
76
- return @tasks.group_by(&:sprint).sort if label.empty? || label.nil?
75
+ def sprints(label)
76
+ return @tasks.group_by(&:sprint).sort if label.nil? || label.blank?
77
77
  @tasks.group_by { |t| t.sprint(label) }.sort
78
78
  end
79
79
  end
@@ -27,8 +27,10 @@ require "tempfile"
27
27
  require "nokogiri"
28
28
  require "backtrace"
29
29
  require "active_support/core_ext/hash/conversions"
30
+ require_relative "../../os"
30
31
  require_relative "../../salt"
31
32
  require_relative "../../opts"
33
+ require_relative "svn"
32
34
 
33
35
  module Lazylead
34
36
  module Task
@@ -43,15 +45,13 @@ module Lazylead
43
45
  end
44
46
 
45
47
  def run(_, postman, opts)
46
- cmd = [
47
- "svn log --diff --no-auth-cache",
48
- "--username #{opts.decrypt('svn_user', 'svn_salt')}",
49
- "--password #{opts.decrypt('svn_password', 'svn_salt')}",
50
- "-r#{opts['since_rev']}:HEAD #{opts['svn_url']}"
51
- ]
52
- stdout = `#{cmd.join(" ")}`
53
- stdout.scrub!
54
- send_email postman, opts.merge(stdout: stdout) unless stdout.blank?
48
+ stdout = OS.new.run "svn log --diff --no-auth-cache",
49
+ "--username #{opts.decrypt('svn_user', 'svn_salt')}",
50
+ "--password #{opts.decrypt('svn_password', 'svn_salt')}",
51
+ "-r#{opts['since_rev']}:HEAD #{opts['svn_url']}"
52
+ return if stdout.blank?
53
+ commits = Lazylead::Svn::Commits.new(stdout)
54
+ send_email postman, opts.merge(commits: commits) unless commits.empty?
55
55
  end
56
56
 
57
57
  # Send email with svn log as an attachment.
@@ -25,8 +25,10 @@
25
25
  require "tmpdir"
26
26
  require "nokogiri"
27
27
  require "active_support/core_ext/hash/conversions"
28
+ require_relative "../../os"
28
29
  require_relative "../../salt"
29
30
  require_relative "../../opts"
31
+ require_relative "svn"
30
32
 
31
33
  module Lazylead
32
34
  module Task
@@ -47,14 +49,12 @@ module Lazylead
47
49
 
48
50
  # Return all svn commits for particular date range in repo
49
51
  def svn_log(opts)
50
- cmd = [
51
- "svn log --diff --no-auth-cache",
52
- "--username #{opts.decrypt('svn_user', 'svn_salt')}",
53
- "--password #{opts.decrypt('svn_password', 'svn_salt')}",
54
- "-r {#{from(opts)}}:{#{now(opts)}} #{opts['svn_url']}"
55
- ]
56
- stdout = `#{cmd.join(" ")}`
57
- stdout.split("-" * 72).reject(&:blank?).reverse.map { |e| Entry.new(e) }
52
+ stdout = OS.new.run "svn log --diff --no-auth-cache",
53
+ "--username #{opts.decrypt('svn_user', 'svn_salt')}",
54
+ "--password #{opts.decrypt('svn_password', 'svn_salt')}",
55
+ "-r {#{from(opts)}}:{#{now(opts)}} #{opts['svn_url']}"
56
+ return [] if stdout.blank?
57
+ Lazylead::Svn::Commits.new(stdout)
58
58
  end
59
59
 
60
60
  # The start date & time for search range
@@ -73,67 +73,4 @@ module Lazylead
73
73
  end
74
74
  end
75
75
  end
76
-
77
- # Single SVN commit details
78
- class Entry
79
- def initialize(commit)
80
- @commit = commit
81
- end
82
-
83
- def to_s
84
- "#{rev} #{msg}"
85
- end
86
-
87
- def rev
88
- header.first[1..]
89
- end
90
-
91
- def author
92
- header[1]
93
- end
94
-
95
- def time
96
- header[2]
97
- end
98
-
99
- def msg
100
- lines[1]
101
- end
102
-
103
- # The modified lines contains expected text
104
- def includes?(text)
105
- text = [text] unless text.respond_to? :each
106
- lines[4..].select { |l| l.start_with? "+" }
107
- .any? { |l| text.any? { |t| l.include? t } }
108
- end
109
-
110
- def lines
111
- @lines ||= @commit.split("\n").reject(&:blank?)
112
- end
113
-
114
- def header
115
- @header ||= lines.first.split(" | ").reject(&:blank?)
116
- end
117
-
118
- # Detect SVN diff lines with particular text
119
- def diff(text)
120
- @diff ||= begin
121
- files = affected(text).uniq
122
- @commit.split("Index: ")
123
- .select { |i| files.any? { |f| i.start_with? f } }
124
- .map { |i| i.split "\n" }
125
- .flatten
126
- end
127
- end
128
-
129
- # Detect affected files with particular text
130
- def affected(text)
131
- occurrences = lines.each_index.select do |i|
132
- lines[i].start_with?("+") && text.any? { |t| lines[i].include? t }
133
- end
134
- occurrences.map do |occ|
135
- lines[2..occ].reverse.find { |l| l.start_with? "Index: " }[7..]
136
- end
137
- end
138
- end
139
76
  end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License
4
+ #
5
+ # Copyright (c) 2019-2021 Yurii Dubinka
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"),
9
+ # to deal in the Software without restriction, including without limitation
10
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
11
+ # and/or sell copies of the Software, and to permit persons to whom
12
+ # the Software is furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included
15
+ # in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
23
+ # OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ require "forwardable"
26
+
27
+ module Lazylead
28
+ module Svn
29
+ #
30
+ # SVN commit built from command-line stdout (OS#run).
31
+ #
32
+ class Commit
33
+ def initialize(raw)
34
+ @raw = raw
35
+ end
36
+
37
+ def to_s
38
+ "#{rev} #{msg}"
39
+ end
40
+
41
+ def rev
42
+ header.first[1..]
43
+ end
44
+
45
+ def author
46
+ header[1]
47
+ end
48
+
49
+ def time
50
+ DateTime.parse(header[2]).strftime("%d-%m-%Y %H:%M:%S")
51
+ end
52
+
53
+ def msg
54
+ lines[1]
55
+ end
56
+
57
+ def lines
58
+ @lines ||= @raw.split("\n").reject(&:blank?)
59
+ end
60
+
61
+ def header
62
+ @header ||= lines.first.split(" | ").reject(&:blank?)
63
+ end
64
+
65
+ # The modified lines contains expected text
66
+ def includes?(text)
67
+ text = [text] unless text.respond_to? :each
68
+ lines[4..].select { |l| l.start_with? "+" }.any? { |l| text.any? { |t| l.include? t } }
69
+ end
70
+
71
+ # Detect SVN diff lines with particular text
72
+ def diff(text)
73
+ @diff ||= begin
74
+ files = affected(text).uniq
75
+ @raw.split("Index: ")
76
+ .select { |i| files.any? { |f| i.start_with? f } }
77
+ .map { |i| i.split "\n" }
78
+ .flatten
79
+ end
80
+ end
81
+
82
+ # Detect affected files with particular text
83
+ def affected(text)
84
+ occurrences = lines.each_index.select do |i|
85
+ lines[i].start_with?("+") && text.any? { |t| lines[i].include? t }
86
+ end
87
+ occurrences.map do |occ|
88
+ lines[2..occ].reverse.find { |l| l.start_with? "Index: " }[7..]
89
+ end
90
+ end
91
+ end
92
+
93
+ #
94
+ # SVN commits built from command-line stdout (OS#run).
95
+ #
96
+ class Commits
97
+ extend Forwardable
98
+ def_delegators :@all, :each, :map, :select, :empty?
99
+
100
+ # @todo #/DEV Find a way how to avoid @all initialization directly in constructor.
101
+ # There should be a way how to make lazy initialization of @all with 'forwardable'.
102
+ def initialize(stdout)
103
+ @all = stdout.split("-" * 72).reject(&:blank?).reverse.map { |e| Commit.new(e) }
104
+ end
105
+ end
106
+ end
107
+ end
@@ -25,6 +25,7 @@
25
25
  require "tmpdir"
26
26
  require "nokogiri"
27
27
  require "active_support/core_ext/hash/conversions"
28
+ require_relative "../../os"
28
29
  require_relative "../../salt"
29
30
  require_relative "../../opts"
30
31
 
@@ -64,13 +65,10 @@ module Lazylead
64
65
  DateTime.now
65
66
  end
66
67
  start = (now.to_time - opts["period"].to_i).to_datetime
67
- cmd = [
68
- "svn log --no-auth-cache",
69
- "--username #{opts.decrypt('svn_user', 'svn_salt')}",
70
- "--password #{opts.decrypt('svn_password', 'svn_salt')}",
71
- "--xml -v -r {#{start}}:{#{now}} #{opts['svn_url']}"
72
- ]
73
- raw = `#{cmd.join(" ")}`
68
+ raw = OS.new.run "svn log --no-auth-cache",
69
+ "--username #{opts.decrypt('svn_user', 'svn_salt')}",
70
+ "--password #{opts.decrypt('svn_password', 'svn_salt')}",
71
+ "--xml -v -r {#{start}}:{#{now}} #{opts['svn_url']}"
74
72
  Nokogiri.XML(raw, nil, "UTF-8")
75
73
  end
76
74
 
@@ -23,5 +23,5 @@
23
23
  # OR OTHER DEALINGS IN THE SOFTWARE.
24
24
 
25
25
  module Lazylead
26
- VERSION = "0.9.2"
26
+ VERSION = "0.10.2"
27
27
  end
@@ -102,8 +102,7 @@
102
102
  <tr>
103
103
  <td><a href='<%= a.issue.url %>'><%= a.issue.key %></a></td>
104
104
  <td><%= a.issue.priority %></td>
105
- <td><%= DateTime.parse(a.last["created"])
106
- .strftime('%d-%b-%Y %I:%M:%S %p') %></td>
105
+ <td><%= DateTime.parse(a.last["created"]).strftime('%d-%b') %></td>
107
106
  <td><span style='color: red'><%= a.last["author"]["displayName"] %></span>
108
107
  (<%= a.last["author"]["name"] %>)
109
108
  </td>
@@ -73,7 +73,7 @@
73
73
  <th id="total">
74
74
  <a href="<%= search_link %><%= CGI.escape(jql) %>">Assigned From</a>
75
75
  </th>
76
- <th id="duedate">Next Due date</th>
76
+ <th id="duedate">Next due date</th>
77
77
  </tr>
78
78
  <% assignments.each do |teammate, assignment| %>
79
79
  <% if assignment.free? %>
@@ -64,20 +64,13 @@
64
64
  padding: 36px
65
65
  }
66
66
 
67
- .commit {
68
- min-width: 100%;
69
- border-radius: 3.5px;
70
- overflow: hidden;
71
- display: inline-block;
72
- line-height: 15px;
73
- font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
74
- border: 1px solid #BCC6CC;
67
+ th, td {
68
+ padding-left: 3px;
69
+ padding-right: 3px;
75
70
  }
76
71
 
77
- .commit * {
78
- padding-left: 4px;
79
- font-size: 8px;
80
- line-height: 12px;
72
+ th {
73
+ font-weight: bold;
81
74
  }
82
75
  </style>
83
76
  <title>SVN log</title>
@@ -86,23 +79,30 @@
86
79
  <p>Hi,</p>
87
80
  <p>The following file(s) changed since <code><%= since_rev %></code> rev in
88
81
  <a href="<%= svn_url %>"><%= svn_url %></a>:</p>
89
- <% stdout.split("------------------------------------------------------------------------").reject(&:blank?).reverse.each do |commit| %>
90
- <div class="commit">
91
- <% commit.split("\n").reject(&:blank?).each_with_index do |line, index| %>
92
- <p style="background: gainsboro;">
93
- <% if index.zero? %>
94
- <% details = line.split("|").map(&:strip).reject(&:blank?) %>
95
- <a href="<%= commit_url %><%= details[0][1..-1] %>"><%= details[0] %></a>
96
- by <a href="<%= user %><%= details[1] %>"><%= details[1] %></a>
97
- at <span style="color: #275a90;"><%= details[2] %></span>
98
- <% end %>
99
- <% if index == 1 %>
100
- <span style="padding-left: 4px"><%= line %></span>
101
- <% end %>
102
- </p>
103
- <% end %>
104
- </div>
105
- <% end %>
82
+ <table summary="diff">
83
+ <tr>
84
+ <th id="rev">rev</th>
85
+ <th id="author">Author</th>
86
+ <th id="time">When</th>
87
+ <th id="msg">Message</th>
88
+ </tr>
89
+ <% commits.each do |commit| %>
90
+ <tr>
91
+ <td>
92
+ <div class="auto">
93
+ <a href="<%= commit_url %><%= commit.rev %>"><%= commit.rev %></a>
94
+ </div>
95
+ </td>
96
+ <td>
97
+ <div class="auto">
98
+ <a href="<%= user %><%= commit.author %>"><%= commit.author %></a>
99
+ </div>
100
+ </td>
101
+ <td><%= commit.time %></td>
102
+ <td><%= commit.lines[1] %></td>
103
+ </tr>
104
+ <% end %>
105
+ </table>
106
106
  <p>Posted by
107
107
  <a href="https://github.com/dgroup/lazylead">lazylead v<%= version %></a>.
108
108
  </p>
@@ -91,18 +91,16 @@
91
91
  <p>Commit(s) since <code><%= since_rev %></code> revision in <a href="<%= svn_url %>"><%= svn_url %></a>:</p>
92
92
  <div id="table-of-contents" class="table-of-contents">
93
93
  <ul>
94
- <% stdout.split("------------------------------------------------------------------------").reject(&:blank?).reverse.each do |commit| %>
95
- <% details = commit.split("\n").reject(&:blank?).first.split("|").map(&:strip).reject(&:blank?) %>
96
- <li><a href="#<%= details[0] %>"><%= details[0] %></a> by <%= details[1] %> at <%= details[2] %></li>
94
+ <% commits.each do |commit| %>
95
+ <li><a href="#<%= commit.rev %>"><%= commit.rev %></a> by <%= commit.author %> at <%= commit.time %></li>
97
96
  <% end %>
98
97
  </ul>
99
98
  </div>
100
- <% stdout.split("------------------------------------------------------------------------").reject(&:blank?).reverse.each do |commit| %>
99
+ <% commits.each do |commit| %>
101
100
  <div class="commit">
102
- <% commit.split("\n").reject(&:blank?).each_with_index do |line, index| %>
101
+ <% commit.lines.each_with_index do |line, index| %>
103
102
  <% if index.zero? %>
104
- <% details = line.split("|").map(&:strip).reject(&:blank?) %>
105
- <p style="background: gainsboro;" id="<%= details[0] %>"><a href="<%= commit_url %><%= details[0][1..-1] %>"><%= details[0] %></a> by <a href="<%= user %><%= details[1] %>"><%= details[1] %></a> at <span style="color: #275a90;"><%= details[2] %></span>
103
+ <p style="background: gainsboro;" id="<%= commit.rev %>"><a href="<%= commit_url %><%= commit.rev %>"><%= commit.rev %></a> by <a href="<%= user %><%= commit.author %>"><%= commit.author %></a> at <span style="color: #275a90;"><%= commit.time %></span>
106
104
  <a href="#table-of-contents">&#8593;</a>
107
105
  </p>
108
106
  <% else %>
@@ -64,14 +64,8 @@
64
64
  padding: 36px
65
65
  }
66
66
 
67
- .commit {
68
- min-width: 100%;
69
- border-radius: 3.5px;
70
- overflow: hidden;
71
- display: inline-block;
72
- line-height: 15px;
73
- font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
74
- border: 1px solid #BCC6CC;
67
+ td {
68
+ padding: 2px;
75
69
  }
76
70
 
77
71
  .commit * {
@@ -86,29 +80,37 @@
86
80
  <p>Hi,</p>
87
81
  <p>The following file(s) contain text <code><%= text %></code> in
88
82
  <a href="<%= svn_url %>"><%= svn_url %></a>:</p>
89
- <% entries.each do |commit| %>
90
- <div class="commit">
91
- <p style="background: gainsboro;">
92
- <a href="<%= commit_url %><%= commit.rev %>"><%= commit.msg %></a>
93
- by <a href="<%= user %><%= commit.author %>"><%= commit.author %></a>
94
- at <span style="color: #275a90;"><%= commit.time %></span>
95
- </p>
96
- <% commit.diff(text.split(",")).each do |line| %>
97
- <% if line.start_with?("+++") || line.start_with?("---") %>
98
- <p style="background: gainsboro;"><%= line.gsub(/[<>]/, '<' => '&lt;', '>' => '&gt;') %></p>
99
- <% elsif text.split(",").any? { |t| line.include? t } %>
100
- <p style="background: #F9F103;"><%= line.gsub(/[<>]/, '<' => '&lt;', '>' => '&gt;') %></p>
101
- <% elsif line.start_with?("+") %>
102
- <p style="<%= "background: darkseagreen;" %>"><%= line.gsub(/[<>]/, '<' => '&lt;', '>' => '&gt;') %></p>
103
- <% elsif line.start_with?("-") %>
104
- <p style="<%= "background: lightsalmon;" %>"><%= line.gsub(/[<>]/, '<' => '&lt;', '>' => '&gt;') %></p>
105
- <% else %>
106
- <p style="background: gainsboro;"><%= line.gsub(/[<>]/, '<' => '&lt;', '>' => '&gt;') %></p>
107
- <% end %>
108
- <% end %>
109
- </div>
110
- <% end %>
111
- <p>Posted by<a href="https://github.com/dgroup/lazylead">lazylead
112
- v<%= version %></a>.</p>
83
+ <table summary="table with svn commits">
84
+ <tr>
85
+ <th id="rev">rev</th>
86
+ <th id="when">when</th>
87
+ <th id="author">author</th>
88
+ <th id="commit_msg" style="text-align: left">commit</th>
89
+ </tr>
90
+ <% entries.each do |commit| %>
91
+ <tr>
92
+ <td><a href="<%= commit_url %><%= commit.rev %>"><%= commit.rev %></a></td>
93
+ <td><%= commit.time %></td>
94
+ <td><a href="<%= user %><%= commit.author %>"><%= commit.author %></a></td>
95
+ <td>
96
+ <b><p><%= commit.msg %></p></b>
97
+ <% commit.diff(text.split(",")).each do |line| %>
98
+ <% if line.start_with?("+++") || line.start_with?("---") %>
99
+ <p style="background: gainsboro;"><%= line.gsub(/[<>]/, '<' => '&lt;', '>' => '&gt;') %></p>
100
+ <% elsif text.split(",").any? { |t| line.include? t } %>
101
+ <p style="background: #F9F103;"><%= line.gsub(/[<>]/, '<' => '&lt;', '>' => '&gt;') %></p>
102
+ <% elsif line.start_with?("+") %>
103
+ <p style="<%= "background: darkseagreen;" %>"><%= line.gsub(/[<>]/, '<' => '&lt;', '>' => '&gt;') %></p>
104
+ <% elsif line.start_with?("-") %>
105
+ <p style="<%= "background: lightsalmon;" %>"><%= line.gsub(/[<>]/, '<' => '&lt;', '>' => '&gt;') %></p>
106
+ <% else %>
107
+ <p style="background: gainsboro;"><%= line.gsub(/[<>]/, '<' => '&lt;', '>' => '&gt;') %></p>
108
+ <% end %>
109
+ <% end %>
110
+ </td>
111
+ </tr>
112
+ <% end %>
113
+ </table>
114
+ <p>Posted by<a href="https://github.com/dgroup/lazylead">lazylead v<%= version %></a>.</p>
113
115
  </body>
114
116
  </html>