table_helper 0.0.5 → 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/{CHANGELOG → CHANGELOG.rdoc} +11 -10
- data/{MIT-LICENSE → LICENSE} +0 -0
- data/{README → README.rdoc} +9 -9
- data/Rakefile +44 -36
- data/lib/table_helper.rb +173 -175
- data/lib/table_helper/body.rb +102 -105
- data/lib/table_helper/body_row.rb +58 -74
- data/lib/table_helper/cell.rb +44 -46
- data/lib/table_helper/collection_table.rb +53 -49
- data/lib/table_helper/footer.rb +36 -38
- data/lib/table_helper/header.rb +138 -140
- data/lib/table_helper/html_element.rb +39 -41
- data/lib/table_helper/row.rb +85 -87
- data/test/{table_helper_test.rb → helpers/table_helper_test.rb} +5 -5
- data/test/test_helper.rb +4 -7
- data/test/{body_row_test.rb → unit/body_row_test.rb} +13 -13
- data/test/{body_test.rb → unit/body_test.rb} +35 -35
- data/test/{cell_test.rb → unit/cell_test.rb} +7 -7
- data/test/{collection_table_test.rb → unit/collection_table_test.rb} +31 -31
- data/test/{footer_test.rb → unit/footer_test.rb} +12 -12
- data/test/{header_builder_test.rb → unit/header_builder_test.rb} +8 -8
- data/test/{header_test.rb → unit/header_test.rb} +36 -36
- data/test/{html_element_test.rb → unit/html_element_test.rb} +8 -8
- data/test/{row_builder_test.rb → unit/row_builder_test.rb} +9 -9
- data/test/{row_test.rb → unit/row_test.rb} +11 -11
- metadata +37 -35
data/lib/table_helper/body.rb
CHANGED
@@ -1,119 +1,116 @@
|
|
1
1
|
require 'table_helper/body_row'
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
3
|
+
module TableHelper
|
4
|
+
# Represents the body of the table. In HTML, you can think of this as
|
5
|
+
# the <tbody> tag of the table.
|
6
|
+
class Body < HtmlElement
|
7
|
+
# If set to :odd or :even, every odd or even-numbered row will have the
|
8
|
+
# class 'alternate' appended to its html attributes. Default is nil.
|
9
|
+
attr_accessor :alternate_rows
|
10
|
+
|
11
|
+
# The caption to display in the collection is empty
|
12
|
+
attr_accessor :empty_caption
|
13
|
+
|
14
|
+
def initialize(collection, header) #:nodoc:
|
15
|
+
super()
|
12
16
|
|
13
|
-
|
14
|
-
|
17
|
+
@collection, @header = collection, header
|
18
|
+
@empty_caption = 'No matches found.'
|
19
|
+
end
|
20
|
+
|
21
|
+
def alternate_rows=(value) #:nodoc:
|
22
|
+
raise ArgumentError, 'alternate_rows must be set to :odd or :even' if value && ![:odd, :even].include?(value)
|
23
|
+
@alternate_rows = value
|
24
|
+
end
|
25
|
+
|
26
|
+
# Builds the body of the table. This includes the actual data that is
|
27
|
+
# generated for each object in the collection.
|
28
|
+
#
|
29
|
+
# +build+ expects a block that defines the data in each cell. Each
|
30
|
+
# iteration of the block will provide the object being rendered, the row
|
31
|
+
# within the table that will be built and the index of the object. For
|
32
|
+
# example,
|
33
|
+
#
|
34
|
+
# body.build do |row, post, index|
|
35
|
+
# row.title "<div class=\"wrapped\">#{post.title}</div>"
|
36
|
+
# row.category post.category.name
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# In addition, to specifying the data, you can also modify the html
|
40
|
+
# options of the row. For more information on doing this, see the
|
41
|
+
# BodyRow class.
|
42
|
+
#
|
43
|
+
# If the collection is empty and +empty_caption+ is set on the body,
|
44
|
+
# then the actual body will be replaced by a single row containing the
|
45
|
+
# html that was stored in +empty_caption+.
|
46
|
+
#
|
47
|
+
# == Default Values
|
48
|
+
#
|
49
|
+
# Whenever possible, the default value of a cell will be set to the
|
50
|
+
# object's attribute with the same name as the cell. For example,
|
51
|
+
# if a Post consists of the attribute +title+, then the cell for the
|
52
|
+
# title will be prepopulated with that attribute's value:
|
53
|
+
#
|
54
|
+
# body.build do |row, post index|
|
55
|
+
# row.category post.category.name
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# <tt>row.title</tt> is already set to post.category so there's no need to
|
59
|
+
# manually set the value of that cell. However, it is always possible
|
60
|
+
# to override the default value like so:
|
61
|
+
#
|
62
|
+
# body.build do |row, post, index|
|
63
|
+
# row.title link_to(post.title, post_url(post))
|
64
|
+
# row.category post.category.name
|
65
|
+
# end
|
66
|
+
def build(&block)
|
67
|
+
@content = ''
|
15
68
|
|
16
|
-
|
17
|
-
|
69
|
+
# Display nothing if there are no objects to display
|
70
|
+
if @collection.empty? && @empty_caption
|
71
|
+
row = Row.new
|
72
|
+
row[:class] = 'no_content'
|
18
73
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
def alternate_rows=(value) #:nodoc:
|
24
|
-
raise ArgumentError, 'alternate_rows must be set to :odd or :even' if value && ![:odd, :even].include?(value)
|
25
|
-
@alternate_rows = value
|
26
|
-
end
|
27
|
-
|
28
|
-
# Builds the body of the table. This includes the actual data that is
|
29
|
-
# generated for each object in the collection.
|
30
|
-
#
|
31
|
-
# build expects a block that defines the data in each cell. Each
|
32
|
-
# iteration of the block will provide the object being rendered, the row
|
33
|
-
# within the table that will be built and the index of the object. For
|
34
|
-
# example,
|
35
|
-
#
|
36
|
-
# body.build do |row, post, index|
|
37
|
-
# row.title "<div class=\"wrapped\">#{post.title}</div>"
|
38
|
-
# row.category post.category.name
|
39
|
-
# end
|
40
|
-
#
|
41
|
-
# In addition, to specifying the data, you can also modify the html
|
42
|
-
# options of the row. For more information on doing this, see the
|
43
|
-
# BodyRow class.
|
44
|
-
#
|
45
|
-
# If the collection is empty and +empty_caption+ is set on the Body,
|
46
|
-
# then the actual body will be replaced by a single row containing the
|
47
|
-
# html that was stored in +empty_caption+.
|
48
|
-
#
|
49
|
-
# == Default Values
|
50
|
-
#
|
51
|
-
# Whenever possible, the default value of a cell will be set to the
|
52
|
-
# object's attribute with the same name as the cell. For example,
|
53
|
-
# if a Post consists of the attribute +title+, then the cell for the
|
54
|
-
# title will be prepopulated with that attribute's value:
|
55
|
-
#
|
56
|
-
# body.build do |row, post index|
|
57
|
-
# row.category post.category.name
|
58
|
-
# end
|
59
|
-
#
|
60
|
-
# +row.title+ is already set to post.category so there's no need to
|
61
|
-
# manually set the value of that cell. However, it is always possible
|
62
|
-
# to override the default value like so:
|
63
|
-
#
|
64
|
-
# body.build do |row, post, index|
|
65
|
-
# row.title link_to(post.title, post_url(post))
|
66
|
-
# row.category post.category.name
|
67
|
-
# end
|
68
|
-
def build(&block)
|
69
|
-
@content = ''
|
74
|
+
html_options = {}
|
75
|
+
html_options[:colspan] = @header.column_names.size if @header.column_names.size > 1
|
76
|
+
row.cell nil, @empty_caption, html_options
|
70
77
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
html_options = {}
|
77
|
-
html_options[:colspan] = @header.column_names.size if @header.column_names.size > 1
|
78
|
-
row.cell nil, @empty_caption, html_options
|
79
|
-
|
80
|
-
@content << row.html
|
81
|
-
else
|
82
|
-
@collection.each_with_index do |object, i|
|
83
|
-
@content << build_row(object, i, &block)
|
84
|
-
end
|
78
|
+
@content << row.html
|
79
|
+
else
|
80
|
+
@collection.each_with_index do |object, i|
|
81
|
+
@content << build_row(object, i, &block)
|
85
82
|
end
|
86
|
-
|
87
|
-
@content
|
88
83
|
end
|
89
84
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
row.html
|
100
|
-
end
|
85
|
+
@content
|
86
|
+
end
|
87
|
+
|
88
|
+
# Builds a row for an object in the table.
|
89
|
+
#
|
90
|
+
# The provided block should set the values for each cell in the row.
|
91
|
+
def build_row(object, index = @collection.index(object), &block)
|
92
|
+
row = BodyRow.new(object, @header)
|
93
|
+
row.alternate = alternate_rows ? index.send("#{@alternate_rows}?") : false
|
101
94
|
|
102
|
-
|
103
|
-
html_options = @html_options.dup
|
104
|
-
html_options[:class] = (html_options[:class].to_s + ' alternate').strip if alternate_rows
|
105
|
-
|
106
|
-
content_tag(tag_name, content, html_options)
|
107
|
-
end
|
95
|
+
yield row.builder, object, index if block_given?
|
108
96
|
|
109
|
-
|
110
|
-
def tag_name
|
111
|
-
'tbody'
|
112
|
-
end
|
113
|
-
|
114
|
-
def content
|
115
|
-
@content
|
116
|
-
end
|
97
|
+
row.html
|
117
98
|
end
|
99
|
+
|
100
|
+
def html #:nodoc:
|
101
|
+
html_options = @html_options.dup
|
102
|
+
html_options[:class] = (html_options[:class].to_s + ' alternate').strip if alternate_rows
|
103
|
+
|
104
|
+
content_tag(tag_name, content, html_options)
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
def tag_name
|
109
|
+
'tbody'
|
110
|
+
end
|
111
|
+
|
112
|
+
def content
|
113
|
+
@content
|
114
|
+
end
|
118
115
|
end
|
119
116
|
end
|
@@ -1,86 +1,70 @@
|
|
1
1
|
require 'table_helper/row'
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
# r.border[:style] = 'color: #ff0000;'
|
21
|
-
#
|
22
|
-
# == Alternating rows
|
23
|
-
#
|
24
|
-
# Alternating rows can be automated by setting the +alternate+ property.
|
25
|
-
# For example,
|
26
|
-
#
|
27
|
-
# r = BodyRow.new
|
28
|
-
# r.alternate = true
|
29
|
-
class BodyRow < Row
|
30
|
-
# True if this is an alternating row, otherwise false. Default is false.
|
31
|
-
attr_accessor :alternate
|
3
|
+
module TableHelper
|
4
|
+
# Represents a single row within the body of a table. The row can consist
|
5
|
+
# of either data cells or header cells.
|
6
|
+
#
|
7
|
+
# == Alternating rows
|
8
|
+
#
|
9
|
+
# Alternating rows can be automated by setting the +alternate+ property.
|
10
|
+
# For example,
|
11
|
+
#
|
12
|
+
# r = BodyRow.new
|
13
|
+
# r.alternate = true
|
14
|
+
class BodyRow < Row
|
15
|
+
# True if this is an alternating row, otherwise false. Default is false.
|
16
|
+
attr_accessor :alternate
|
17
|
+
|
18
|
+
def initialize(object, header) #:nodoc:
|
19
|
+
super()
|
32
20
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
21
|
+
@header = header
|
22
|
+
@alternate = false
|
23
|
+
@html_options[:class] = ('row ' + @html_options[:class].to_s).strip
|
24
|
+
|
25
|
+
# For each column defined in the table, see if we can prepopulate the
|
26
|
+
# cell based on the data in the object. If not, we can at least
|
27
|
+
# provide shortcut accessors to the cell
|
28
|
+
@header.column_names.each do |column|
|
29
|
+
if object.respond_to?(column)
|
30
|
+
cell(column, object.send(column))
|
31
|
+
else
|
32
|
+
builder.define_cell(column)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Generates the html for this row in additional to the border row
|
38
|
+
# (if specified)
|
39
|
+
def html
|
40
|
+
original_options = @html_options.dup
|
41
|
+
@html_options[:class] = (@html_options[:class].to_s + ' alternate').strip if alternate
|
42
|
+
html = super
|
43
|
+
@html_options = original_options
|
44
|
+
html
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
# Builds the row's cells based on the order of the columns in the
|
49
|
+
# header. If a cell cannot be found for a specific column, then a blank
|
50
|
+
# cell is rendered.
|
51
|
+
def content
|
52
|
+
number_to_skip = 0 # Keeps track of the # of columns to skip
|
39
53
|
|
40
|
-
|
41
|
-
# cell based on the data in the object. If not, we can at least
|
42
|
-
# provide shortcut accessors to the cell
|
54
|
+
html = ''
|
43
55
|
@header.column_names.each do |column|
|
44
|
-
if
|
45
|
-
|
56
|
+
number_to_skip -= 1 and next if number_to_skip > 0
|
57
|
+
|
58
|
+
if cell = @cells[column]
|
59
|
+
number_to_skip = (cell[:colspan] || 1) - 1
|
46
60
|
else
|
47
|
-
|
61
|
+
cell = Cell.new(column, '', :class => 'empty')
|
48
62
|
end
|
63
|
+
|
64
|
+
html << cell.html
|
49
65
|
end
|
50
|
-
|
51
|
-
|
52
|
-
# Generates the html for this row in additional to the border row
|
53
|
-
# (if specified)
|
54
|
-
def html
|
55
|
-
original_options = @html_options.dup
|
56
|
-
@html_options[:class] = (@html_options[:class].to_s + ' alternate').strip if alternate
|
57
|
-
html = super
|
58
|
-
@html_options = original_options
|
66
|
+
|
59
67
|
html
|
60
68
|
end
|
61
|
-
|
62
|
-
private
|
63
|
-
# Builds the row's cells based on the order of the columns in the
|
64
|
-
# header. If a cell cannot be found for a specific column, then a blank
|
65
|
-
# cell is rendered.
|
66
|
-
def content
|
67
|
-
number_to_skip = 0 # Keeps track of the # of columns to skip
|
68
|
-
|
69
|
-
html = ''
|
70
|
-
@header.column_names.each do |column|
|
71
|
-
number_to_skip -= 1 and next if number_to_skip > 0
|
72
|
-
|
73
|
-
if cell = @cells[column]
|
74
|
-
number_to_skip = (cell[:colspan] || 1) - 1
|
75
|
-
else
|
76
|
-
cell = Cell.new(column, '', :class => 'empty')
|
77
|
-
end
|
78
|
-
|
79
|
-
html << cell.html
|
80
|
-
end
|
81
|
-
|
82
|
-
html
|
83
|
-
end
|
84
|
-
end
|
85
69
|
end
|
86
70
|
end
|
data/lib/table_helper/cell.rb
CHANGED
@@ -1,51 +1,49 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
super(html_options)
|
27
|
-
|
28
|
-
@content = content
|
29
|
-
@html_options[:class] = ("#{class_name} " + @html_options[:class].to_s).strip if class_name
|
30
|
-
|
31
|
-
self.content_type = :data
|
32
|
-
end
|
1
|
+
module TableHelper
|
2
|
+
# Represents a single cell within a table. This can either be a regular
|
3
|
+
# data cell (td) or a header cell (th). By default, all cells will have
|
4
|
+
# their column name appended to the cell's class attribute.
|
5
|
+
#
|
6
|
+
# == Creating data cells
|
7
|
+
#
|
8
|
+
# Cell.new(:author, 'John Doe').build
|
9
|
+
#
|
10
|
+
# ...would generate the following tag:
|
11
|
+
#
|
12
|
+
# <td class="author">John Doe</td>
|
13
|
+
#
|
14
|
+
# == Creating header cells
|
15
|
+
#
|
16
|
+
# c = Cell.new(:author, 'Author Name')
|
17
|
+
# c.content_type = :header
|
18
|
+
# c.build
|
19
|
+
#
|
20
|
+
# ...would generate the following tag:
|
21
|
+
#
|
22
|
+
# <th class="author">Author Name</th>
|
23
|
+
class Cell < HtmlElement
|
24
|
+
def initialize(class_name, content = class_name.to_s.titleize, html_options = {}) #:nodoc
|
25
|
+
super(html_options)
|
33
26
|
|
34
|
-
|
35
|
-
|
36
|
-
def content_type=(value)
|
37
|
-
raise ArgumentError, "content_type must be set to :data or :header, was: #{value.inspect}" unless [:data, :header].include?(value)
|
38
|
-
@content_type = value
|
39
|
-
end
|
27
|
+
@content = content
|
28
|
+
@html_options[:class] = ("#{class_name} " + @html_options[:class].to_s).strip if class_name
|
40
29
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
30
|
+
self.content_type = :data
|
31
|
+
end
|
32
|
+
|
33
|
+
# Indicates what type of content will be stored in this cell. This can
|
34
|
+
# be set to either :data or :header.
|
35
|
+
def content_type=(value)
|
36
|
+
raise ArgumentError, "content_type must be set to :data or :header, was: #{value.inspect}" unless [:data, :header].include?(value)
|
37
|
+
@content_type = value
|
49
38
|
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def tag_name
|
42
|
+
@content_type == :data ? 'td' : 'th'
|
43
|
+
end
|
44
|
+
|
45
|
+
def content
|
46
|
+
@content
|
47
|
+
end
|
50
48
|
end
|
51
49
|
end
|