crosstab 0.1.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/History.txt +4 -0
- data/Manifest.txt +23 -0
- data/README.txt +133 -0
- data/Rakefile +17 -0
- data/lib/crosstab.rb +25 -0
- data/lib/crosstab/banner.rb +90 -0
- data/lib/crosstab/cell.rb +162 -0
- data/lib/crosstab/column.rb +64 -0
- data/lib/crosstab/crosstab.rb +243 -0
- data/lib/crosstab/extensions.rb +26 -0
- data/lib/crosstab/generic.rb +83 -0
- data/lib/crosstab/group.rb +28 -0
- data/lib/crosstab/row.rb +65 -0
- data/lib/crosstab/table.rb +85 -0
- data/test/test_banner.rb +81 -0
- data/test/test_cell.rb +95 -0
- data/test/test_column.rb +60 -0
- data/test/test_crosstab.rb +214 -0
- data/test/test_extensions.rb +12 -0
- data/test/test_group.rb +67 -0
- data/test/test_missing.rb +75 -0
- data/test/test_row.rb +60 -0
- data/test/test_table.rb +67 -0
- metadata +87 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
class Crosstab::Table < Crosstab::Generic
|
2
|
+
# Pass in a block and we'll execute it within the context of this class.
|
3
|
+
#
|
4
|
+
# Example:
|
5
|
+
#
|
6
|
+
# my_crosstab = Crosstab.new do
|
7
|
+
# table do
|
8
|
+
# row "Male", :a => 1
|
9
|
+
# row "Female", :a => 2
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
def initialize(&block)
|
14
|
+
instance_eval(&block) if block
|
15
|
+
end
|
16
|
+
|
17
|
+
# attr_reader for the rows attribute which should contain an empty array, or a list of rows
|
18
|
+
#
|
19
|
+
# Example:
|
20
|
+
#
|
21
|
+
# rows
|
22
|
+
# #=> []
|
23
|
+
#
|
24
|
+
# row "Male", :a => 1
|
25
|
+
#
|
26
|
+
# rows
|
27
|
+
# #=> [Crosstab::Row...]
|
28
|
+
#
|
29
|
+
def rows
|
30
|
+
@rows ||= []
|
31
|
+
end
|
32
|
+
|
33
|
+
# Creates a new Crosstab::Row and appends it to the rows array.
|
34
|
+
#
|
35
|
+
# Example:
|
36
|
+
#
|
37
|
+
# rows
|
38
|
+
# #=> []
|
39
|
+
#
|
40
|
+
# row "Male", :a => 1
|
41
|
+
#
|
42
|
+
# rows
|
43
|
+
# #=> [Crosstab::Row...]
|
44
|
+
#
|
45
|
+
def row(name,qualification)
|
46
|
+
rows << Crosstab::Row.new(name, qualification)
|
47
|
+
end
|
48
|
+
|
49
|
+
# DSL child setter, creates a new Group, and rows created within its block will be assigned to that group.
|
50
|
+
#
|
51
|
+
# Example:
|
52
|
+
#
|
53
|
+
# rows
|
54
|
+
# #=> []
|
55
|
+
#
|
56
|
+
# group "Gender" do
|
57
|
+
# row "Male", :a => 1
|
58
|
+
# row "Female", :a => 2
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# rows
|
62
|
+
# #=> [Crosstab::Row..., Crosstab::Row...]
|
63
|
+
#
|
64
|
+
# rows[0].group.title
|
65
|
+
# # => "Gender"
|
66
|
+
#
|
67
|
+
# rows[1].group.title
|
68
|
+
# # => "Gender"
|
69
|
+
|
70
|
+
def group(name=nil, &block)
|
71
|
+
if block
|
72
|
+
old_rows = rows.dup # Save current state
|
73
|
+
|
74
|
+
instance_eval(&block) # Execute block within current scope
|
75
|
+
|
76
|
+
g = Crosstab::Group.new(name) # Create new group
|
77
|
+
|
78
|
+
# Set group for all of the new rows
|
79
|
+
(rows - old_rows).each do |row|
|
80
|
+
row.group g
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
data/test/test_banner.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
$LOAD_PATH.unshift "../lib" if __FILE__ == $0
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'crosstab'
|
5
|
+
|
6
|
+
class BannerTests < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@banner = Crosstab::Banner.new
|
9
|
+
end
|
10
|
+
|
11
|
+
# Responds to these public methods
|
12
|
+
def test_respond_methods
|
13
|
+
[:title, :qualification, :qualifies?, :columns, :column, :group].each do |method|
|
14
|
+
assert_respond_to @banner, method, "Hey, an instantiated banner should have a `#{method}` method."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Test defaults for reader methods
|
19
|
+
def test_default_columns
|
20
|
+
assert_kind_of Array, @banner.columns
|
21
|
+
|
22
|
+
# Should have a total column by default
|
23
|
+
assert_equal 1, @banner.columns.length
|
24
|
+
assert_kind_of Crosstab::Column, @banner.columns.first
|
25
|
+
assert_equal "Total", @banner.columns.first.title
|
26
|
+
assert_equal true, @banner.columns.first.qualifies?(Hash.new)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_default_title
|
30
|
+
assert_nil @banner.title
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_default_qualifies?
|
34
|
+
assert_equal true, @banner.qualifies?(Hash.new)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Test behavior of setter methods
|
38
|
+
def test_set_title
|
39
|
+
@banner.title "Test Title"
|
40
|
+
assert_equal "Test Title", @banner.title
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_set_qualification
|
44
|
+
@banner.qualification :a => 1
|
45
|
+
|
46
|
+
assert_equal true, @banner.qualifies?(:a => 1, :b => 1)
|
47
|
+
assert_equal false, @banner.qualifies?(:a => 2, :b => 1)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_set_column
|
51
|
+
# erase the default column
|
52
|
+
@banner.columns.pop
|
53
|
+
|
54
|
+
@banner.column "New Column", :a => 2
|
55
|
+
|
56
|
+
assert_equal 1, @banner.columns.length
|
57
|
+
assert_kind_of Crosstab::Column, @banner.columns.first
|
58
|
+
assert_equal "New Column", @banner.columns.first.title
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_set_group
|
62
|
+
# erase the default column
|
63
|
+
@banner.columns.pop
|
64
|
+
|
65
|
+
@banner.group "Gender" do
|
66
|
+
column "Male", :a => 1
|
67
|
+
column "Female", :a => 2
|
68
|
+
end
|
69
|
+
|
70
|
+
assert_equal 2, @banner.columns.length
|
71
|
+
assert_kind_of Crosstab::Column, @banner.columns.first
|
72
|
+
assert_kind_of Crosstab::Column, @banner.columns.last
|
73
|
+
|
74
|
+
assert_equal "Male", @banner.columns.first.title
|
75
|
+
assert_equal "Female", @banner.columns.last.title
|
76
|
+
|
77
|
+
assert_equal "Gender", @banner.columns.first.group.title
|
78
|
+
assert_equal "Gender", @banner.columns.last.group.title
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
data/test/test_cell.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
$LOAD_PATH.unshift "../lib" if __FILE__ == $0
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
class TestCrosstabCell < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@cell = Crosstab::Cell.new
|
8
|
+
end
|
9
|
+
|
10
|
+
# Responds to these public methods
|
11
|
+
def test_respond_methods
|
12
|
+
[:base, :frequency, :percentage, :result, :significant_against?, :column].each do |method|
|
13
|
+
assert_respond_to @cell, method, "Hey, an instantiated cell should have a `#{method}` method."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Test defaults for reader methods
|
18
|
+
def test_default_base
|
19
|
+
assert_equal 0, @cell.base
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_default_frequency
|
23
|
+
assert_equal 0, @cell.frequency
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_default_percentage
|
27
|
+
assert_equal 0, @cell.percentage
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_default_result
|
31
|
+
assert_equal ["--"], @cell.result
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_normal_result
|
35
|
+
@cell.base 100
|
36
|
+
@cell.frequency 75
|
37
|
+
|
38
|
+
assert_equal [75, "75%"], @cell.result
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_zero_frequency_result
|
42
|
+
@cell.base 100
|
43
|
+
@cell.frequency 0
|
44
|
+
|
45
|
+
assert_equal ["--"], @cell.result
|
46
|
+
end
|
47
|
+
|
48
|
+
# Test behavior of setter methods
|
49
|
+
def test_set_base
|
50
|
+
@cell.base 999
|
51
|
+
assert_equal 999, @cell.base
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_set_frequency
|
55
|
+
@cell.frequency 999
|
56
|
+
assert_equal 999, @cell.frequency
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_default_column
|
60
|
+
assert_equal nil, @cell.column
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_set_column
|
64
|
+
column = Crosstab::Column.new
|
65
|
+
@cell.column column
|
66
|
+
assert_equal column, @cell.column
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_alternate_initialization
|
70
|
+
cell = Crosstab::Cell.new :frequency => 2, :base => 4
|
71
|
+
|
72
|
+
assert_equal 2, cell.frequency
|
73
|
+
assert_equal 4, cell.base
|
74
|
+
end
|
75
|
+
|
76
|
+
# Test behavior of complex methods
|
77
|
+
def test_percentage
|
78
|
+
@cell.base 100
|
79
|
+
@cell.frequency 75
|
80
|
+
|
81
|
+
assert_equal 0.75, @cell.percentage
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_significant_against
|
85
|
+
@cell.base 100
|
86
|
+
@cell.frequency 50
|
87
|
+
|
88
|
+
@test_cell_a = Crosstab::Cell.new :base => 100, :frequency => 63
|
89
|
+
@test_cell_b = Crosstab::Cell.new :base => 100, :frequency => 64
|
90
|
+
|
91
|
+
assert_equal false, @test_cell_a.significant_against?(@cell) #63/100 against 50/100 is not significant at a 95% confidence level
|
92
|
+
assert_equal true, @test_cell_b.significant_against?(@cell) #64/100 against 50/100, however, is.
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
data/test/test_column.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
$LOAD_PATH.unshift "../lib" if __FILE__ == $0
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
|
6
|
+
class ColumnTests < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
@column = Crosstab::Column.new
|
9
|
+
end
|
10
|
+
|
11
|
+
# Responds to these public methods
|
12
|
+
def test_respond_method
|
13
|
+
[:title, :qualification, :qualifies?, :records, :group].each do |method|
|
14
|
+
assert_respond_to @column, method, "Hey, an instantiated column should have a `#{method}` method."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Test defaults for reader methods
|
19
|
+
def test_default_title
|
20
|
+
assert_nil @column.title
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_default_qualifies?
|
24
|
+
assert_equal true, @column.qualifies?(Hash.new)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_default_records
|
28
|
+
assert_equal [], @column.records
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_default_group
|
32
|
+
assert_equal nil, @column.group
|
33
|
+
end
|
34
|
+
|
35
|
+
# Test behavior of setter methods
|
36
|
+
def test_set_title
|
37
|
+
@column.title "Test Title"
|
38
|
+
assert_equal "Test Title", @column.title
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_set_qualification
|
42
|
+
@column.qualification :a => 1
|
43
|
+
|
44
|
+
assert_equal true, @column.qualifies?(:a => 1, :b => 1)
|
45
|
+
assert_equal false, @column.qualifies?(:a => 2, :b => 1)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_set_records
|
49
|
+
record = {:a => 1}
|
50
|
+
|
51
|
+
@column.records [record]
|
52
|
+
assert_equal [record], @column.records
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_set_group
|
56
|
+
@column.group Crosstab::Group.new("Group Title")
|
57
|
+
assert_equal "Group Title", @column.group.title
|
58
|
+
assert_equal [ @column ], @column.group.children
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
$LOAD_PATH.unshift "../lib" if __FILE__ == $0
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'crosstab'
|
5
|
+
|
6
|
+
class CrosstabTests < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
# Minimal crosstab for most tests
|
9
|
+
@crosstab = Crosstab::Crosstab.new
|
10
|
+
|
11
|
+
# Complex crosstab for calculation tests
|
12
|
+
@calculated_crosstab = Crosstab::Crosstab.new do
|
13
|
+
data_source [{:a => 1, :b => 1, :c => 1},
|
14
|
+
{:a => 1, :b => 2, :c => 2},
|
15
|
+
{:a => 1, :b => 2, :c => 4}]
|
16
|
+
|
17
|
+
banner do
|
18
|
+
column "Total"
|
19
|
+
group "Gender" do
|
20
|
+
column "Male", :a => 1
|
21
|
+
column "Female", :a => 2
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
table do
|
26
|
+
title "Q.A Gender"
|
27
|
+
row "Male", :a => 1
|
28
|
+
row "Female", :a => 2
|
29
|
+
end
|
30
|
+
|
31
|
+
table do
|
32
|
+
title "Q.B Age"
|
33
|
+
row "18-34", :b => 1
|
34
|
+
row "35-54", :b => 2
|
35
|
+
end
|
36
|
+
|
37
|
+
table do
|
38
|
+
title "Q.C Likelihood of purchasing"
|
39
|
+
|
40
|
+
group "Very/somewhat likely (Net)" do
|
41
|
+
row "Very likely", :c => 1
|
42
|
+
row "Somewhat likely", :c => 2
|
43
|
+
end
|
44
|
+
|
45
|
+
group "Very/somewhat unlikely (Net)" do
|
46
|
+
row "Somewhat unlikely", :c => 3
|
47
|
+
row "Very unlikely", :c => 4
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
@calculated_crosstab.calculate
|
52
|
+
end
|
53
|
+
|
54
|
+
# Responds to these public methods
|
55
|
+
def test_respond_methods
|
56
|
+
[:data_source, :calculate, :title, :qualification,
|
57
|
+
:qualifies?, :banner, :table, :tables, :printed?].each do |method|
|
58
|
+
assert_respond_to @crosstab, method, "Hey an instantiated crosstab should have a `#{method}` method."
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Test defaults for reader methods
|
63
|
+
def test_default_data_source
|
64
|
+
assert_equal [], @crosstab.data_source, "By default, `data_source` is supposed to respond with an empty array."
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_default_title
|
68
|
+
assert_nil @crosstab.title, "By default, `title` is supposed to be nil"
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_default_qualifies?
|
72
|
+
record = Hash.new
|
73
|
+
|
74
|
+
assert(@crosstab.qualifies?(record), "By default, `qualifies?` is supposed to respond with true.")
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_default_banner
|
78
|
+
assert_kind_of Crosstab::Banner, @crosstab.banner, "By default, `banner` is supposed to respond with a Crosstab::Banner kind of object. The user shouldn't have to define their own unless they want more than a total column."
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_default_tables
|
82
|
+
assert_equal [], @crosstab.tables, "By default, `tables` is supposed to respond with an empty array."
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_default_printed?
|
86
|
+
assert_equal false, @crosstab.printed?
|
87
|
+
end
|
88
|
+
|
89
|
+
# Test behavior of setter methods
|
90
|
+
def test_set_data_source
|
91
|
+
test_data = [{:a => 1}, {:a => 2}]
|
92
|
+
|
93
|
+
@crosstab.data_source test_data
|
94
|
+
assert_equal test_data, @crosstab.data_source, "`data_source [{:a => 1}, {:a => 2}]` is supposed to respond with the same thing it was set with."
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_set_title
|
98
|
+
@crosstab.title "Test Title"
|
99
|
+
assert_equal "Test Title", @crosstab.title
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_set_qualification
|
103
|
+
@crosstab.qualification :a => true
|
104
|
+
|
105
|
+
assert_equal true, @crosstab.qualifies?({:a => true}), "`qualification :a => true` should redefine `qualifies?` so that it returns true on `qualifies? {:a => true}`"
|
106
|
+
assert_equal false, @crosstab.qualifies?({:a => false}), "`qualification :a => true` should redefine `qualifies?` so that it returns false on `qualifies? {:a => false}`"
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_set_banner
|
110
|
+
old_banner = @crosstab.banner
|
111
|
+
@crosstab.banner do
|
112
|
+
# empty on purpose
|
113
|
+
end
|
114
|
+
|
115
|
+
assert_not_same old_banner, @crosstab.banner, "`banner` should create a new banner. It didn't. It still has the same ID."
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_set_table
|
119
|
+
@crosstab.table do
|
120
|
+
# empty on purpose
|
121
|
+
end
|
122
|
+
|
123
|
+
assert_equal 1, @crosstab.tables.length, "`table {}` should push a new table onto `tables`. `tables` should now have a length of 1."
|
124
|
+
assert_kind_of Crosstab::Table, @crosstab.tables.first, "`table {}` should push a new Crosstab::Table object onto `tables`. It didn't."
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_set_printed?
|
128
|
+
@crosstab.printed? true
|
129
|
+
assert_equal true, @crosstab.printed?
|
130
|
+
end
|
131
|
+
|
132
|
+
# Test the post-calculation methods
|
133
|
+
def test_calculate_adds_cells
|
134
|
+
columns = @calculated_crosstab.banner.columns
|
135
|
+
rows = @calculated_crosstab.tables[0].rows
|
136
|
+
|
137
|
+
assert_equal true, rows.all? { |row| row.cells.length == columns.length }, "Each row should have a number of cells equal to the number of columns in the banner."
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_calculate_cells_make_sense
|
141
|
+
|
142
|
+
tables = []
|
143
|
+
|
144
|
+
tables << []
|
145
|
+
tables.last << [{:frequency => 3, :base => 3},
|
146
|
+
{:frequency => 3, :base => 3},
|
147
|
+
{:frequency => 0, :base => 0}] # Male
|
148
|
+
|
149
|
+
tables.last << [{:frequency => 0, :base => 3},
|
150
|
+
{:frequency => 0, :base => 3},
|
151
|
+
{:frequency => 0, :base => 0}] # Female
|
152
|
+
|
153
|
+
tables << []
|
154
|
+
tables.last << [{:frequency => 1, :base => 3},
|
155
|
+
{:frequency => 1, :base => 3},
|
156
|
+
{:frequency => 0, :base => 0}] # 18-34
|
157
|
+
tables.last << [{:frequency => 2, :base => 3},
|
158
|
+
{:frequency => 2, :base => 3},
|
159
|
+
{:frequency => 0, :base => 0}] # 35+
|
160
|
+
|
161
|
+
tables << []
|
162
|
+
tables.last << [{:frequency => 1, :base => 3},
|
163
|
+
{:frequency => 1, :base => 3},
|
164
|
+
{:frequency => 0, :base => 0}] # Very likely
|
165
|
+
tables.last << [{:frequency => 1, :base => 3},
|
166
|
+
{:frequency => 1, :base => 3},
|
167
|
+
{:frequency => 0, :base => 0}] # Somewhat likely
|
168
|
+
tables.last << [{:frequency => 0, :base => 3},
|
169
|
+
{:frequency => 0, :base => 3},
|
170
|
+
{:frequency => 0, :base => 0}] # Somewhat unlikely
|
171
|
+
tables.last << [{:frequency => 1, :base => 3},
|
172
|
+
{:frequency => 1, :base => 3},
|
173
|
+
{:frequency => 0, :base => 0}] # Very unlikely
|
174
|
+
|
175
|
+
tables.each_with_index do |table, table_index|
|
176
|
+
table.each_with_index do |row, row_index|
|
177
|
+
row.each_with_index do |cell, cell_index|
|
178
|
+
cell.each do |method,value|
|
179
|
+
assert_equal value, @calculated_crosstab.
|
180
|
+
tables[table_index].
|
181
|
+
rows[row_index].
|
182
|
+
cells[cell_index].
|
183
|
+
send(method), "for @calculated_crosstab.tables[#{table_index}].rows[#{row_index}].cells[#{cell_index}].send(#{method.inspect})"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_calculate_groups
|
191
|
+
groups = []
|
192
|
+
groups << [{:frequency => 2, :base => 3},
|
193
|
+
{:frequency => 2, :base => 3},
|
194
|
+
{:frequency => 0, :base => 0}] # Very//somewhat likely
|
195
|
+
groups << [{:frequency => 1, :base => 3},
|
196
|
+
{:frequency => 1, :base => 3},
|
197
|
+
{:frequency => 0, :base => 0}] # Somewhat unlikely
|
198
|
+
|
199
|
+
groups.each_with_index do |row, index|
|
200
|
+
row.each_with_index do |cell, cell_index|
|
201
|
+
cell.each do |method, value|
|
202
|
+
# index + 1 is a cheat for referencing rows 1 & 2 which both have two different groups.
|
203
|
+
assert_equal value,
|
204
|
+
@calculated_crosstab.tables[2].rows[index + 1].group.cells[cell_index].send(method)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def test_add_tests_for_to_s
|
211
|
+
assert false
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|