pine2csv 1.0.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.
- data/.gitignore +4 -0
- data/COPYING +674 -0
- data/LICENSE +674 -0
- data/README.rdoc +16 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/bin/pine2csv +39 -0
- data/lib/pine2csv.rb +37 -0
- data/lib/pine_addressbook.treetop +178 -0
- data/test/helper.rb +10 -0
- data/test/test_pine2csv.rb +356 -0
- metadata +124 -0
data/README.rdoc
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
= pine2csv
|
|
2
|
+
|
|
3
|
+
A library for converting pine address books to csv format.
|
|
4
|
+
|
|
5
|
+
== Note on Patches/Pull Requests
|
|
6
|
+
|
|
7
|
+
* Fork the project.
|
|
8
|
+
* Make your feature addition or bug fix.
|
|
9
|
+
* Commit, do not mess with rakefile, version, or history. (if you
|
|
10
|
+
want to have your own version, that is fine but bump version in a
|
|
11
|
+
commit by itself I can ignore when I pull)
|
|
12
|
+
* Send me a pull request. Bonus points for topic branches.
|
|
13
|
+
|
|
14
|
+
== Copyright
|
|
15
|
+
|
|
16
|
+
Copyright (c) 2010 Ben Walton. See LICENSE for details.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'jeweler'
|
|
6
|
+
Jeweler::Tasks.new do |gem|
|
|
7
|
+
gem.name = "pine2csv"
|
|
8
|
+
gem.summary = %Q{Convert pine address books to csv format}
|
|
9
|
+
gem.description = %Q{A library and binary for converting pine address books to csv format.}
|
|
10
|
+
gem.email = "bwalton@artsci.utoronto.ca"
|
|
11
|
+
gem.homepage = "http://github.com/bdwalton/pine2csv"
|
|
12
|
+
gem.authors = ["Ben Walton"]
|
|
13
|
+
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
|
14
|
+
gem.executables = ["pine2csv"]
|
|
15
|
+
gem.add_dependency "treetop", ">= 1.4.8"
|
|
16
|
+
gem.add_dependency "polyglot", ">= 0.3.1"
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
Jeweler::GemcutterTasks.new
|
|
20
|
+
rescue LoadError
|
|
21
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
require 'rake/testtask'
|
|
25
|
+
Rake::TestTask.new(:test) do |test|
|
|
26
|
+
test.libs << 'lib' << 'test'
|
|
27
|
+
test.pattern = 'test/**/test_*.rb'
|
|
28
|
+
test.verbose = true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
begin
|
|
32
|
+
require 'rcov/rcovtask'
|
|
33
|
+
Rcov::RcovTask.new do |test|
|
|
34
|
+
test.libs << 'test'
|
|
35
|
+
test.pattern = 'test/**/test_*.rb'
|
|
36
|
+
test.verbose = true
|
|
37
|
+
end
|
|
38
|
+
rescue LoadError
|
|
39
|
+
task :rcov do
|
|
40
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
task :test => :check_dependencies
|
|
45
|
+
|
|
46
|
+
task :default => :test
|
|
47
|
+
|
|
48
|
+
require 'rake/rdoctask'
|
|
49
|
+
Rake::RDocTask.new do |rdoc|
|
|
50
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
|
51
|
+
|
|
52
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
53
|
+
rdoc.title = "pine2csv #{version}"
|
|
54
|
+
rdoc.rdoc_files.include('README*')
|
|
55
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
56
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.0.0
|
data/bin/pine2csv
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/ruby-enterprise/bin/ruby
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
|
|
5
|
+
retried = false
|
|
6
|
+
begin
|
|
7
|
+
require 'pine2csv'
|
|
8
|
+
rescue LoadError => e
|
|
9
|
+
$:.unshift File.join(File.dirname(File.expand_path(__FILE__)), "..", "lib")
|
|
10
|
+
if retried
|
|
11
|
+
raise
|
|
12
|
+
else
|
|
13
|
+
retried = true
|
|
14
|
+
retry
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
if ARGV.size.eql?(0)
|
|
19
|
+
$stderr.puts "Usage: #{File.basename $0} /path/to/pineabook"
|
|
20
|
+
$stderr.puts
|
|
21
|
+
$stderr.puts "No address book file specified..."
|
|
22
|
+
else
|
|
23
|
+
begin
|
|
24
|
+
abook = File.read(File.expand_path(ARGV[0]))
|
|
25
|
+
p2c = Pine2CSV.new(abook)
|
|
26
|
+
puts p2c.to_csv
|
|
27
|
+
rescue Pine2CSV::Error => e
|
|
28
|
+
$stderr.puts e.message
|
|
29
|
+
exit 1
|
|
30
|
+
rescue Errno::ENOENT => e
|
|
31
|
+
$stderr.puts "Invalid address book file (#{ARGV[0]}). File doesn't exist."
|
|
32
|
+
exit 1
|
|
33
|
+
rescue => e
|
|
34
|
+
$stderr.puts "Unhandled exception: #{e.class}"
|
|
35
|
+
$stderr.puts e.message
|
|
36
|
+
exit 1
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
data/lib/pine2csv.rb
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'treetop'
|
|
3
|
+
require 'polyglot'
|
|
4
|
+
require 'pine_addressbook'
|
|
5
|
+
|
|
6
|
+
class Pine2CSV
|
|
7
|
+
|
|
8
|
+
class Error < Exception
|
|
9
|
+
def initialize(msg)
|
|
10
|
+
super(msg)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr :abook
|
|
15
|
+
|
|
16
|
+
def initialize(abook = nil)
|
|
17
|
+
@abook = abook
|
|
18
|
+
@parser = PineAddressbookParser.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def abook=(data)
|
|
22
|
+
if data.kind_of?(String)
|
|
23
|
+
@abook = data
|
|
24
|
+
else
|
|
25
|
+
raise Pine2CSV::Error.new("Please pass an address book in string form")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_csv
|
|
30
|
+
raise Pine2CSV::Error.new("No address book data supplied.") unless abook
|
|
31
|
+
tree = @parser.parse(@abook)
|
|
32
|
+
|
|
33
|
+
raise Pine2CSV::Error.new("Parse failure:\n#{@parser.failure_reason}") unless tree
|
|
34
|
+
|
|
35
|
+
tree.to_csv
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
grammar PineAddressbook
|
|
2
|
+
rule addressbook
|
|
3
|
+
car:addressentry cdr:addressentry* eof {
|
|
4
|
+
def to_csv
|
|
5
|
+
csv = car.to_csv
|
|
6
|
+
csv += cdr.elements.map do |ae|
|
|
7
|
+
ae.to_csv
|
|
8
|
+
end.join('')
|
|
9
|
+
end
|
|
10
|
+
}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
rule addressentry
|
|
14
|
+
nick fieldsep pinename fieldsep recipient optionals? eol {
|
|
15
|
+
require 'csv'
|
|
16
|
+
def to_csv
|
|
17
|
+
values = [:nick, :pinename, :recipient].map do |token|
|
|
18
|
+
self.send(token).value
|
|
19
|
+
end.flatten
|
|
20
|
+
CSV.generate_line(values) + "\n"
|
|
21
|
+
end
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
rule optionals
|
|
26
|
+
fieldsep fcc (fieldsep entrycomment)?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
rule fieldsep
|
|
30
|
+
continuation? "\t" continuation?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
rule continuation
|
|
34
|
+
eol padding
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
rule padding
|
|
38
|
+
" "
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
rule nick
|
|
42
|
+
(!fieldsep .)* {
|
|
43
|
+
def value; text_value; end
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
rule pinename
|
|
48
|
+
qpname / bpname
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
rule recipient
|
|
52
|
+
personlist / person
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
rule personlist
|
|
56
|
+
lstart continuation? car:person cdr:(plistsep person)* continuation? lend {
|
|
57
|
+
def value
|
|
58
|
+
['group', car.value[1], cdr.elements.map do |p|
|
|
59
|
+
p.person.value[1]
|
|
60
|
+
end.flatten]
|
|
61
|
+
end
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
rule lstart
|
|
67
|
+
'('
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
rule lend
|
|
71
|
+
')'
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
rule person
|
|
75
|
+
qualified_address / emailaddress
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
rule qualified_address
|
|
79
|
+
recipname [\s]+ bracketedaddress {
|
|
80
|
+
def value
|
|
81
|
+
['person', "#{recipname.value} #{bracketedaddress.value[1]}"]
|
|
82
|
+
end
|
|
83
|
+
}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
rule plistsep
|
|
88
|
+
continuation? [\s]* [,] [\s]* continuation?
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
rule recipname
|
|
92
|
+
dqrname / sqrname / brname
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
rule dqrname
|
|
96
|
+
dq n:(!dq !eol . / dq dq)* dq {
|
|
97
|
+
def value; n.text_value.squeeze("'").squeeze("\""); end
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
rule sqrname
|
|
102
|
+
sq n:(!sq !eol . / sq sq)* sq {
|
|
103
|
+
def value; n.text_value.squeeze("'").squeeze("\""); end
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
rule brname
|
|
108
|
+
(!' <' !eol .)+ {
|
|
109
|
+
def value; text_value.squeeze("'").squeeze("\""); end
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
rule fcc
|
|
114
|
+
(!fieldsep !eol .)*
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
rule entrycomment
|
|
118
|
+
(!eol .)*
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
rule emailaddress
|
|
122
|
+
bracketedaddress / bareaddress
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
rule bracketedaddress
|
|
126
|
+
'<' bareaddress '>' {
|
|
127
|
+
def value
|
|
128
|
+
bareaddress.value
|
|
129
|
+
end
|
|
130
|
+
}
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
rule bareaddress
|
|
134
|
+
(![@] [a-zA-Z0-9._-])+ [@] (![\t>,\)\n] .)+ {
|
|
135
|
+
def value
|
|
136
|
+
['person', "<#{text_value}>"]
|
|
137
|
+
end
|
|
138
|
+
}
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
rule qpname
|
|
142
|
+
sqpname / dqpname
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
rule bpname
|
|
146
|
+
(!fieldsep .)* {
|
|
147
|
+
def value; text_value.squeeze("'").squeeze("\""); end
|
|
148
|
+
}
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
rule sqpname
|
|
152
|
+
sq n:(!sq . / sq sq)* sq {
|
|
153
|
+
def value; n.text_value.squeeze("'").squeeze("\""); end
|
|
154
|
+
}
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
rule dqpname
|
|
158
|
+
dq n:(!dq . / dq dq)* dq {
|
|
159
|
+
def value; n.text_value.squeeze("'").squeeze("\""); end
|
|
160
|
+
}
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
rule dq
|
|
164
|
+
["]
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
rule sq
|
|
168
|
+
[']
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
rule eol
|
|
172
|
+
"\n"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
rule eof
|
|
176
|
+
!.
|
|
177
|
+
end
|
|
178
|
+
end
|
data/test/helper.rb
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
require 'helper'
|
|
2
|
+
|
|
3
|
+
class TestPine2csv < Test::Unit::TestCase
|
|
4
|
+
context "Require valid data" do
|
|
5
|
+
setup do
|
|
6
|
+
@p = Pine2CSV.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
should "require abook data" do
|
|
10
|
+
assert_raise Pine2CSV::Error do
|
|
11
|
+
@p.to_csv
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
should "require string data" do
|
|
16
|
+
assert_raise Pine2CSV::Error do
|
|
17
|
+
@p.abook = @p
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context "Accept valid single entries" do
|
|
23
|
+
setup do
|
|
24
|
+
@p = Pine2CSV.new
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
should "take simple line" do
|
|
28
|
+
@p.abook = "ben\tben walton\tbwalton@example.org\n"
|
|
29
|
+
assert_equal @p.to_csv, "ben,ben walton,person,<bwalton@example.org>\n"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
should "allow blank nickname" do
|
|
33
|
+
@p.abook = "\tben walton\tbwalton@example.org\n"
|
|
34
|
+
assert_equal @p.to_csv, "\"\",ben walton,person,<bwalton@example.org>\n"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
should "allow blank name" do
|
|
38
|
+
@p.abook = "ben\t\tbwalton@example.org\n"
|
|
39
|
+
assert_equal @p.to_csv, "ben,\"\",person,<bwalton@example.org>\n"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
should "allow bracketed email recipient with no name" do
|
|
44
|
+
@p.abook = "ben\tben\t<bwalton@example.com>\n"
|
|
45
|
+
assert_equal @p.to_csv, "ben,ben,person,<bwalton@example.com>\n"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
should "allow an fcc without a comment" do
|
|
49
|
+
@p.abook = "ben\tben walton\tbwalton@example.org\tfcc\n"
|
|
50
|
+
assert_equal @p.to_csv, "ben,ben walton,person,<bwalton@example.org>\n"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
should "allow all 5 possible fields" do
|
|
54
|
+
@p.abook = "ben\tben walton\tbwalton@example.org\tfcc\tcomment text\n"
|
|
55
|
+
assert_equal @p.to_csv, "ben,ben walton,person,<bwalton@example.org>\n"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
should "allow double quoted name" do
|
|
59
|
+
@p.abook = "ben\t\"ben walton\"\tbwalton@example.org\tfcc\tcomment text\n"
|
|
60
|
+
assert_equal @p.to_csv, "ben,ben walton,person,<bwalton@example.org>\n"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
should "allow single quoted name" do
|
|
64
|
+
@p.abook = "ben\t'ben walton'\tbwalton@example.org\tfcc\tcomment text\n"
|
|
65
|
+
assert_equal @p.to_csv, "ben,ben walton,person,<bwalton@example.org>\n"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
should "allow single quoted name with embedded single quote" do
|
|
69
|
+
@p.abook = "ben\t'ben o''walton'\tbwalton@example.org\tfcc\tcomment text\n"
|
|
70
|
+
assert_equal @p.to_csv, "ben,ben o'walton,person,<bwalton@example.org>\n"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
should "allow double quoted name with embedded double quote" do
|
|
74
|
+
@p.abook = "ben\t\"ben \"\"quoted\"\" walton\"\tbwalton@example.org\tfcc\tcomment text\n"
|
|
75
|
+
assert_equal @p.to_csv, "ben,\"ben \"\"quoted\"\" walton\",person,<bwalton@example.org>\n"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
should "allow recipient with name and email" do
|
|
80
|
+
@p.abook = "ben\tben\tBen Walton <bwalton@example.org>\n"
|
|
81
|
+
assert_equal @p.to_csv, "ben,ben,person,Ben Walton <bwalton@example.org>\n"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
should "allow recipient with double quoted name" do
|
|
85
|
+
@p.abook = "ben\tben\t\"Ben Walton\" <bwalton@example.org>\n"
|
|
86
|
+
assert_equal @p.to_csv, "ben,ben,person,Ben Walton <bwalton@example.org>\n"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
should "allow recipient with single quoted name" do
|
|
90
|
+
@p.abook = "ben\tben\t'Ben Walton' <bwalton@example.org>\n"
|
|
91
|
+
assert_equal @p.to_csv, "ben,ben,person,Ben Walton <bwalton@example.org>\n"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
should "allow recipient with single quoted name with embedded double quote" do
|
|
95
|
+
@p.abook = "ben\tben\t'Ben \"quoted\" Walton' <bwalton@example.org>\n"
|
|
96
|
+
assert_equal @p.to_csv, "ben,ben,person,\"Ben \"\"quoted\"\" Walton <bwalton@example.org>\"\n"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
should "allow recipient with single quoted name with embedded quote" do
|
|
100
|
+
@p.abook = "ben\tben\t'Ben O''Walton' <bwalton@example.org>\n"
|
|
101
|
+
assert_equal @p.to_csv, "ben,ben,person,Ben O'Walton <bwalton@example.org>\n"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
should "allow recipient with double quoted name with embedded double quote" do
|
|
105
|
+
@p.abook = "ben\tben\t\"Ben \"\"quoted\"\" Walton\" <bwalton@example.org>\n"
|
|
106
|
+
assert_equal @p.to_csv, "ben,ben,person,\"Ben \"\"quoted\"\" Walton <bwalton@example.org>\"\n"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
should "allow simple group" do
|
|
110
|
+
@p.abook = "bens\tthe bens\t(ben1@example.com,ben2@example.com)\n"
|
|
111
|
+
assert_equal @p.to_csv, "bens,the bens,group,<ben1@example.com>,<ben2@example.com>\n"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
should "allow groups with fcc values" do
|
|
115
|
+
@p.abook = "bens\tthe bens\t(ben1@example.com,ben2@example.com)\tfcc\n"
|
|
116
|
+
assert_equal @p.to_csv, "bens,the bens,group,<ben1@example.com>,<ben2@example.com>\n"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
should "allow groups with fcc and comment values" do
|
|
120
|
+
@p.abook = "bens\tthe bens\t(ben1@example.com,ben2@example.com)\tfcc\tcomment\n"
|
|
121
|
+
assert_equal @p.to_csv, "bens,the bens,group,<ben1@example.com>,<ben2@example.com>\n"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
should "allow group with only one member" do
|
|
125
|
+
@p.abook = "bens\tthe bens\t(ben1@example.com)\n"
|
|
126
|
+
assert_equal @p.to_csv, "bens,the bens,group,<ben1@example.com>\n"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
should "allow group with only one (named) member" do
|
|
130
|
+
@p.abook = "bens\tthe bens\t('ben1' <ben1@example.com>)\n"
|
|
131
|
+
assert_equal @p.to_csv, "bens,the bens,group,ben1 <ben1@example.com>\n"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
should "allow group with single quoted names" do
|
|
135
|
+
@p.abook = "bens\tthe bens\t('ben1' <ben1@example.com>, 'ben2' <ben2@example.com>)\n"
|
|
136
|
+
assert_equal @p.to_csv, "bens,the bens,group,ben1 <ben1@example.com>,ben2 <ben2@example.com>\n"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
should "allow group with double quoted names" do
|
|
140
|
+
@p.abook = "bens\tthe bens\t(\"ben1\" <ben1@example.com>, \"ben2\" <ben2@example.com>)\n"
|
|
141
|
+
assert_equal @p.to_csv, "bens,the bens,group,ben1 <ben1@example.com>,ben2 <ben2@example.com>\n"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
should "allow group with double quoted names containing double quotes" do
|
|
145
|
+
@p.abook = "bens\tthe bens\t(\"ben1\" <ben1@example.com>, \"ben2 o\"\"walton\" <ben2@example.com>)\n"
|
|
146
|
+
assert_equal @p.to_csv, "bens,the bens,group,ben1 <ben1@example.com>,\"ben2 o\"\"walton <ben2@example.com>\"\n"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
should "allow group with double quoted names containing single quotes" do
|
|
150
|
+
@p.abook = "bens\tthe bens\t(\"ben1\" <ben1@example.com>, \"ben2 o'walton\" <ben2@example.com>)\n"
|
|
151
|
+
assert_equal @p.to_csv, "bens,the bens,group,ben1 <ben1@example.com>,ben2 o'walton <ben2@example.com>\n"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
should "allow group with quoted names containing single quotes" do
|
|
155
|
+
@p.abook = "bens\tthe bens\t(\"ben1\" <ben1@example.com>, 'ben2 o''walton' <ben2@example.com>)\n"
|
|
156
|
+
assert_equal @p.to_csv, "bens,the bens,group,ben1 <ben1@example.com>,ben2 o'walton <ben2@example.com>\n"
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
should "allow continuation after nickname" do
|
|
160
|
+
@p.abook = "ben\t\n ben walton\tbwalton@example.com\n"
|
|
161
|
+
assert_equal @p.to_csv, "ben,ben walton,person,<bwalton@example.com>\n"
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
should "allow continuation after name" do
|
|
165
|
+
@p.abook = "ben\tben walton\t\n bwalton@example.com\n"
|
|
166
|
+
assert_equal @p.to_csv, "ben,ben walton,person,<bwalton@example.com>\n"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
should "allow continuation after email (with fcc)" do
|
|
170
|
+
@p.abook = "ben\tben walton\tbwalton@example.com\t\n fcc\n"
|
|
171
|
+
assert_equal @p.to_csv, "ben,ben walton,person,<bwalton@example.com>\n"
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
should "allow continuation after fcc (with comment)" do
|
|
175
|
+
@p.abook = "ben\tben walton\tbwalton@example.com\tfcc\t\n comment\n"
|
|
176
|
+
assert_equal @p.to_csv, "ben,ben walton,person,<bwalton@example.com>\n"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
should "allow multiple continuations" do
|
|
180
|
+
[ "ben\t\n ben walton\tbwalton@example.com\tfcc\t\n comment\n",
|
|
181
|
+
"ben\tben walton\t\n bwalton@example.com\tfcc\t\n comment\n",
|
|
182
|
+
"ben\tben walton\t\n bwalton@example.com\t\n fcc\t\n comment\n"
|
|
183
|
+
].each do |continued_line|
|
|
184
|
+
@p.abook = continued_line
|
|
185
|
+
assert_equal @p.to_csv, "ben,ben walton,person,<bwalton@example.com>\n"
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
should "allow continuations in a group list" do
|
|
190
|
+
@p.abook = "bens\tthe bens\t(\n ben1@example.com,\n ben2@example.com,\n ben3@example.com)\n"
|
|
191
|
+
assert_equal @p.to_csv, "bens,the bens,group,<ben1@example.com>,<ben2@example.com>,<ben3@example.com>\n"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
should "allow continuations in a group list with named recipients" do
|
|
195
|
+
@p.abook = "bens\tthe bens\t(\n 'ben1' <ben1@example.com>,\n ben2@example.com,\n ben3@example.com)\n"
|
|
196
|
+
assert_equal @p.to_csv, "bens,the bens,group,ben1 <ben1@example.com>,<ben2@example.com>,<ben3@example.com>\n"
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
should "allow group with fcc and command mixed in continuations" do
|
|
200
|
+
@p.abook = "bens\tthe bens\t\n (bwalton@example.org,\n ben2@example.org)\t\n fcc\tcomment\n"
|
|
201
|
+
assert_equal @p.to_csv, "bens,the bens,group,<bwalton@example.org>,<ben2@example.org>\n"
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
should "allow tab after continuation" do
|
|
205
|
+
[ "ben\n \tben walton\tbwalton@example.org\n",
|
|
206
|
+
"ben\tben walton\n \tbwalton@example.org\n",
|
|
207
|
+
"ben\n \tben walton\n \tbwalton@example.org\n"
|
|
208
|
+
].each do |odd_continuation|
|
|
209
|
+
@p.abook = odd_continuation
|
|
210
|
+
assert_equal @p.to_csv, "ben,ben walton,person,<bwalton@example.org>\n"
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
context "Reject invalid entries" do
|
|
217
|
+
setup do
|
|
218
|
+
@p = Pine2CSV.new
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
should "reject entry with no newline" do
|
|
222
|
+
[ "ben\tben\tbwalton@example.org",
|
|
223
|
+
"ben1\tben1\tben1@example.org\nben2\tben2\tben2@example.org"
|
|
224
|
+
].each do |missing_eol|
|
|
225
|
+
@p.abook = missing_eol
|
|
226
|
+
assert_raise Pine2CSV::Error do
|
|
227
|
+
@p.to_csv
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
should "reject abook with extra newline" do
|
|
233
|
+
@p.abook = "ben\tben\tbwalton@example.org\n\n"
|
|
234
|
+
assert_raise Pine2CSV::Error do
|
|
235
|
+
@p.to_csv
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
should "reject missing recipient" do
|
|
240
|
+
[ "ben\tben walton\t\tfcc\tcomment\n",
|
|
241
|
+
"ben\tben walton\t\n",
|
|
242
|
+
"ben\tben walton\n"
|
|
243
|
+
].each do |missing_recip|
|
|
244
|
+
@p.abook = missing_recip
|
|
245
|
+
assert_raise Pine2CSV::Error do
|
|
246
|
+
@p.to_csv
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
should "reject recipient with no @ in address" do
|
|
252
|
+
[ "ben\tben walton\tbwaltonATexample.org\n",
|
|
253
|
+
"ben\tben walton\t<bwaltonATexample.org>\n"
|
|
254
|
+
].each do |bad_address|
|
|
255
|
+
@p.abook = bad_address
|
|
256
|
+
assert_raise Pine2CSV::Error do
|
|
257
|
+
@p.to_csv
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
should "reject named recipient when address not bracketed" do
|
|
263
|
+
@p.abook = "ben\tben\t'ben' bwalton@example.com\n"
|
|
264
|
+
assert_raise Pine2CSV::Error do
|
|
265
|
+
@p.to_csv
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
should "reject non-escaped quotes in name field" do
|
|
270
|
+
[
|
|
271
|
+
"ben\t'ben o'walton'\tbwalton@example.org\n",
|
|
272
|
+
"ben\t\"ben \"bad quote\" walton\"\tbwalton@example.org\n"
|
|
273
|
+
].each do |bad|
|
|
274
|
+
@p.abook = bad
|
|
275
|
+
assert_raise Pine2CSV::Error do
|
|
276
|
+
@p.to_csv
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
should "reject missing angle quote on email" do
|
|
282
|
+
[ "ben\tben walton\t<bwalton@example.com\n",
|
|
283
|
+
"ben\tben walton\tbwalton@example.com>\n"
|
|
284
|
+
].each do |missing_bracket|
|
|
285
|
+
@p.abook = missing_bracket
|
|
286
|
+
assert_raise Pine2CSV::Error do
|
|
287
|
+
@p.to_csv
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
should "reject groups with missing brackets" do
|
|
293
|
+
[ "bens\tthe bens\tben1@example.com,ben2@example.com\n",
|
|
294
|
+
"bens\tthe bens\t(ben1@example.com,ben2@example.com\n",
|
|
295
|
+
"bens\tthe bens\tben1@example.com,ben2@example.com)\n"
|
|
296
|
+
].each do |missing_bracket|
|
|
297
|
+
@p.abook = missing_bracket
|
|
298
|
+
assert_raise Pine2CSV::Error do
|
|
299
|
+
@p.to_csv
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
should "reject entries with dangling continuations" do
|
|
305
|
+
[ "ben\tben\tbwalton@example.org\n ",
|
|
306
|
+
"ben\tben\tbwalton@example.org\tfcc\n ",
|
|
307
|
+
"ben\tben\tbwalton@example.org\t\n fcc\n ",
|
|
308
|
+
].each do |bad_cont|
|
|
309
|
+
@p.abook = bad_cont
|
|
310
|
+
assert_raise Pine2CSV::Error do
|
|
311
|
+
@p.to_csv
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
should "reject a bad continuation mixed in valid entries" do
|
|
317
|
+
@p.abook = "ben\tben\tbwalton@example.org\nben2\tben2\n \nben3\tben3\tben3@example.org\n"
|
|
318
|
+
assert_raise Pine2CSV::Error do
|
|
319
|
+
@p.to_csv
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
should "reject a bad group entry mixed in valid entries" do
|
|
324
|
+
@p.abook = "ben\tben\tbwalton@example.org\nbens\tthe bens\t(ben1@example.org,ben2@example.org\nben2\tben2\t<ben2@example.org>\n"
|
|
325
|
+
assert_raise Pine2CSV::Error do
|
|
326
|
+
@p.to_csv
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
should "reject a bad group entry in the middle of valid group entries" do
|
|
331
|
+
@p.abook = "bencom\tthe com bens\t(ben1@example.com,ben2@example.com)\nbadgr\tbad group\t(bad group,)\nbenorg\tthe org bens\t(ben1@example.org)\n"
|
|
332
|
+
assert_raise Pine2CSV::Error do
|
|
333
|
+
@p.to_csv
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
context "Accept multiple lines" do
|
|
339
|
+
setup do
|
|
340
|
+
@p = Pine2CSV.new
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
should "allow multiple lines" do
|
|
344
|
+
@p.abook = "ben\tben walton\tbwalton@example.org\nben\tben walton\tbwalton@example.org\n"
|
|
345
|
+
assert_equal @p.to_csv, "ben,ben walton,person,<bwalton@example.org>\nben,ben walton,person,<bwalton@example.org>\n"
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
should "allow mixed group with single recipient lines" do
|
|
349
|
+
@p.abook = "ben\tben\tbwalton@example.org\nbens\tthe bens\t(ben1@example.org,ben2@example.org)\n"
|
|
350
|
+
assert_equal @p.to_csv, "ben,ben,person,<bwalton@example.org>\nbens,the bens,group,<ben1@example.org>,<ben2@example.org>\n"
|
|
351
|
+
|
|
352
|
+
@p.abook = "bens\tthe bens\t(ben1@example.org,ben2@example.org)\nben\tben\tbwalton@example.org\n"
|
|
353
|
+
assert_equal @p.to_csv, "bens,the bens,group,<ben1@example.org>,<ben2@example.org>\nben,ben,person,<bwalton@example.org>\n"
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
end
|