pine2csv 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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