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.
- data/.rspec +1 -0
- data/.rvmrc +1 -1
- data/.travis.yml +5 -0
- data/Gemfile +11 -10
- data/README.rdoc +85 -32
- data/Rakefile +13 -13
- data/VERSION +1 -1
- data/features/adding_columns.feature +48 -0
- data/features/configuring_output.feature +57 -0
- data/features/excluding_columns.feature +28 -0
- data/features/sensible_defaults.feature +86 -0
- data/features/support/step_definitions/before.rb +3 -0
- data/features/support/step_definitions/steps.rb +77 -0
- data/lib/cattr.rb +46 -0
- data/lib/column.rb +45 -0
- data/lib/config.rb +36 -0
- data/lib/config_resolver.rb +91 -0
- data/lib/fingerprinter.rb +85 -0
- data/lib/formatter.rb +45 -0
- data/lib/hash_extensions.rb +37 -0
- data/lib/kernel_extensions.rb +12 -0
- data/lib/printable.rb +22 -0
- data/lib/returnable.rb +21 -0
- data/lib/row_group.rb +227 -0
- data/lib/table_print.rb +33 -389
- data/spec/column_spec.rb +71 -0
- data/spec/config_resolver_spec.rb +236 -0
- data/spec/config_spec.rb +52 -0
- data/spec/fingerprinter_spec.rb +151 -0
- data/spec/formatter_spec.rb +78 -0
- data/spec/hash_extensions_spec.rb +21 -0
- data/spec/printable_spec.rb +51 -0
- data/spec/returnable_spec.rb +23 -0
- data/spec/row_group_spec.rb +466 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/table_print_spec.rb +59 -0
- data/table_print.gemspec +50 -26
- metadata +147 -68
- data/Gemfile.lock +0 -20
- data/test/helper.rb +0 -56
- data/test/test_column.rb +0 -379
- data/test/test_table_print.rb +0 -162
@@ -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
|