table_print 0.2.3 → 1.0.0.rc3

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.
@@ -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