mail_diff 0.1.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.
- data/LICENSE +20 -0
- data/README.rdoc +27 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/lib/mail_diff/.DS_Store +0 -0
- data/lib/mail_diff/chunk.rb +51 -0
- data/lib/mail_diff/diff.rb +60 -0
- data/lib/mail_diff/html_generators/chunk_generator.rb +19 -0
- data/lib/mail_diff/html_generators/diff_generator.rb +41 -0
- data/lib/mail_diff/html_generators/line_generator.rb +69 -0
- data/lib/mail_diff/line.rb +63 -0
- data/lib/mail_diff/line_numbers.rb +75 -0
- data/lib/mail_diff.rb +15 -0
- data/mail_diff.gemspec +59 -0
- data/test/dummy_mail_diff_test.rb +11 -0
- data/test/example.udiff +212 -0
- data/test/stylesheet.css +65 -0
- metadata +97 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Ilya Sabanin
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
= Mail Diff
|
2
|
+
|
3
|
+
Similar to PrettyDiff (http://github.com/iSabanin/pretty_diff) but for email messages. Contains a lot of hours of markup tuning to make diffs viewable in almost all known browsers.
|
4
|
+
|
5
|
+
Heavily used in Beanstalk (http://beanstalkapp.com) application.
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
gem install mail_diff
|
10
|
+
|
11
|
+
== Example
|
12
|
+
|
13
|
+
A quick example will tell it all:
|
14
|
+
|
15
|
+
udiff = File.read("awesome.diff")
|
16
|
+
mail_diff = MailDiff::Diff.new(udiff)
|
17
|
+
mail_diff.to_html
|
18
|
+
|
19
|
+
Wrap it with HTML, add some styles and you will get something like this:
|
20
|
+
|
21
|
+
http://ilya.sabanin.ru/projects/pretty_diff_example.html
|
22
|
+
|
23
|
+
== Features
|
24
|
+
|
25
|
+
Same as in PrettyDiff (http://github.com/iSabanin/pretty_diff).
|
26
|
+
|
27
|
+
Copyright (c) 2011 Ilya Sabanin and Eugene Fedorenko, Wildbit; see LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "mail_diff"
|
8
|
+
gem.summary = %Q{Unified Diff to HTML converter for emails}
|
9
|
+
gem.description = %Q{Version of PrettyDiff that generate markup that works in most email clients}
|
10
|
+
gem.email = "ilya.sabanin@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/iSabanin/mail_diff"
|
12
|
+
gem.authors = ["Ilya Sabanin"]
|
13
|
+
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/test_*.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rcov/rcovtask'
|
30
|
+
Rcov::RcovTask.new do |test|
|
31
|
+
test.libs << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
rescue LoadError
|
36
|
+
task :rcov do
|
37
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
task :test => :check_dependencies
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "mail_diff #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
Binary file
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#
|
2
|
+
# Represent a single piece of a diff.
|
3
|
+
#
|
4
|
+
class MailDiff::Chunk #:nodoc:
|
5
|
+
attr_reader :diff, :meta_info, :content, :lines
|
6
|
+
|
7
|
+
def initialize(diff, meta_info, content)
|
8
|
+
@diff = diff
|
9
|
+
@meta_info = meta_info
|
10
|
+
@content = content
|
11
|
+
end
|
12
|
+
|
13
|
+
# Generate HTML presentation for a Chunk. Return a string.
|
14
|
+
def to_html
|
15
|
+
# We have to find lines before we can call line numbers methods.
|
16
|
+
find_lines!
|
17
|
+
generator.generate
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return LineNumbers object that represents two columns of numbers
|
21
|
+
# that will be displayed on the left of the HTML presentation.
|
22
|
+
#
|
23
|
+
# IMPORTANT! Before calling this method it's essential to call "find_lines!" first,
|
24
|
+
# otherwise the array will be empty.
|
25
|
+
def line_numbers
|
26
|
+
@_line_numbers ||= MailDiff::LineNumbers.new(diff, meta_info)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def generator
|
32
|
+
@_generator ||= MailDiff::ChunkGenerator.new(self)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Parse the content searching for lines. Initialize Line object for every line.
|
36
|
+
# Return an array of Line objects.
|
37
|
+
def find_lines!
|
38
|
+
@lines = []
|
39
|
+
@lines.tap do
|
40
|
+
content.split(/\r?\n/).each do |line_str|
|
41
|
+
line = MailDiff::Line.new(diff, line_str)
|
42
|
+
next if line.ignore?
|
43
|
+
@lines << line
|
44
|
+
line_numbers.act_on_line(line)
|
45
|
+
line.left_number = line_numbers.left_column.last
|
46
|
+
line.right_number = line_numbers.right_column.last
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Main class to interact with. In fact this is the only class you should interact with
|
5
|
+
# when using the library.
|
6
|
+
#
|
7
|
+
# === Usage example
|
8
|
+
# mail_diff = MailDiff::Diff.new(udiff)
|
9
|
+
# mail_diff.to_html
|
10
|
+
#
|
11
|
+
# Keep in mind that Diff will automatically escape all HTML tags from the intput string
|
12
|
+
# so that it doesn't interfere with the output.
|
13
|
+
#
|
14
|
+
class MailDiff::Diff
|
15
|
+
CHUNK_REGEXP = /@@ .+ @@\n/
|
16
|
+
|
17
|
+
attr_reader :input, :options
|
18
|
+
|
19
|
+
# Create new Diff object.
|
20
|
+
# Accept a String in unified diff format and options hash.
|
21
|
+
def initialize(unified_diff, options={})
|
22
|
+
@input = escape_html(unified_diff)
|
23
|
+
@options = options
|
24
|
+
end
|
25
|
+
|
26
|
+
# Generate HTML presentation. Return a string.
|
27
|
+
def to_html
|
28
|
+
generator.generate
|
29
|
+
end
|
30
|
+
|
31
|
+
# Return an array of Chunk objects that Diff found in the input.
|
32
|
+
def chunks
|
33
|
+
@_chunks ||= find_chunks(input)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def generator
|
39
|
+
@_generator ||= MailDiff::DiffGenerator.new(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Parse the input for diff chunks and initialize a Chunk object for each of them.
|
43
|
+
# Return an array of Chunks.
|
44
|
+
def find_chunks(text)
|
45
|
+
meta_info = text.scan(CHUNK_REGEXP)
|
46
|
+
chunks = []
|
47
|
+
chunks.tap do
|
48
|
+
split = text.split(CHUNK_REGEXP)
|
49
|
+
split.shift
|
50
|
+
split.each_with_index do |lines, idx|
|
51
|
+
chunks << MailDiff::Chunk.new(self, meta_info[idx], lines)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def escape_html(input_text)
|
57
|
+
CGI.escapeHTML(input_text)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class MailDiff::DiffGenerator
|
2
|
+
|
3
|
+
attr_reader :diff
|
4
|
+
|
5
|
+
def initialize(diff)
|
6
|
+
@diff = diff
|
7
|
+
end
|
8
|
+
|
9
|
+
def generate
|
10
|
+
chunks_html = diff.chunks.map{|c| c.to_html}.join(chunk_separator_html).to_s
|
11
|
+
intro_html + chunks_html + outro_html
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def intro_html
|
17
|
+
%Q[<div style="padding: 0 0 1em; overflow:auto; overflow-y: hidden;">
|
18
|
+
<table width="100%" border="0" cellspacing="0" cellpadding="0" bgcolor="#F9F9F9" style="margin: 0; border: 1px solid #E5E5E5; border-collapse: collapse; padding: 0; font: 11px Monaco, 'Lucida Console', Consolas, 'Courier New', monospace;">]
|
19
|
+
end
|
20
|
+
|
21
|
+
def chunk_separator_html
|
22
|
+
%Q[<tr>
|
23
|
+
<td bgcolor="#F3F3F3" style="border-right: 1px solid #E5E5E5; padding: 0 .75em;"> </td>
|
24
|
+
<td bgcolor="#F3F3F3" style="border-right: 1px solid #E5E5E5; padding: 0 .75em;"> </td>
|
25
|
+
<td style="padding: 0 1em;"> </td>
|
26
|
+
</tr>
|
27
|
+
<tr>
|
28
|
+
<td colspan="3" height="2" bgcolor="#E5E5E5"></td>
|
29
|
+
</tr>
|
30
|
+
<tr>
|
31
|
+
<td bgcolor="#F3F3F3" style="border-right: 1px solid #E5E5E5; padding: 0 .75em;"> </td>
|
32
|
+
<td bgcolor="#F3F3F3" style="border-right: 1px solid #E5E5E5; padding: 0 .75em;"> </td>
|
33
|
+
<td style="padding: 0 1em;"> </td>
|
34
|
+
</tr>]
|
35
|
+
end
|
36
|
+
|
37
|
+
def outro_html
|
38
|
+
%Q[</table></div>]
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class MailDiff::LineGenerator
|
2
|
+
|
3
|
+
attr_reader :line
|
4
|
+
|
5
|
+
def initialize(line)
|
6
|
+
@line = line
|
7
|
+
end
|
8
|
+
|
9
|
+
def generate
|
10
|
+
if line.added?
|
11
|
+
added_html
|
12
|
+
elsif line.deleted?
|
13
|
+
deleted_html
|
14
|
+
else
|
15
|
+
not_modified_html
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def wrapper(&block)
|
22
|
+
line_start +
|
23
|
+
line_numbers +
|
24
|
+
yield +
|
25
|
+
line_end
|
26
|
+
end
|
27
|
+
|
28
|
+
def line_start
|
29
|
+
%Q[<tr>]
|
30
|
+
end
|
31
|
+
|
32
|
+
def line_end
|
33
|
+
%Q[</tr>]
|
34
|
+
end
|
35
|
+
|
36
|
+
def line_numbers
|
37
|
+
%Q[<td width="4%" align="right" valign="top" bgcolor="#F3F3F3" style="border-right: 1px solid #E5E5E5; padding: .3em .75em; color: #BBB;">
|
38
|
+
<pre style="width:auto; margin: 0; border: 0; padding: 0; font: 11px Monaco, 'Lucida Console', Consolas, 'Courier New', monospace;">#{ line.left_number }</pre>
|
39
|
+
</td>
|
40
|
+
<td width="4%" align="right" valign="top" bgcolor="#F3F3F3" style="border-right: 1px solid #E5E5E5; padding: .3em .75em; color: #BBB;">
|
41
|
+
<pre style="width:auto; margin: 0; border: 0; padding: 0; font: 11px Monaco, 'Lucida Console', Consolas, 'Courier New', monospace;">#{ line.right_number }</pre>
|
42
|
+
</td>]
|
43
|
+
end
|
44
|
+
|
45
|
+
def added_html
|
46
|
+
wrapper do
|
47
|
+
%Q[<td valign="top" style="padding: .3em 1em;">
|
48
|
+
<pre style="width:auto; margin: 0; border: 0; padding: 0; white-space: pre-wrap; font: 11px Monaco, 'Lucida Console', Consolas, 'Courier New', monospace;"><span class="gi">#{ line.format }</span></pre>
|
49
|
+
</td>]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def deleted_html
|
54
|
+
wrapper do
|
55
|
+
%Q[<td valign="top" style="padding: .3em 1em;">
|
56
|
+
<pre style="width:auto; margin: 0; border: 0; padding: 0; white-space: pre-wrap; font: 11px Monaco, 'Lucida Console', Consolas, 'Courier New', monospace;"><span class="gd">#{ line.format }</span></pre>
|
57
|
+
</td>]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def not_modified_html
|
62
|
+
wrapper do
|
63
|
+
%Q[<td valign="top" style="padding: .3em 1em;">
|
64
|
+
<pre style="width:auto; margin: 0; border: 0; padding: 0; white-space: pre-wrap; font: 11px Monaco, 'Lucida Console', Consolas, 'Courier New', monospace;">#{ line.format }</pre>
|
65
|
+
</td>]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#
|
2
|
+
# Represent a single line of the diff.
|
3
|
+
#
|
4
|
+
class MailDiff::Line #:nodoc:
|
5
|
+
|
6
|
+
attr_reader :diff, :content
|
7
|
+
attr_accessor :left_number, :right_number
|
8
|
+
|
9
|
+
def initialize(diff, content)
|
10
|
+
@diff = diff
|
11
|
+
@content = content
|
12
|
+
end
|
13
|
+
|
14
|
+
# Generate HTML presentation for a Line. Return a string.
|
15
|
+
def to_html
|
16
|
+
generator.generate
|
17
|
+
end
|
18
|
+
|
19
|
+
# Prepare Line contents for injection into HTML structure.
|
20
|
+
# Currently used for replacing Tab symbols with spaces.
|
21
|
+
# Return a string.
|
22
|
+
def format
|
23
|
+
content.gsub("\t", ' ')
|
24
|
+
end
|
25
|
+
|
26
|
+
# Unified Diff sometimes emit a special line at the end of the file
|
27
|
+
# that we should not display in the output.
|
28
|
+
# Return true or false.
|
29
|
+
def ignore?
|
30
|
+
content =~ /\/
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return status of the Line. Can be :added, :deleted or :not_modified.
|
34
|
+
def status
|
35
|
+
case content
|
36
|
+
when /^\+/
|
37
|
+
:added
|
38
|
+
when /^\-/
|
39
|
+
:deleted
|
40
|
+
else
|
41
|
+
:not_modified
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def added?
|
46
|
+
status == :added
|
47
|
+
end
|
48
|
+
|
49
|
+
def deleted?
|
50
|
+
status == :deleted
|
51
|
+
end
|
52
|
+
|
53
|
+
def not_modified?
|
54
|
+
status == :not_modified
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def generator
|
60
|
+
@_generator ||= MailDiff::LineGenerator.new(self)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#
|
2
|
+
# Represent 2 columns of numbers that will be displayed
|
3
|
+
# on the left of the HTML presentation.
|
4
|
+
#
|
5
|
+
class MailDiff::LineNumbers #:nodoc:
|
6
|
+
|
7
|
+
attr_reader :diff, :meta_info
|
8
|
+
|
9
|
+
def initialize(diff, meta)
|
10
|
+
@diff = diff
|
11
|
+
@meta_info = meta
|
12
|
+
end
|
13
|
+
|
14
|
+
# Increase either left column of numbers, right or both of them; depending on the Line status.
|
15
|
+
def act_on_line(line)
|
16
|
+
if line.added?
|
17
|
+
increase_right
|
18
|
+
elsif line.deleted?
|
19
|
+
increase_left
|
20
|
+
else
|
21
|
+
increase_both
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def left_column
|
26
|
+
@left_column ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
def right_column
|
30
|
+
@right_column ||= []
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Search for information about line numbers changes provided by unified diff format.
|
36
|
+
def scan_meta(target)
|
37
|
+
meta_info.scan(target).flatten.first
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return starting number for the left column according to unified diff information.
|
41
|
+
def left_starts_at
|
42
|
+
scan_meta(/^@@ -(\d+),/).to_i
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return starting number for the right column according to unified diff information.
|
46
|
+
def right_starts_at
|
47
|
+
scan_meta(/\+(\d+),\d+ @@$/).to_i
|
48
|
+
end
|
49
|
+
|
50
|
+
# Increase left column line number by one.
|
51
|
+
def increase_left
|
52
|
+
left_column << increase_or_start(:left)
|
53
|
+
right_column << nil
|
54
|
+
end
|
55
|
+
|
56
|
+
# Increase right column line number by one.
|
57
|
+
def increase_right
|
58
|
+
left_column << nil
|
59
|
+
right_column << increase_or_start(:right)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Increase both columns line numbers by one.
|
63
|
+
def increase_both
|
64
|
+
left_column << increase_or_start(:left)
|
65
|
+
right_column << increase_or_start(:right)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Either increasing existing line number by one or using the initial number provided by
|
69
|
+
# unified diff format.
|
70
|
+
def increase_or_start(which)
|
71
|
+
previous = send("#{which}_column").reverse.find{|e| !e.nil?}
|
72
|
+
if previous then previous + 1 else send("#{which}_starts_at") end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
data/lib/mail_diff.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module MailDiff #:nodoc:
|
2
|
+
end
|
3
|
+
|
4
|
+
def require_local(suffix)
|
5
|
+
require(File.expand_path(File.join(File.dirname(__FILE__), suffix)))
|
6
|
+
end
|
7
|
+
|
8
|
+
require_local 'mail_diff/diff'
|
9
|
+
require_local 'mail_diff/chunk'
|
10
|
+
require_local 'mail_diff/line_numbers'
|
11
|
+
require_local 'mail_diff/line'
|
12
|
+
|
13
|
+
require_local 'mail_diff/html_generators/diff_generator'
|
14
|
+
require_local 'mail_diff/html_generators/chunk_generator'
|
15
|
+
require_local 'mail_diff/html_generators/line_generator'
|
data/mail_diff.gemspec
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{mail_diff}
|
8
|
+
s.version = "0.1.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Ilya Sabanin"]
|
12
|
+
s.date = %q{2011-04-18}
|
13
|
+
s.description = %q{Version of PrettyDiff that generate markup that works in most email clients}
|
14
|
+
s.email = %q{ilya.sabanin@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"LICENSE",
|
21
|
+
"README.rdoc",
|
22
|
+
"Rakefile",
|
23
|
+
"VERSION",
|
24
|
+
"lib/mail_diff.rb",
|
25
|
+
"lib/mail_diff/.DS_Store",
|
26
|
+
"lib/mail_diff/chunk.rb",
|
27
|
+
"lib/mail_diff/diff.rb",
|
28
|
+
"lib/mail_diff/html_generators/chunk_generator.rb",
|
29
|
+
"lib/mail_diff/html_generators/diff_generator.rb",
|
30
|
+
"lib/mail_diff/html_generators/line_generator.rb",
|
31
|
+
"lib/mail_diff/line.rb",
|
32
|
+
"lib/mail_diff/line_numbers.rb",
|
33
|
+
"mail_diff.gemspec",
|
34
|
+
"test/dummy_mail_diff_test.rb",
|
35
|
+
"test/example.udiff",
|
36
|
+
"test/stylesheet.css"
|
37
|
+
]
|
38
|
+
s.homepage = %q{http://github.com/iSabanin/mail_diff}
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.rubygems_version = %q{1.3.7}
|
41
|
+
s.summary = %q{Unified Diff to HTML converter for emails}
|
42
|
+
s.test_files = [
|
43
|
+
"test/dummy_mail_diff_test.rb"
|
44
|
+
]
|
45
|
+
|
46
|
+
if s.respond_to? :specification_version then
|
47
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
48
|
+
s.specification_version = 3
|
49
|
+
|
50
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
51
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
54
|
+
end
|
55
|
+
else
|
56
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'mail_diff')
|
2
|
+
|
3
|
+
file = File.read("example.udiff")
|
4
|
+
diff = MailDiff::Diff.new(file)
|
5
|
+
|
6
|
+
File.open("test_result.html", 'w') do |f|
|
7
|
+
f << File.read("stylesheet.css")
|
8
|
+
f << diff.to_html
|
9
|
+
end
|
10
|
+
|
11
|
+
`open test_result.html`
|
data/test/example.udiff
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
--- Revision 12
|
2
|
+
+++ Revision 17
|
3
|
+
@@ -4,7 +4,7 @@
|
4
|
+
<head>
|
5
|
+
<meta charset="utf-8">
|
6
|
+
<title>Team Schedules</title>
|
7
|
+
- <style type="text/css"> article,aside,dialog,figure,footer,header,hgroup,nav,section{display:block}html,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,hr,p,blockquote,pre,abbr,address,cite,code,del,dfn,em,img,ins,kbd,q,samp,small,strong,sub,sup,var,b,i,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,dialog,figure,footer,header,hgroup,menu,nav,section,time,mark,audio,video{margin:0;border:0;padding:0;outline:0;font-size:1em;vertical-align:baseline;background-color:transparent}html,body{height:100%}body{background-color:#FFF;color:#333;font:normal .8em/1.4 'Helvetica Neue',Arial,Helvetica,sans-serif}select{width:160px}table{width:100%;height:100%;border-collapse:collapse;border-spacing:0}table th,table td{border-bottom:1px solid rgba(0,0,0,.02);vertical-align:middle}table thead th{width:10%;padding:1em 0}table tbody th{padding-right:1em;border-color:#DDD;background-color:#EEE;color:#AAA;font-size:.85em;font-weight:normal;text-align:right}table th.now{background-color:#FFFFD5;color:#908E00}table td.working{background-color:#D5F4FF} </style>
|
8
|
+
+ <style type="text/css"> article,aside,dialog,figure,footer,header,hgroup,nav,section{display:block}html,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,hr,p,blockquote,pre,abbr,address,cite,code,del,dfn,em,img,ins,kbd,q,samp,small,strong,sub,sup,var,b,i,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,dialog,figure,footer,header,hgroup,menu,nav,section,time,mark,audio,video{margin:0;border:0;padding:0;outline:0;font-size:1em;vertical-align:baseline;background-color:transparent}html,body{height:100%}body{background-color:#FFF;color:#333;font:normal .8em/1.4 'Helvetica Neue',Arial,Helvetica,sans-serif}select{width:160px}table{width:100%;height:100%;border-collapse:collapse;border-spacing:0}table th,table td{border-bottom:1px solid rgba(0,0,0,.02);vertical-align:middle}table thead th{width:10%;padding:1em 0}table thead th span{color:#AAA;font-size:.85em;font-weight:normal;vertical-align:top;}table tbody th{padding-right:1em;border-color:#DDD;background-color:#EEE;color:#AAA;font-size:.85em;font-weight:normal;text-align:right}table th.now{background-color:#FFFFD5;color:#908E00}table td.working{background-color:#D5F4FF} </style>
|
9
|
+
</head>
|
10
|
+
|
11
|
+
<body>
|
12
|
+
@@ -31,7 +31,7 @@
|
13
|
+
<td></td> <!-- Eugene -->
|
14
|
+
<td></td> <!-- Gilbert -->
|
15
|
+
<td></td> <!-- Ilya -->
|
16
|
+
- <td></td> <!-- Dima -->
|
17
|
+
+ <td>*</td> <!-- Dima -->
|
18
|
+
<td></td> <!-- Hristo -->
|
19
|
+
<td></td> <!-- Igor -->
|
20
|
+
</tr>
|
21
|
+
@@ -42,7 +42,7 @@
|
22
|
+
<td></td> <!-- Eugene -->
|
23
|
+
<td></td> <!-- Gilbert -->
|
24
|
+
<td>*</td> <!-- Ilya -->
|
25
|
+
- <td></td> <!-- Dima -->
|
26
|
+
+ <td>*</td> <!-- Dima -->
|
27
|
+
<td></td> <!-- Hristo -->
|
28
|
+
<td></td> <!-- Igor -->
|
29
|
+
</tr>
|
30
|
+
@@ -53,7 +53,7 @@
|
31
|
+
<td>*</td> <!-- Eugene -->
|
32
|
+
<td>*</td> <!-- Gilbert -->
|
33
|
+
<td>*</td> <!-- Ilya -->
|
34
|
+
- <td></td> <!-- Dima -->
|
35
|
+
+ <td>*</td> <!-- Dima -->
|
36
|
+
<td>*</td> <!-- Hristo -->
|
37
|
+
<td>*</td> <!-- Igor -->
|
38
|
+
</tr>
|
39
|
+
@@ -64,7 +64,7 @@
|
40
|
+
<td>*</td> <!-- Eugene -->
|
41
|
+
<td>*</td> <!-- Gilbert -->
|
42
|
+
<td>*</td> <!-- Ilya -->
|
43
|
+
- <td></td> <!-- Dima -->
|
44
|
+
+ <td>*</td> <!-- Dima -->
|
45
|
+
<td>*</td> <!-- Hristo -->
|
46
|
+
<td>*</td> <!-- Igor -->
|
47
|
+
</tr>
|
48
|
+
@@ -125,8 +125,8 @@
|
49
|
+
</tr>
|
50
|
+
<tr>
|
51
|
+
<th>10 AM</th>
|
52
|
+
- <td></td> <!-- Chris -->
|
53
|
+
- <td></td> <!-- Natalie -->
|
54
|
+
+ <td>*</td> <!-- Chris -->
|
55
|
+
+ <td>*</td> <!-- Natalie -->
|
56
|
+
<td>*</td> <!-- Eugene -->
|
57
|
+
<td>*</td> <!-- Gilbert -->
|
58
|
+
<td>*</td> <!-- Ilya -->
|
59
|
+
@@ -136,8 +136,8 @@
|
60
|
+
</tr>
|
61
|
+
<tr>
|
62
|
+
<th>11 AM</th>
|
63
|
+
- <td></td> <!-- Chris -->
|
64
|
+
- <td></td> <!-- Natalie -->
|
65
|
+
+ <td>*</td> <!-- Chris -->
|
66
|
+
+ <td>*</td> <!-- Natalie -->
|
67
|
+
<td>*</td> <!-- Eugene -->
|
68
|
+
<td>*</td> <!-- Gilbert -->
|
69
|
+
<td>*</td> <!-- Ilya -->
|
70
|
+
@@ -147,10 +147,10 @@
|
71
|
+
</tr>
|
72
|
+
<tr>
|
73
|
+
<th>Noon</th>
|
74
|
+
- <td></td> <!-- Chris -->
|
75
|
+
- <td></td> <!-- Natalie -->
|
76
|
+
+ <td>*</td> <!-- Chris -->
|
77
|
+
+ <td>*</td> <!-- Natalie -->
|
78
|
+
<td>*</td> <!-- Eugene -->
|
79
|
+
- <td>*</td> <!-- Gilbert -->
|
80
|
+
+ <td></td> <!-- Gilbert -->
|
81
|
+
<td></td> <!-- Ilya -->
|
82
|
+
<td></td> <!-- Dima -->
|
83
|
+
<td>*</td> <!-- Hristo -->
|
84
|
+
@@ -158,20 +158,20 @@
|
85
|
+
</tr>
|
86
|
+
<tr>
|
87
|
+
<th>1 PM</th>
|
88
|
+
- <td></td> <!-- Chris -->
|
89
|
+
- <td></td> <!-- Natalie -->
|
90
|
+
- <td>*</td> <!-- Eugene -->
|
91
|
+
- <td>*</td> <!-- Gilbert -->
|
92
|
+
+ <td>*</td> <!-- Chris -->
|
93
|
+
+ <td>*</td> <!-- Natalie -->
|
94
|
+
+ <td></td> <!-- Eugene -->
|
95
|
+
+ <td></td> <!-- Gilbert -->
|
96
|
+
<td></td> <!-- Ilya -->
|
97
|
+
<td></td> <!-- Dima -->
|
98
|
+
- <td>*</td> <!-- Hristo -->
|
99
|
+
- <td>*</td> <!-- Igor -->
|
100
|
+
+ <td></td> <!-- Hristo -->
|
101
|
+
+ <td></td> <!-- Igor -->
|
102
|
+
</tr>
|
103
|
+
<tr>
|
104
|
+
<th>2 PM</th>
|
105
|
+
- <td></td> <!-- Chris -->
|
106
|
+
- <td></td> <!-- Natalie -->
|
107
|
+
- <td>*</td> <!-- Eugene -->
|
108
|
+
+ <td>*</td> <!-- Chris -->
|
109
|
+
+ <td>*</td> <!-- Natalie -->
|
110
|
+
+ <td></td> <!-- Eugene -->
|
111
|
+
<td></td> <!-- Gilbert -->
|
112
|
+
<td></td> <!-- Ilya -->
|
113
|
+
<td></td> <!-- Dima -->
|
114
|
+
@@ -180,8 +180,8 @@
|
115
|
+
</tr>
|
116
|
+
<tr>
|
117
|
+
<th>3 PM</th>
|
118
|
+
- <td></td> <!-- Chris -->
|
119
|
+
- <td></td> <!-- Natalie -->
|
120
|
+
+ <td>*</td> <!-- Chris -->
|
121
|
+
+ <td>*</td> <!-- Natalie -->
|
122
|
+
<td></td> <!-- Eugene -->
|
123
|
+
<td></td> <!-- Gilbert -->
|
124
|
+
<td></td> <!-- Ilya -->
|
125
|
+
@@ -191,8 +191,8 @@
|
126
|
+
</tr>
|
127
|
+
<tr>
|
128
|
+
<th>4 PM</th>
|
129
|
+
- <td></td> <!-- Chris -->
|
130
|
+
- <td></td> <!-- Natalie -->
|
131
|
+
+ <td>*</td> <!-- Chris -->
|
132
|
+
+ <td>*</td> <!-- Natalie -->
|
133
|
+
<td></td> <!-- Eugene -->
|
134
|
+
<td></td> <!-- Gilbert -->
|
135
|
+
<td></td> <!-- Ilya -->
|
136
|
+
@@ -202,8 +202,8 @@
|
137
|
+
</tr>
|
138
|
+
<tr>
|
139
|
+
<th>5 PM</th>
|
140
|
+
- <td></td> <!-- Chris -->
|
141
|
+
- <td></td> <!-- Natalie -->
|
142
|
+
+ <td>*</td> <!-- Chris -->
|
143
|
+
+ <td>*</td> <!-- Natalie -->
|
144
|
+
<td></td> <!-- Eugene -->
|
145
|
+
<td></td> <!-- Gilbert -->
|
146
|
+
<td></td> <!-- Ilya -->
|
147
|
+
@@ -240,7 +240,7 @@
|
148
|
+
<td></td> <!-- Eugene -->
|
149
|
+
<td></td> <!-- Gilbert -->
|
150
|
+
<td></td> <!-- Ilya -->
|
151
|
+
- <td></td> <!-- Dima -->
|
152
|
+
+ <td>*</td> <!-- Dima -->
|
153
|
+
<td></td> <!-- Hristo -->
|
154
|
+
<td></td> <!-- Igor -->
|
155
|
+
</tr>
|
156
|
+
@@ -251,7 +251,7 @@
|
157
|
+
<td></td> <!-- Eugene -->
|
158
|
+
<td></td> <!-- Gilbert -->
|
159
|
+
<td></td> <!-- Ilya -->
|
160
|
+
- <td></td> <!-- Dima -->
|
161
|
+
+ <td>*</td> <!-- Dima -->
|
162
|
+
<td></td> <!-- Hristo -->
|
163
|
+
<td></td> <!-- Igor -->
|
164
|
+
</tr>
|
165
|
+
@@ -262,7 +262,7 @@
|
166
|
+
<td></td> <!-- Eugene -->
|
167
|
+
<td></td> <!-- Gilbert -->
|
168
|
+
<td>*</td> <!-- Ilya -->
|
169
|
+
- <td></td> <!-- Dima -->
|
170
|
+
+ <td>*</td> <!-- Dima -->
|
171
|
+
<td></td> <!-- Hristo -->
|
172
|
+
<td></td> <!-- Igor -->
|
173
|
+
</tr>
|
174
|
+
@@ -273,7 +273,7 @@
|
175
|
+
<td></td> <!-- Eugene -->
|
176
|
+
<td></td> <!-- Gilbert -->
|
177
|
+
<td>*</td> <!-- Ilya -->
|
178
|
+
- <td></td> <!-- Dima -->
|
179
|
+
+ <td>*</td> <!-- Dima -->
|
180
|
+
<td></td> <!-- Hristo -->
|
181
|
+
<td></td> <!-- Igor -->
|
182
|
+
</tr>
|
183
|
+
@@ -284,7 +284,7 @@
|
184
|
+
<td></td> <!-- Eugene -->
|
185
|
+
<td></td> <!-- Gilbert -->
|
186
|
+
<td></td> <!-- Ilya -->
|
187
|
+
- <td></td> <!-- Dima -->
|
188
|
+
+ <td>*</td> <!-- Dima -->
|
189
|
+
<td></td> <!-- Hristo -->
|
190
|
+
<td></td> <!-- Igor -->
|
191
|
+
</tr>
|
192
|
+
@@ -299,8 +299,19 @@
|
193
|
+
var now = new Date();
|
194
|
+
|
195
|
+
$("td:contains('*')").addClass('working').text('');
|
196
|
+
- $('table tbody tr:eq(' + (now.getHours() - 7) + ') th').addClass('now')
|
197
|
+
|
198
|
+
+ $('table tbody tr:eq(' + (now.getHours() - 7) + ') th').addClass('now');
|
199
|
+
+
|
200
|
+
+ $('table thead th:gt(0)').each(function(i){
|
201
|
+
+ var hours = 0;
|
202
|
+
+
|
203
|
+
+ $('table tbody tr').find('td:eq(' + i + ')').each(function(){
|
204
|
+
+ if ($(this).hasClass('working')) hours++;
|
205
|
+
+ });
|
206
|
+
+
|
207
|
+
+ $(this).append(' <span>' + hours + '</span>');
|
208
|
+
+ });
|
209
|
+
+
|
210
|
+
});
|
211
|
+
|
212
|
+
</script>
|
data/test/stylesheet.css
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
<style type="text/css">
|
2
|
+
|
3
|
+
/* Syntax Coloring */
|
4
|
+
|
5
|
+
pre span.lineno { background-color: #F0F0F0; }
|
6
|
+
pre .hll { background-color: #FFC; }
|
7
|
+
pre .c { color: #AAA; } /* Comment */
|
8
|
+
pre .err { color: #F00000; background-color: #F0A0A0; } /* Error */
|
9
|
+
pre .k { color: #00A; } /* Keyword */
|
10
|
+
pre .cm { color: #AAA; } /* Comment.Multiline */
|
11
|
+
pre .cp { color: #4C8317; } /* Comment.Preproc */
|
12
|
+
pre .c1 { color: #AAA; } /* Comment.Single */
|
13
|
+
pre .cs { color: #00A; } /* Comment.Special */
|
14
|
+
pre .gd { background-color: #FDD; color: #300; } /* Generic.Deleted */
|
15
|
+
pre .ge { } /* Generic.Emph */
|
16
|
+
pre .gr { color: #A00 } /* Generic.Error */
|
17
|
+
pre .gh { color: #000080; } /* Generic.Heading */
|
18
|
+
pre .gi { background-color: #E0F2CE; color: #030; } /* Generic.Inserted */
|
19
|
+
pre .go { color: #888 } /* Generic.Output */
|
20
|
+
pre .gp { color: #555 } /* Generic.Prompt */
|
21
|
+
pre .gs { } /* Generic.Strong */
|
22
|
+
pre .gu { color: #800080; } /* Generic.Subheading */
|
23
|
+
pre .gt { color: #A00 } /* Generic.Traceback */
|
24
|
+
pre .kc { color: #00A } /* Keyword.Constant */
|
25
|
+
pre .kd { color: #00A } /* Keyword.Declaration */
|
26
|
+
pre .kn { color: #00A } /* Keyword.Namespace */
|
27
|
+
pre .kp { color: #00A } /* Keyword.Pseudo */
|
28
|
+
pre .kr { color: #00A } /* Keyword.Reserved */
|
29
|
+
pre .kt { color: #0AA } /* Keyword.Type */
|
30
|
+
pre .m { color: #099 } /* Literal.Number */
|
31
|
+
pre .s { color: #A50 } /* Literal.String */
|
32
|
+
pre .na { color: #1E90FF } /* Name.Attribute */
|
33
|
+
pre .nb { color: #0AA } /* Name.Builtin */
|
34
|
+
pre .nc { color: #0A0; text-decoration: underline } /* Name.Class */
|
35
|
+
pre .no { color: #A00 } /* Name.Constant */
|
36
|
+
pre .nd { color: #888 } /* Name.Decorator */
|
37
|
+
pre .ni { color: #800000; } /* Name.Entity */
|
38
|
+
pre .nf { color: #0A0 } /* Name.Function */
|
39
|
+
pre .nn { color: #0AA; text-decoration: underline } /* Name.Namespace */
|
40
|
+
pre .nt { color: #1E90FF; } /* Name.Tag */
|
41
|
+
pre .nv { color: #A00 } /* Name.Variable */
|
42
|
+
pre .ow { color: #00A } /* Operator.Word */
|
43
|
+
pre .w { color: #BBB } /* Text.Whitespace */
|
44
|
+
pre .mf { color: #099 } /* Literal.Number.Float */
|
45
|
+
pre .mh { color: #099 } /* Literal.Number.Hex */
|
46
|
+
pre .mi { color: #099 } /* Literal.Number.Integer */
|
47
|
+
pre .mo { color: #099 } /* Literal.Number.Oct */
|
48
|
+
pre .sb { color: #A50 } /* Literal.String.Backtick */
|
49
|
+
pre .sc { color: #A50 } /* Literal.String.Char */
|
50
|
+
pre .sd { color: #A50 } /* Literal.String.Doc */
|
51
|
+
pre .s2 { color: #A50 } /* Literal.String.Double */
|
52
|
+
pre .se { color: #A50 } /* Literal.String.Escape */
|
53
|
+
pre .sh { color: #A50 } /* Literal.String.Heredoc */
|
54
|
+
pre .si { color: #A50 } /* Literal.String.Interpol */
|
55
|
+
pre .sx { color: #A50 } /* Literal.String.Other */
|
56
|
+
pre .sr { color: #099 } /* Literal.String.Regex */
|
57
|
+
pre .s1 { color: #A50 } /* Literal.String.Single */
|
58
|
+
pre .ss { color: #00A } /* Literal.String.Symbol */
|
59
|
+
pre .bp { color: #0AA } /* Name.Builtin.Pseudo */
|
60
|
+
pre .vc { color: #A00 } /* Name.Variable.Class */
|
61
|
+
pre .vg { color: #A00 } /* Name.Variable.Global */
|
62
|
+
pre .vi { color: #A00 } /* Name.Variable.Instance */
|
63
|
+
pre .il { color: #099 } /* Literal.Number.Integer.Long */
|
64
|
+
|
65
|
+
</style>
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mail_diff
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Ilya Sabanin
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-04-18 00:00:00 +08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: thoughtbot-shoulda
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
description: Version of PrettyDiff that generate markup that works in most email clients
|
36
|
+
email: ilya.sabanin@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- LICENSE
|
43
|
+
- README.rdoc
|
44
|
+
files:
|
45
|
+
- LICENSE
|
46
|
+
- README.rdoc
|
47
|
+
- Rakefile
|
48
|
+
- VERSION
|
49
|
+
- lib/mail_diff.rb
|
50
|
+
- lib/mail_diff/.DS_Store
|
51
|
+
- lib/mail_diff/chunk.rb
|
52
|
+
- lib/mail_diff/diff.rb
|
53
|
+
- lib/mail_diff/html_generators/chunk_generator.rb
|
54
|
+
- lib/mail_diff/html_generators/diff_generator.rb
|
55
|
+
- lib/mail_diff/html_generators/line_generator.rb
|
56
|
+
- lib/mail_diff/line.rb
|
57
|
+
- lib/mail_diff/line_numbers.rb
|
58
|
+
- mail_diff.gemspec
|
59
|
+
- test/dummy_mail_diff_test.rb
|
60
|
+
- test/example.udiff
|
61
|
+
- test/stylesheet.css
|
62
|
+
has_rdoc: true
|
63
|
+
homepage: http://github.com/iSabanin/mail_diff
|
64
|
+
licenses: []
|
65
|
+
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 3
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
requirements: []
|
90
|
+
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 1.3.7
|
93
|
+
signing_key:
|
94
|
+
specification_version: 3
|
95
|
+
summary: Unified Diff to HTML converter for emails
|
96
|
+
test_files:
|
97
|
+
- test/dummy_mail_diff_test.rb
|