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
@@ -3,58 +3,62 @@ require 'table_helper/header'
|
|
3
3
|
require 'table_helper/body'
|
4
4
|
require 'table_helper/footer'
|
5
5
|
|
6
|
-
module
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
6
|
+
module TableHelper
|
7
|
+
# Represents a table that displays data for multiple objects within a
|
8
|
+
# collection.
|
9
|
+
class CollectionTable < HtmlElement
|
10
|
+
# Creates a new table based on the objects within the given collection.
|
11
|
+
#
|
12
|
+
# Configuration options:
|
13
|
+
# * +class+ - The actual class of the objects contained within the collection. This is used to help build the header columns.
|
14
|
+
# * +header+ - Whether or not to display a header. Default is true.
|
15
|
+
# * +footer+ - Whether or not to display a footer. Default is false.
|
16
|
+
def initialize(collection, options = {}, html_options = {}) #:nodoc:
|
17
|
+
super(html_options)
|
18
|
+
|
19
|
+
options.assert_valid_keys(
|
20
|
+
:class,
|
21
|
+
:footer,
|
22
|
+
:header
|
23
|
+
)
|
24
|
+
@options = options.reverse_merge(
|
25
|
+
:header => true,
|
26
|
+
:footer => false
|
27
|
+
)
|
28
|
+
|
29
|
+
@html_options.reverse_merge!(
|
30
|
+
:cellspacing => '0',
|
31
|
+
:cellpadding => '0'
|
32
|
+
)
|
33
|
+
|
34
|
+
@header = Header.new(collection, options[:class])
|
35
|
+
@body = Body.new(collection, @header)
|
36
|
+
@footer = Footer.new(collection)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Builds the table by rendering a header (if enabled), body, and footer (if enabled).
|
40
|
+
def build(&block)
|
41
|
+
@body.build # Build with the defaults
|
42
|
+
|
43
|
+
elements = []
|
44
|
+
elements << @header.builder if @options[:header]
|
45
|
+
elements << @body
|
46
|
+
elements << @footer if @options[:footer]
|
47
|
+
|
48
|
+
yield *elements if block_given?
|
49
|
+
|
50
|
+
@content = ''
|
51
|
+
elements.each {|element| @content << element.html}
|
52
|
+
@content
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def tag_name
|
57
|
+
'table'
|
32
58
|
end
|
33
59
|
|
34
|
-
|
35
|
-
def build(&block)
|
36
|
-
@body.build # Build with the defaults
|
37
|
-
|
38
|
-
elements = []
|
39
|
-
elements << @header.builder if @options[:header]
|
40
|
-
elements << @body
|
41
|
-
elements << @footer if @options[:footer]
|
42
|
-
|
43
|
-
yield *elements if block_given?
|
44
|
-
|
45
|
-
@content = ''
|
46
|
-
elements.each {|element| @content << element.html}
|
60
|
+
def content
|
47
61
|
@content
|
48
62
|
end
|
49
|
-
|
50
|
-
private
|
51
|
-
def tag_name
|
52
|
-
'table'
|
53
|
-
end
|
54
|
-
|
55
|
-
def content
|
56
|
-
@content
|
57
|
-
end
|
58
|
-
end
|
59
63
|
end
|
60
64
|
end
|
data/lib/table_helper/footer.rb
CHANGED
@@ -1,47 +1,45 @@
|
|
1
1
|
require 'table_helper/row'
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
module TableHelper
|
4
|
+
# Represents the header of the table. In HTML, you can think of this as
|
5
|
+
# the <tfoot> tag of the table.
|
6
|
+
class Footer < HtmlElement
|
7
|
+
# The actual footer row
|
8
|
+
attr_reader :row
|
9
|
+
|
10
|
+
delegate :cell,
|
11
|
+
:to => :row
|
12
|
+
|
13
|
+
# Whether or not the footer should be hidden when the collection is
|
14
|
+
# empty. Default is true.
|
15
|
+
attr_accessor :hide_when_empty
|
16
|
+
|
17
|
+
def initialize(collection) #:nodoc:
|
18
|
+
super()
|
10
19
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
20
|
+
@collection = collection
|
21
|
+
@row = Row.new
|
22
|
+
@hide_when_empty = true
|
23
|
+
end
|
24
|
+
|
25
|
+
def html #:nodoc:
|
26
|
+
html_options = @html_options.dup
|
27
|
+
html_options[:style] = 'display: none;' if @collection.empty? && hide_when_empty
|
17
28
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
29
|
+
content_tag(tag_name, content, html_options)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def tag_name
|
34
|
+
'tfoot'
|
24
35
|
end
|
25
36
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
37
|
+
# Generates the html for the footer. The footer generally consists of a
|
38
|
+
# summary of the data in the body. This row will be wrapped inside of
|
39
|
+
# a tfoot tag. If the collection is empty and hide_when_empty was set
|
40
|
+
# to true, then the footer will be hidden.
|
41
|
+
def content
|
42
|
+
@row.html
|
31
43
|
end
|
32
|
-
|
33
|
-
private
|
34
|
-
def tag_name
|
35
|
-
'tfoot'
|
36
|
-
end
|
37
|
-
|
38
|
-
# Generates the html for the footer. The footer generally consists of a
|
39
|
-
# summary of the data in the body. This row will be wrapped inside of
|
40
|
-
# a tfoot tag. If the collection is empty and hide_when_empty was set
|
41
|
-
# to true, then the footer will be hidden.
|
42
|
-
def content
|
43
|
-
@row.html
|
44
|
-
end
|
45
|
-
end
|
46
44
|
end
|
47
45
|
end
|
data/lib/table_helper/header.rb
CHANGED
@@ -1,163 +1,161 @@
|
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
method_name = name.to_s.gsub('-', '_')
|
29
|
-
|
30
|
-
klass = class << self; self; end
|
31
|
-
klass.class_eval do
|
32
|
-
define_method(method_name) do |*args|
|
33
|
-
header.row.builder.__send__(method_name, *args)
|
34
|
-
end
|
35
|
-
end unless klass.method_defined?(method_name)
|
36
|
-
end
|
3
|
+
module TableHelper
|
4
|
+
# Provides a blank class that can be used to build the columns for a header
|
5
|
+
class HeaderBuilder < BlankSlate #:nodoc:
|
6
|
+
reveal :respond_to?
|
7
|
+
|
8
|
+
attr_reader :header
|
9
|
+
|
10
|
+
# Creates a builder for the given header
|
11
|
+
def initialize(header)
|
12
|
+
@header = header
|
13
|
+
end
|
14
|
+
|
15
|
+
# Proxies all missed methods to the header
|
16
|
+
def method_missing(*args)
|
17
|
+
header.send(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Defines the accessor method for the given column name. For example, if
|
21
|
+
# a column with the name :title was defined, then the column would be able
|
22
|
+
# to be read and written like so:
|
23
|
+
#
|
24
|
+
# header.title #=> Accesses the title
|
25
|
+
# header.title "Page Title" #=> Creates a new column with "Page Title" as the content
|
26
|
+
def define_column(name)
|
27
|
+
method_name = name.to_s.gsub('-', '_')
|
37
28
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
remove_method(name.gsub('-', '_'))
|
29
|
+
klass = class << self; self; end
|
30
|
+
klass.class_eval do
|
31
|
+
define_method(method_name) do |*args|
|
32
|
+
header.row.builder.__send__(method_name, *args)
|
43
33
|
end
|
34
|
+
end unless klass.method_defined?(method_name)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Removes the definition for the given cp;i,m
|
38
|
+
def undef_column(name)
|
39
|
+
klass = class << self; self; end
|
40
|
+
klass.class_eval do
|
41
|
+
remove_method(name.gsub('-', '_'))
|
44
42
|
end
|
45
43
|
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Represents the header of the table. In HTML, you can think of this as
|
47
|
+
# the <thead> tag of the table.
|
48
|
+
class Header < HtmlElement
|
49
|
+
# The actual header row
|
50
|
+
attr_reader :row
|
51
|
+
|
52
|
+
# The proxy class used externally to build the actual columns
|
53
|
+
attr_reader :builder
|
54
|
+
|
55
|
+
# Whether or not the header should be hidden when the collection is
|
56
|
+
# empty. Default is true.
|
57
|
+
attr_accessor :hide_when_empty
|
46
58
|
|
47
|
-
#
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
59
|
+
# Creates a new header for a collection that contains objects of the
|
60
|
+
# given class.
|
61
|
+
#
|
62
|
+
# If the class is known, then the header will be pre-filled with
|
63
|
+
# the columns defined in that class (assuming it's an ActiveRecord
|
64
|
+
# class).
|
65
|
+
def initialize(collection, klass = nil)
|
66
|
+
super()
|
52
67
|
|
53
|
-
|
54
|
-
|
68
|
+
@collection = collection
|
69
|
+
@row = Row.new
|
70
|
+
@builder = HeaderBuilder.new(self)
|
55
71
|
|
56
|
-
|
57
|
-
|
58
|
-
attr_accessor :hide_when_empty
|
72
|
+
@hide_when_empty = true
|
73
|
+
@customized = true
|
59
74
|
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
75
|
+
# If we know what class the objects in the collection are and we can
|
76
|
+
# figure out what columns are defined in that class, then we can
|
77
|
+
# pre-fill the header with those columns so that the user doesn't
|
78
|
+
# have to
|
79
|
+
klass ||= class_for_collection(collection)
|
80
|
+
if klass && klass.respond_to?(:column_names)
|
81
|
+
klass.column_names.each {|name| column(name)}
|
82
|
+
@customized = false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# The current columns in this header, in the order in which they will be built
|
87
|
+
def columns
|
88
|
+
row.cells
|
89
|
+
end
|
90
|
+
|
91
|
+
# Gets the names of all of the columns being displayed in the table
|
92
|
+
def column_names
|
93
|
+
row.cell_names
|
94
|
+
end
|
95
|
+
|
96
|
+
# Clears all of the current columns from the header
|
97
|
+
def clear
|
98
|
+
# Remove all of the shortcut methods
|
99
|
+
column_names.each {|name| builder.undef_column(name)}
|
100
|
+
row.clear
|
101
|
+
end
|
102
|
+
|
103
|
+
# Creates a new column with the specified caption. Columns must be
|
104
|
+
# defined in the order in which they will be rendered.
|
105
|
+
#
|
106
|
+
# The caption determines what will be displayed in each cell of the
|
107
|
+
# header (if the header is rendered). For example,
|
108
|
+
#
|
109
|
+
# header.column :title, 'The Title'
|
110
|
+
#
|
111
|
+
# ...will create a column the displays "The Title" in the cell.
|
112
|
+
#
|
113
|
+
# = Setting html options
|
114
|
+
#
|
115
|
+
# In addition to customizing the content of the column, you can also
|
116
|
+
# specify html options like so:
|
117
|
+
#
|
118
|
+
# header.column :title, 'The Title', :class => 'pretty'
|
119
|
+
def column(name, *args)
|
120
|
+
# Clear the header row if this is being customized by the user
|
121
|
+
unless @customized
|
74
122
|
@customized = true
|
75
|
-
|
76
|
-
# If we know what class the objects in the collection are and we can
|
77
|
-
# figure out what columns are defined in that class, then we can
|
78
|
-
# pre-fill the header with those columns so that the user doesn't
|
79
|
-
# have to
|
80
|
-
klass ||= class_for_collection(collection)
|
81
|
-
if klass && klass.respond_to?(:column_names)
|
82
|
-
klass.column_names.each {|name| column(name)}
|
83
|
-
@customized = false
|
84
|
-
end
|
123
|
+
clear
|
85
124
|
end
|
86
125
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
end
|
126
|
+
column = row.cell(name, *args)
|
127
|
+
column.content_type = :header
|
128
|
+
column[:scope] ||= 'col'
|
91
129
|
|
92
|
-
|
93
|
-
def column_names
|
94
|
-
row.cell_names
|
95
|
-
end
|
130
|
+
builder.define_column(name)
|
96
131
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
132
|
+
column
|
133
|
+
end
|
134
|
+
|
135
|
+
# Creates and returns the generated html for the header
|
136
|
+
def html
|
137
|
+
html_options = @html_options.dup
|
138
|
+
html_options[:style] = 'display: none;' if @collection.empty? && hide_when_empty
|
103
139
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
#
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
# = Setting html options
|
115
|
-
#
|
116
|
-
# In addition to customizing the content of the column, you can also
|
117
|
-
# specify html options like so:
|
118
|
-
#
|
119
|
-
# header.column :title, 'The Title', :class => 'pretty'
|
120
|
-
def column(name, *args)
|
121
|
-
# Clear the header row if this is being customized by the user
|
122
|
-
unless @customized
|
123
|
-
@customized = true
|
124
|
-
clear
|
140
|
+
content_tag(tag_name, content, html_options)
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
# Finds the class representing the objects within the collection
|
145
|
+
def class_for_collection(collection)
|
146
|
+
if collection.respond_to?(:proxy_reflection)
|
147
|
+
collection.proxy_reflection.klass
|
148
|
+
elsif !collection.empty?
|
149
|
+
collection.first.class
|
125
150
|
end
|
126
|
-
|
127
|
-
column = row.cell(name, *args)
|
128
|
-
column.content_type = :header
|
129
|
-
column[:scope] ||= 'col'
|
130
|
-
|
131
|
-
builder.define_column(name)
|
132
|
-
|
133
|
-
column
|
134
151
|
end
|
135
152
|
|
136
|
-
|
137
|
-
|
138
|
-
html_options = @html_options.dup
|
139
|
-
html_options[:style] = 'display: none;' if @collection.empty? && hide_when_empty
|
140
|
-
|
141
|
-
content_tag(tag_name, content, html_options)
|
153
|
+
def tag_name
|
154
|
+
'thead'
|
142
155
|
end
|
143
156
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
if collection.respond_to?(:proxy_reflection)
|
148
|
-
collection.proxy_reflection.klass
|
149
|
-
elsif !collection.empty?
|
150
|
-
collection.first.class
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
def tag_name
|
155
|
-
'thead'
|
156
|
-
end
|
157
|
-
|
158
|
-
def content
|
159
|
-
@row.html
|
160
|
-
end
|
161
|
-
end
|
157
|
+
def content
|
158
|
+
@row.html
|
159
|
+
end
|
162
160
|
end
|
163
161
|
end
|