table_print 0.2.3 → 1.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+ require "formatter"
3
+
4
+ include TablePrint
5
+
6
+ describe TablePrint::TimeFormatter do
7
+ describe "#format" do
8
+ it "only operates on Time objects" do
9
+ f = TablePrint::TimeFormatter.new
10
+ f.format(12).should == 12
11
+ end
12
+
13
+ it "uses the config'd time_format" do
14
+ f = TablePrint::TimeFormatter.new
15
+ time = Time.local(2012, 01, 11, 1, 23, 45)
16
+ f.format(time).should == "2012-01-11 01:23:45" # default time format is set in config.rb
17
+ end
18
+
19
+ it "overrides the config'd time format with one it was passed" do
20
+ f = TablePrint::TimeFormatter.new("%Y")
21
+ time = Time.local(2012, 01, 11, 1, 23, 45)
22
+ f.format(time).should == "2012" # default time format is set in config.rb
23
+ end
24
+ end
25
+ end
26
+
27
+ describe TablePrint::NoNewlineFormatter do
28
+ before(:each) do
29
+ @f = TablePrint::NoNewlineFormatter.new
30
+ end
31
+
32
+ describe "#format" do
33
+ it "replaces carriage returns with spaces" do
34
+ @f.format("foo\r\nbar").should == "foo bar"
35
+ end
36
+
37
+ it "replaces newlines with spaces" do
38
+ @f.format("foo\nbar").should == "foo bar"
39
+ end
40
+ end
41
+ end
42
+
43
+ describe TablePrint::FixedWidthFormatter do
44
+ before(:each) do
45
+ @f = TablePrint::FixedWidthFormatter.new(10)
46
+ end
47
+
48
+ describe "#format" do
49
+ it "pads a short field to the specified width" do
50
+ @f.format("asdf").should == "asdf "
51
+ end
52
+
53
+ it "truncates long fields with periods" do
54
+ @f.format("1234567890123456").should == "1234567..."
55
+ end
56
+
57
+ it "uses an empty string in place of nils" do
58
+ @f.format(nil).should == " "
59
+ end
60
+
61
+ it "turns objects into strings before trying to format them" do
62
+ @f.format(123).should == "123 "
63
+ end
64
+ end
65
+
66
+ describe "#width" do
67
+ it "returns the width" do
68
+ @f.width.should == 10
69
+ end
70
+
71
+ it "respects the config'd max_width" do
72
+ max = TablePrint::Config.max_width
73
+ TablePrint::Config.max_width = 5
74
+ @f.width.should == 5
75
+ TablePrint::Config.max_width = max
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+ require 'hash_extensions'
3
+
4
+ describe "#constructive_merge" do
5
+ it "merges hashes without clobbering" do
6
+ x = {'reviews' => {'user' => {}}}
7
+ y = {'reviews' => {'ratings' => {}}}
8
+ x.extend TablePrint::HashExtensions::ConstructiveMerge
9
+ x.constructive_merge(y).should == {'reviews' => {'user' => {}, 'ratings' => {}}}
10
+ end
11
+ end
12
+
13
+ describe "#constructive_merge!" do
14
+ it "merges hashes in place without clobbering" do
15
+ x = {'reviews' => {'user' => {}}}
16
+ y = {'reviews' => {'ratings' => {}}}
17
+ x.extend TablePrint::HashExtensions::ConstructiveMerge
18
+ x.constructive_merge!(y)
19
+ x.should == {'reviews' => {'user' => {}, 'ratings' => {}}}
20
+ end
21
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+ require 'printable'
3
+
4
+ describe TablePrint::Printable do
5
+ before(:each) do
6
+ Sandbox.cleanup!
7
+ end
8
+
9
+ describe "#default_display_methods" do
10
+ it "returns attribute getters" do
11
+ Sandbox.add_class("Hat")
12
+ Sandbox.add_attributes("Hat", "brand")
13
+
14
+ p = Sandbox::Hat.new
15
+ TablePrint::Printable.default_display_methods(p).should == %W(brand)
16
+ end
17
+
18
+ it "ignores dangerous methods" do
19
+ Sandbox.add_class("Hat")
20
+ Sandbox.add_method("Hat", "brand!") {}
21
+
22
+ p = Sandbox::Hat.new
23
+ TablePrint::Printable.default_display_methods(p).should == []
24
+ end
25
+
26
+ it "ignores methods defined in a superclass" do
27
+ Sandbox.add_class("Hat::Bowler")
28
+ Sandbox.add_attributes("Hat", "brand")
29
+ Sandbox.add_attributes("Hat::Bowler", "brim_width")
30
+
31
+ p = Sandbox::Hat::Bowler.new
32
+ TablePrint::Printable.default_display_methods(p).should == %W(brim_width)
33
+ end
34
+
35
+ it "ignores methods that require arguments" do
36
+ Sandbox.add_class("Hat")
37
+ Sandbox.add_attributes("Hat", "brand")
38
+ Sandbox.add_method("Hat", "tip?") { |person| person.rapscallion? }
39
+
40
+ p = Sandbox::Hat.new
41
+ TablePrint::Printable.default_display_methods(p).should == %W(brand)
42
+ end
43
+
44
+ it "ignores methods from an included module" do
45
+ pending "waiting for Cat to support module manipulation"
46
+ end
47
+
48
+ it "uses column information when available (eg, from ActiveRecord objects)"
49
+ end
50
+ end
51
+
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ require 'returnable'
3
+ require 'config'
4
+
5
+ describe TablePrint::Returnable do
6
+ it "returns its initialized value from its to_s method" do
7
+ r = TablePrint::Returnable.new("foobar")
8
+ r.to_s.should == "foobar"
9
+ end
10
+
11
+ it "passes #set through to TablePrint::Config" do
12
+ TablePrint::Config.should_receive(:set).with(Object, [:foo])
13
+ r = TablePrint::Returnable.new
14
+ r.set(Object, :foo)
15
+ end
16
+
17
+ it "passes #clear through to TablePrint::Config" do
18
+ TablePrint::Config.should_receive(:clear).with(Object)
19
+ r = TablePrint::Returnable.new
20
+ r.clear(Object)
21
+ end
22
+ end
23
+
@@ -0,0 +1,466 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+ require 'cat'
4
+ require "row_group"
5
+
6
+ include TablePrint
7
+
8
+ describe RowRecursion do
9
+ let(:parent) { RowGroup.new }
10
+ let(:child) { Row.new }
11
+
12
+ describe "#set_column" do
13
+ it "assigns the column object to the column name" do
14
+ column = Column.new
15
+ parent.set_column(:foobar, column)
16
+ parent.column_for(:foobar).should == column
17
+ end
18
+ end
19
+
20
+ describe "#add_child" do
21
+ it "adds the child to my children" do
22
+ parent.add_child(child)
23
+ parent.child_count.should == 1
24
+ end
25
+
26
+ it "sets me as my child's parent" do
27
+ parent.add_child(child)
28
+ child.parent.should == parent
29
+ end
30
+ end
31
+
32
+ describe "#add_children" do
33
+ let (:child2) {Row.new}
34
+
35
+ it "adds all the children to myself" do
36
+ parent.add_children([child, child2])
37
+ parent.child_count.should == 2
38
+ end
39
+
40
+ it "sets me as their parent" do
41
+ parent.add_children([child, child2])
42
+ child.parent.should == parent
43
+ child2.parent.should == parent
44
+ end
45
+ end
46
+
47
+ describe "#columns" do
48
+ it "returns columns populated with names and data" do
49
+ child.set_cell_values(:title => 'foobar')
50
+ parent.add_child(child)
51
+
52
+ parent.columns.length.should == 1
53
+ parent.columns.first.name.should == 'title'
54
+ parent.columns.first.data.should == ['foobar']
55
+ end
56
+
57
+ it "gets the columns from the root node" do
58
+ parent.add_child(child)
59
+ child.set_cell_values(:title => 'foobar')
60
+
61
+ parent.columns.length.should == 1
62
+ child.columns.should == parent.columns
63
+ end
64
+ end
65
+
66
+ describe "#column_for" do
67
+ it "returns the column object for a given column name" do
68
+ parent.add_child(child)
69
+ child.set_cell_values(:title => 'foobar')
70
+ column = parent.columns.first
71
+ parent.column_for(:title).should == column
72
+ end
73
+ end
74
+
75
+ describe "#add_formatter" do
76
+ it "adds the formatter to the column object" do
77
+ parent.add_child(child)
78
+ child.set_cell_values(:title => 'foobar')
79
+ column = parent.columns.first
80
+ parent.add_formatter(:title, {})
81
+
82
+ column.formatters.should == [{}]
83
+ end
84
+ end
85
+
86
+ describe "#width" do
87
+ it "returns the total width of the columns" do
88
+ parent.add_child(r1 = Row.new)
89
+ parent.add_child(r2 = Row.new)
90
+
91
+ r1.set_cell_values(:title => 'foobar')
92
+ r2.set_cell_values(:subtitle => 'elemental')
93
+
94
+ parent.width.should == 18
95
+ end
96
+ end
97
+
98
+ describe "#horizontal_separator" do
99
+ it "returns hyphens equal to the table width" do
100
+ child.set_cell_values(:title => 'foobar')
101
+ child.horizontal_separator.should == '------'
102
+ end
103
+
104
+ it "matches the header width" do
105
+ child.set_cell_values(:title => 'foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar')
106
+ child.horizontal_separator.should == '------------------------------' # 30 hyphens
107
+ end
108
+ end
109
+
110
+ describe "#header" do
111
+ it "returns the column names, padded to the proper width, separated by the | character" do
112
+ child.set_cell_values(:title => 'first post', :author => 'chris', :subtitle => 'first is the worst')
113
+ compare_rows(child.header, "AUTHOR | SUBTITLE | TITLE ")
114
+ end
115
+ end
116
+ end
117
+
118
+ describe TablePrint::RowGroup do
119
+ describe "#raw_column_data" do
120
+ it "returns the column data from its child rows" do
121
+ group = RowGroup.new
122
+ group.add_child(Row.new.set_cell_values(:title => 'foo'))
123
+ group.add_child(Row.new.set_cell_values(:title => 'bar'))
124
+ group.raw_column_data(:title).should == ['foo', 'bar']
125
+ end
126
+ end
127
+ end
128
+
129
+ def compare_rows(actual_rows, expected_rows)
130
+ actual_rows.split("\n").length.should == expected_rows.split("\n").length
131
+ actual_rows.split("\n").zip(expected_rows.split("\n")).each do |actual, expected|
132
+ actual.split(//).sort.join.should == expected.split(//).sort.join
133
+ end
134
+ end
135
+
136
+ describe TablePrint::Row do
137
+ let(:row) { Row.new.set_cell_values({'title' => "wonky", 'author' => "bob jones", 'pub_date' => "2012"}) }
138
+
139
+ describe "#format" do
140
+ it "formats the row with padding" do
141
+ compare_rows(row.format, "wonky | bob jones | 2012 ")
142
+ end
143
+
144
+ it "also formats the children" do
145
+ row.add_child(RowGroup.new.add_child(Row.new.set_cell_values(:title => "wonky2", :author => "bob jones2", :pub_date => "20122")))
146
+ compare_rows(row.format, "wonky | bob jones | 2012 \nwonky2 | bob jones2 | 20122 ")
147
+ end
148
+ end
149
+
150
+ describe "#apply_formatters" do
151
+ it "calls the format method on each formatter for that column" do
152
+ Sandbox.add_class("DoubleFormatter")
153
+ Sandbox.add_method("DoubleFormatter", :format) { |value| value * 2 }
154
+
155
+ Sandbox.add_class("ChopFormatter")
156
+ Sandbox.add_method("ChopFormatter", :format) { |value| value[0..-2] }
157
+
158
+ f1 = Sandbox::DoubleFormatter.new
159
+ f2 = Sandbox::ChopFormatter.new
160
+
161
+ row.stub(:column_for) {OpenStruct.new(:width => 11, :formatters => [f1, f2])}
162
+
163
+ row.apply_formatters(:title, "foobar").should == "foobarfooba"
164
+ end
165
+
166
+ it "uses the config'd time_format to format times" do
167
+ row.stub(:column_for) {OpenStruct.new(:width => 20, :formatters => [], :time_format => "%Y %m %d")}
168
+
169
+ time_formatter = TablePrint::TimeFormatter.new
170
+ TablePrint::TimeFormatter.should_receive(:new).with("%Y %m %d") {time_formatter}
171
+ row.apply_formatters(:title, Time.local(2012, 6, 1, 14, 20, 20))
172
+ end
173
+ end
174
+
175
+ describe "#raw_column_data" do
176
+ it "returns all the values for a given column" do
177
+ row = Row.new.set_cell_values(:title => 'one', :author => 'two')
178
+
179
+ group = RowGroup.new
180
+ ['two', 'three', 'four', 'five', 'six', 'seven'].each do |title|
181
+ group.add_child(Row.new.set_cell_values(:title => title))
182
+ end
183
+ row.add_child(group)
184
+
185
+ row.raw_column_data('title').should == ['one', 'two', 'three', 'four', 'five', 'six', 'seven']
186
+ end
187
+ end
188
+
189
+ describe "#collapse" do
190
+
191
+ # row: foo
192
+ # group
193
+ # row: bar
194
+ # => foo | bar
195
+ context "for a single row in a single child group" do
196
+ before(:each) do
197
+ @row = Row.new
198
+ @row.set_cell_values(:foo => "foo").add_child(
199
+ RowGroup.new.add_child(
200
+ Row.new.set_cell_values(:bar => "bar")
201
+ )
202
+ )
203
+ @row.collapse!
204
+ end
205
+
206
+ it "pulls the cells up into the parent" do
207
+ @row.cells.should == {"foo" => "foo", "bar" => "bar"}
208
+ end
209
+
210
+ it "dereferences the now-defunct group" do
211
+ @row.children.length.should == 0
212
+ end
213
+ end
214
+
215
+ # row: foo
216
+ # group
217
+ # row: bar
218
+ # row: baz
219
+ # => foo | bar
220
+ # | baz
221
+ context "for two rows in a single child group" do
222
+ before(:each) do
223
+ @row = Row.new
224
+ @row.set_cell_values(:foo => "foo").add_child(
225
+ RowGroup.new.add_children([
226
+ Row.new.set_cell_values(:bar => "bar"),
227
+ Row.new.set_cell_values(:bar => "baz")
228
+ ])
229
+ )
230
+ @row.collapse!
231
+ end
232
+
233
+ it "pulls the cells from the first row up into the parent" do
234
+ @row.cells.should == {"foo" => "foo", "bar" => "bar"}
235
+ end
236
+
237
+ it "deletes the absorbed row but leaves the second row in the group" do
238
+ @row.children.length.should == 1
239
+ @row.children.first.children.length.should == 1
240
+ @row.children.first.children.first.cells.should == {"bar" => "baz"}
241
+ end
242
+ end
243
+
244
+ # row: foo
245
+ # group
246
+ # row: bar
247
+ # group
248
+ # row: baz
249
+ # => foo | bar | baz
250
+ context "for two groups with a single row each" do
251
+ before(:each) do
252
+ @row = Row.new
253
+ @row.set_cell_values(:foo => "foo").add_children([
254
+ RowGroup.new.add_child(
255
+ Row.new.set_cell_values(:bar => "bar")
256
+ ),
257
+ RowGroup.new.add_child(
258
+ Row.new.set_cell_values(:baz => "baz")
259
+ )
260
+ ])
261
+ @row.collapse!
262
+ end
263
+
264
+ it "pulls the cells from both groups into the parent" do
265
+ @row.cells.should == {"foo" => "foo", "bar" => "bar", "baz" => "baz"}
266
+ end
267
+
268
+ it "dereferences both now-defunct groups" do
269
+ @row.children.length.should == 0
270
+ end
271
+ end
272
+
273
+ # row: foo
274
+ # group
275
+ # row: bar
276
+ # group
277
+ # row: baz
278
+ # row: bazaar
279
+ # => foo | bar | baz
280
+ # | bazaar
281
+ context "for two groups, one with a single row and one with two rows" do
282
+ before(:each) do
283
+ @row = Row.new
284
+ @row.set_cell_values(:foo => "foo").add_children([
285
+ RowGroup.new.add_child(
286
+ Row.new.set_cell_values(:bar => "bar")
287
+ ),
288
+ RowGroup.new.add_children([
289
+ Row.new.set_cell_values(:baz => "baz"),
290
+ Row.new.set_cell_values(:baz => "bazaar"),
291
+ ]),
292
+ ])
293
+ @row.collapse!
294
+ end
295
+
296
+ it "pulls the single row and the first row from the double into itself" do
297
+ @row.cells.should == {"foo" => "foo", "bar" => "bar", "baz" => "baz"}
298
+ end
299
+
300
+ it "keeps the second row from the second group in its own group" do
301
+ @row.children.length.should == 1
302
+ @row.children.first.children.length.should == 1
303
+ @row.children.first.children.first.cells.should == {"baz" => "bazaar"}
304
+ end
305
+ end
306
+
307
+ # row: foo
308
+ # group
309
+ # row: bar
310
+ # group
311
+ # row: baz
312
+ # => foo | bar | baz
313
+ context "for two nested groups, each with one row" do
314
+ before(:each) do
315
+ @row = Row.new
316
+ @row.set_cell_values(:foo => "foo").add_child(
317
+ RowGroup.new.add_child(
318
+ Row.new.set_cell_values(:bar => "bar").add_child(
319
+ RowGroup.new.add_child(
320
+ Row.new.set_cell_values(:baz => "baz")
321
+ )
322
+ )
323
+ )
324
+ )
325
+ @row.collapse!
326
+ end
327
+
328
+ it "pulls the cells from both groups into the parent" do
329
+ @row.cells.should == {"foo" => "foo", "bar" => "bar", "baz" => "baz"}
330
+ end
331
+
332
+ it "dereferences both now-defunct groups" do
333
+ @row.children.length.should == 0
334
+ end
335
+ end
336
+
337
+ # row: foo
338
+ # group
339
+ # row: bar
340
+ # group
341
+ # row: baz
342
+ # row: bazaar
343
+ # => foo | bar | baz
344
+ # | bazaar
345
+ context "for a child with one row, which itself has multiple rows" do
346
+ before(:each) do
347
+ @row = Row.new
348
+ @row.set_cell_values(:foo => "foo").add_child(
349
+ RowGroup.new.add_child(
350
+ Row.new.set_cell_values(:bar => "bar").add_child(
351
+ RowGroup.new.add_children([
352
+ Row.new.set_cell_values(:baz => "baz"),
353
+ Row.new.set_cell_values(:baz => "bazaar")
354
+ ])
355
+ )
356
+ )
357
+ )
358
+ @row.collapse!
359
+ end
360
+
361
+ it "pulls the first row from each group up into itself" do
362
+ @row.cells.should == {"foo" => "foo", "bar" => "bar", "baz" => "baz"}
363
+ end
364
+
365
+ it "deletes only the intermediary group" do
366
+ @row.children.length.should == 1
367
+ @row.children.first.children.length.should == 1
368
+ @row.children.first.children.first.cells.should == {"baz" => "bazaar"}
369
+ end
370
+ end
371
+
372
+ # row: foo
373
+ # group
374
+ # row: bar
375
+ # row: bar2
376
+ # group
377
+ # row: bazaar
378
+ # row: bazaar2
379
+ # => foo | bar |
380
+ # | bar2 |
381
+ # | | bazaar
382
+ # | | bazaar2
383
+ context "for multiple children with multiple rows" do
384
+ before(:each) do
385
+ @row = Row.new
386
+ @row.set_cell_values(:foo => "foo").add_children([
387
+ RowGroup.new.add_children([
388
+ Row.new.set_cell_values(:bar => "bar"),
389
+ Row.new.set_cell_values(:bar => "bar2"),
390
+ ]),
391
+ RowGroup.new.add_children([
392
+ Row.new.set_cell_values(:baz => "bazaar"),
393
+ Row.new.set_cell_values(:baz => "bazaar2"),
394
+ ])
395
+ ])
396
+ @row.collapse!
397
+ end
398
+
399
+ it "pulls the first row from the first group into the parent" do
400
+ @row.cells.should == {"foo" => "foo", "bar" => "bar"}
401
+ end
402
+
403
+ it "leaves the second row in the first group" do
404
+ @row.children.length.should == 2
405
+ @row.children.first.children.length.should == 1
406
+ @row.children.first.children.first.cells.should == {"bar" => "bar2"}
407
+ end
408
+
409
+ it "leaves the second group alone" do
410
+ @row.children.last.children.length.should == 2
411
+ @row.children.last.children.first.cells.should == {"baz" => "bazaar"}
412
+ @row.children.last.children.last.cells.should == {"baz" => "bazaar2"}
413
+ end
414
+ end
415
+
416
+ # row: foo
417
+ # group
418
+ # row: bar
419
+ # group
420
+ # row: bare
421
+ # row: bart
422
+ # row: baz
423
+ # group
424
+ # row: bazaar
425
+ # row: bizarre
426
+ # => foo | bar | bare
427
+ # | | bart
428
+ # | baz | bazaar
429
+ # | | bizarre
430
+ context "for multiple children with multiple children" do
431
+ before(:each) do
432
+ @row = Row.new
433
+ @row.set_cell_values(:foo => "foo").add_child(
434
+ RowGroup.new.add_children([
435
+ Row.new.set_cell_values(:bar => "bar").add_child(
436
+ RowGroup.new.add_children([
437
+ Row.new.set_cell_values(:barry => "bare"),
438
+ Row.new.set_cell_values(:barry => "bart")
439
+ ])
440
+ ),
441
+ Row.new.set_cell_values(:bar => "baz").add_child(
442
+ RowGroup.new.add_children([
443
+ Row.new.set_cell_values(:barry => "bazaar"),
444
+ Row.new.set_cell_values(:barry => "bizarre")
445
+ ])
446
+ )
447
+ ])
448
+ )
449
+ @row.collapse!
450
+ end
451
+
452
+ it "pulls the first row from the first child into itself" do
453
+ @row.cells.should == {"foo" => "foo", "bar" => "bar", "barry" => "bare"}
454
+ end
455
+
456
+ it "leaves the second row from the first child in the first group" do
457
+ @row.children.first.children.first.cells.should == {"barry" => "bart"}
458
+ end
459
+
460
+ it "collapses the second group" do
461
+ @row.children.last.children.first.cells.should == {"bar" => "baz", "barry" => "bazaar"}
462
+ @row.children.last.children.first.children.first.children.first.cells.should == {"barry" => "bizarre"}
463
+ end
464
+ end
465
+ end
466
+ end