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/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,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'pine2csv'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -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