tangofoxtrot-table_helper 0.2.2

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,42 @@
1
+ module TableHelper
2
+ # Represents an HTML element
3
+ #
4
+ # == Modifying HTML options
5
+ #
6
+ # HTML options can normally be specified when creating the element.
7
+ # However, if they need to be modified after the element has been created,
8
+ # you can access the properties like so:
9
+ #
10
+ # r = Row.new
11
+ # r[:style] = 'display: none;'
12
+ #
13
+ # or for a cell:
14
+ #
15
+ # c = Cell.new
16
+ # c[:style] = 'display: none;'
17
+ class HtmlElement
18
+ include ActionView::Helpers::TagHelper
19
+
20
+ delegate :[], :[]=, :to => '@html_options'
21
+
22
+ def initialize(html_options = {}) #:nodoc:
23
+ @html_options = html_options.symbolize_keys
24
+ end
25
+
26
+ # Generates the html representing this element
27
+ def html
28
+ content_tag(tag_name, content, @html_options)
29
+ end
30
+
31
+ private
32
+ # The name of the element tag to use (e.g. td, th, tr, etc.)
33
+ def tag_name
34
+ ''
35
+ end
36
+
37
+ # The content that will be displayed inside of the tag
38
+ def content
39
+ ''
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,110 @@
1
+ require 'table_helper/cell'
2
+
3
+ module TableHelper
4
+ # Provides a blank class that can be used to build the cells for a row
5
+ class RowBuilder < BlankSlate #:nodoc:
6
+ reveal :respond_to?
7
+
8
+ # Creates a builder for the given row
9
+ def initialize(row)
10
+ @row = row
11
+ end
12
+
13
+ # Proxies all missed methods to the row
14
+ def method_missing(*args)
15
+ @row.send(*args)
16
+ end
17
+
18
+ # Defines the builder method for the given cell name. For example, if
19
+ # a cell with the name :title was defined, then the cell would be able
20
+ # to be read and written like so:
21
+ #
22
+ # row.title #=> Accesses the title
23
+ # row.title "Page Title" #=> Creates a new cell with "Page Title" as the content
24
+ def define_cell(name)
25
+ method_name = name.gsub('-', '_')
26
+
27
+ klass = class << self; self; end
28
+ klass.class_eval do
29
+ define_method(method_name) do |*args|
30
+ if args.empty?
31
+ @row.cells[name]
32
+ else
33
+ @row.cell(name, *args)
34
+ end
35
+ end
36
+ end unless klass.method_defined?(method_name)
37
+ end
38
+
39
+ # Removes the definition for the given cell
40
+ def undef_cell(name)
41
+ method_name = name.gsub('-', '_')
42
+
43
+ klass = class << self; self; end
44
+ klass.class_eval do
45
+ remove_method(method_name)
46
+ end
47
+ end
48
+ end
49
+
50
+ # Represents a single row within a table. A row can consist of either
51
+ # data cells or header cells.
52
+ class Row < HtmlElement
53
+ # The proxy class used externally to build the actual cells
54
+ attr_reader :builder
55
+
56
+ # The current cells in this row, in the order in which they will be built
57
+ attr_reader :cells
58
+
59
+ # The parent element for this row
60
+ attr_reader :parent
61
+
62
+ delegate :empty?, :to => :cells
63
+ delegate :table, :to => :parent
64
+
65
+ def initialize(parent) #:nodoc:
66
+ super()
67
+
68
+ @parent = parent
69
+ @cells = ActiveSupport::OrderedHash.new
70
+ @builder = RowBuilder.new(self)
71
+ end
72
+
73
+ # Creates a new cell with the given name and generates shortcut
74
+ # accessors for the method.
75
+ def cell(name, *args)
76
+ name = name.to_s if name
77
+
78
+ options = args.last.is_a?(Hash) ? args.pop : {}
79
+ options[:namespace] = table.object_name
80
+ args << options
81
+
82
+ cell = Cell.new(name, *args)
83
+ cells[name] = cell
84
+ builder.define_cell(name) if name
85
+
86
+ cell
87
+ end
88
+
89
+ # The names of all cells in this row
90
+ def cell_names
91
+ cells.keys
92
+ end
93
+
94
+ # Clears all of the current cells from the row
95
+ def clear
96
+ # Remove all of the shortcut methods
97
+ cell_names.each {|name| builder.undef_cell(name)}
98
+ cells.clear
99
+ end
100
+
101
+ private
102
+ def tag_name
103
+ 'tr'
104
+ end
105
+
106
+ def content
107
+ cells.values.map(&:html).join
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,2 @@
1
+ class Person < ActiveRecord::Base
2
+ end
@@ -0,0 +1,11 @@
1
+ class CreatePeople < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :people do |t|
4
+ t.string :first_name, :last_name
5
+ end
6
+ end
7
+
8
+ def self.down
9
+ drop_table :people
10
+ end
11
+ end
@@ -0,0 +1,45 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class TableHelperTest < ActionView::TestCase
4
+ tests TableHelper
5
+
6
+ class Post
7
+ end
8
+
9
+ def test_should_build_collection_table
10
+ html = collection_table(['first', 'second', 'last'], Post) do |t|
11
+ t.header :title
12
+ t.rows.each do |row, post_title, index|
13
+ row.title post_title
14
+ end
15
+ t.footer :total, t.collection.length
16
+ end
17
+
18
+ expected = <<-end_str
19
+ <table cellpadding="0" cellspacing="0" class="posts ui-collection">
20
+ <thead>
21
+ <tr>
22
+ <th class="post-title" scope="col">Title</th>
23
+ </tr>
24
+ </thead>
25
+ <tbody>
26
+ <tr class="post ui-collection-result">
27
+ <td class="post-title">first</td>
28
+ </tr>
29
+ <tr class="post ui-collection-result">
30
+ <td class="post-title">second</td>
31
+ </tr>
32
+ <tr class="post ui-collection-result">
33
+ <td class="post-title">last</td>
34
+ </tr>
35
+ </tbody>
36
+ <tfoot>
37
+ <tr>
38
+ <td class="post-total">3</td>
39
+ </tr>
40
+ </tfoot>
41
+ </table>
42
+ end_str
43
+ assert_html_equal expected, html
44
+ end
45
+ end
@@ -0,0 +1,14 @@
1
+ # Load the plugin testing framework
2
+ $:.unshift("#{File.dirname(__FILE__)}/../../plugin_test_helper/lib")
3
+ require 'rubygems'
4
+ require 'plugin_test_helper'
5
+
6
+ # Run the migrations
7
+ ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate")
8
+
9
+ Test::Unit::TestCase.class_eval do
10
+ private
11
+ def assert_html_equal(expected, actual)
12
+ assert_equal expected.strip.gsub(/\n\s*/, ''), actual
13
+ end
14
+ end
@@ -0,0 +1,155 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class BodyRowByDefaultTest < Test::Unit::TestCase
4
+ def setup
5
+ table = TableHelper::CollectionTable.new([])
6
+ @header = TableHelper::Header.new(table)
7
+ @row = TableHelper::BodyRow.new(Object.new, @header)
8
+ end
9
+
10
+ def test_should_have_a_parent
11
+ assert_equal @header, @row.parent
12
+ end
13
+
14
+ def test_should_not_alternate
15
+ assert !@row.alternate
16
+ end
17
+
18
+ def test_should_have_a_class_name
19
+ assert_equal 'ui-collection-result', @row[:class]
20
+ end
21
+
22
+ def test_should_use_custom_result_class_if_specified
23
+ original_result_class = TableHelper::BodyRow.result_class
24
+ TableHelper::BodyRow.result_class = 'ui-collection-item'
25
+
26
+ row = TableHelper::BodyRow.new(Object.new, @header)
27
+ assert_equal 'ui-collection-item', row[:class]
28
+ ensure
29
+ TableHelper::BodyRow.result_class = original_result_class
30
+ end
31
+ end
32
+
33
+ class BodyRowTest < Test::Unit::TestCase
34
+ class Post
35
+ def title
36
+ 'Default Value'
37
+ end
38
+ end
39
+
40
+ def setup
41
+ table = TableHelper::CollectionTable.new([])
42
+ header = table.header
43
+ header.column :title
44
+
45
+ @row = TableHelper::BodyRow.new(Post.new, header)
46
+ end
47
+
48
+ def test_should_generate_cell_accessors
49
+ assert_nothing_raised {@row.builder.title}
50
+ end
51
+
52
+ def test_should_override_default_cell_content_if_cell_specified
53
+ @row.builder.title 'Hello World'
54
+ assert_equal '<tr class="ui-collection-result"><td class="title">Hello World</td></tr>', @row.html
55
+ end
56
+ end
57
+
58
+ class BodyRowWithTableObjectNameTest < Test::Unit::TestCase
59
+ def setup
60
+ table = TableHelper::CollectionTable.new([], Object)
61
+ header = table.header
62
+
63
+ @row = TableHelper::BodyRow.new(Object.new, header)
64
+ end
65
+
66
+ def test_should_include_object_name_in_class
67
+ assert_equal 'object ui-collection-result', @row[:class]
68
+ end
69
+ end
70
+
71
+ class BodyRowWithNoColumnsTest < Test::Unit::TestCase
72
+ def setup
73
+ table = TableHelper::CollectionTable.new([])
74
+ header = table.header
75
+
76
+ @row = TableHelper::BodyRow.new(Object.new, header)
77
+ end
78
+
79
+ def test_should_not_build_cells
80
+ assert_equal '<tr class="ui-collection-result"></tr>', @row.html
81
+ end
82
+ end
83
+
84
+ class BodyRowWithCustomAttributeTest < Test::Unit::TestCase
85
+ class Post
86
+ def title
87
+ 'Default Value'
88
+ end
89
+ end
90
+
91
+ def setup
92
+ table = TableHelper::CollectionTable.new([])
93
+ header = table.header
94
+ header.column :title
95
+ header.column :author_name
96
+
97
+ @row = TableHelper::BodyRow.new(Post.new, header)
98
+ end
99
+
100
+ def test_should_use_attribute_values_as_cell_content
101
+ @row.builder.author_name 'John Doe'
102
+ assert_equal '<tr class="ui-collection-result"><td class="title">Default Value</td><td class="author_name">John Doe</td></tr>', @row.html
103
+ end
104
+ end
105
+
106
+ class BodyRowWithMissingCellsTest < Test::Unit::TestCase
107
+ def setup
108
+ table = TableHelper::CollectionTable.new([])
109
+ header = table.header
110
+ header.column :title
111
+ header.column :author_name
112
+
113
+ @row = TableHelper::BodyRow.new(Object.new, header)
114
+ end
115
+
116
+ def test_should_build_missing_cells_if_cells_not_specified
117
+ assert_equal '<tr class="ui-collection-result"><td class="title ui-state-empty"></td><td class="author_name ui-state-empty"></td></tr>', @row.html
118
+ end
119
+
120
+ def test_should_skip_missing_cells_if_colspan_replaces_missing_cells
121
+ @row.builder.title 'Hello World', :colspan => 2
122
+ assert_equal '<tr class="ui-collection-result"><td class="title" colspan="2">Hello World</td></tr>', @row.html
123
+ end
124
+
125
+ def test_should_not_skip_missing_cells_if_colspan_doesnt_replace_missing_cells
126
+ @row.builder.title 'Hello World'
127
+ assert_equal '<tr class="ui-collection-result"><td class="title">Hello World</td><td class="author_name ui-state-empty"></td></tr>', @row.html
128
+ end
129
+ end
130
+
131
+ class BodyRowAlternatingTest < Test::Unit::TestCase
132
+ def setup
133
+ table = TableHelper::CollectionTable.new([])
134
+ @header = table.header
135
+
136
+ @row = TableHelper::BodyRow.new(Object.new, @header)
137
+ @row.alternate = true
138
+ end
139
+
140
+ def test_should_add_alternate_class
141
+ assert_equal '<tr class="ui-collection-result ui-state-alternate"></tr>', @row.html
142
+ end
143
+
144
+ def test_should_use_custom_altenrate_class_if_specified
145
+ original_alternate_class = TableHelper::BodyRow.alternate_class
146
+ TableHelper::BodyRow.alternate_class = 'ui-row-alternate'
147
+
148
+ row = TableHelper::BodyRow.new(Object.new, @header)
149
+ row.alternate = true
150
+
151
+ assert_equal '<tr class="ui-collection-result ui-row-alternate"></tr>', @row.html
152
+ ensure
153
+ TableHelper::BodyRow.alternate_class = original_alternate_class
154
+ end
155
+ end
@@ -0,0 +1,299 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class BodyByDefaultTest < Test::Unit::TestCase
4
+ def setup
5
+ @table = TableHelper::CollectionTable.new([])
6
+ @body = TableHelper::Body.new(@table)
7
+ end
8
+
9
+ def test_should_have_a_table
10
+ assert_equal @table, @body.table
11
+ end
12
+
13
+ def test_should_not_alternate
14
+ assert_nil @body.alternate
15
+ end
16
+
17
+ def test_should_have_an_empty_caption
18
+ assert_equal 'No matches found.', @body.empty_caption
19
+ end
20
+ end
21
+
22
+ class BodyTest < Test::Unit::TestCase
23
+ def setup
24
+ @table = TableHelper::CollectionTable.new([])
25
+ @body = TableHelper::Body.new(@table)
26
+ end
27
+
28
+ def test_should_raise_exception_if_invalid_alternate_specified
29
+ assert_raise(ArgumentError) {@body.alternate = :invalid}
30
+ end
31
+
32
+ def test_should_not_raise_exception_for_odd_alternate
33
+ assert_nothing_raised {@body.alternate = :odd}
34
+ assert_equal :odd, @body.alternate
35
+ end
36
+
37
+ def test_should_not_raise_exception_for_even_alternate
38
+ assert_nothing_raised {@body.alternate = :even}
39
+ assert_equal :even, @body.alternate
40
+ end
41
+ end
42
+
43
+ class BodyWithEmptyCollectionTest < Test::Unit::TestCase
44
+ def setup
45
+ @table = TableHelper::CollectionTable.new([])
46
+ @body = TableHelper::Body.new(@table)
47
+ end
48
+
49
+ def test_should_have_no_content_if_no_empty_caption
50
+ @body.empty_caption = nil
51
+ assert_html_equal '<tbody></tbody>', @body.html
52
+ end
53
+
54
+ def test_should_show_content_if_empty_caption
55
+ expected = <<-end_str
56
+ <tbody>
57
+ <tr class="ui-collection-empty">
58
+ <td>No matches found.</td>
59
+ </tr>
60
+ </tbody>
61
+ end_str
62
+ assert_html_equal expected, @body.html
63
+ end
64
+
65
+ def test_should_use_custom_empty_caption_class_if_specified
66
+ original_empty_caption_class = TableHelper::Body.empty_caption_class
67
+ TableHelper::Body.empty_caption_class = 'ui-collection-empty_caption'
68
+
69
+ expected = <<-end_str
70
+ <tbody>
71
+ <tr class="ui-collection-empty_caption">
72
+ <td>No matches found.</td>
73
+ </tr>
74
+ </tbody>
75
+ end_str
76
+ assert_html_equal expected, @body.html
77
+ ensure
78
+ TableHelper::Body.empty_caption_class = original_empty_caption_class
79
+ end
80
+
81
+ def test_should_set_colspan_if_header_has_multiple_columns
82
+ @table.header :title, :author_name
83
+
84
+ expected = <<-end_str
85
+ <tbody>
86
+ <tr class="ui-collection-empty">
87
+ <td colspan="2">No matches found.</td>
88
+ </tr>
89
+ </tbody>
90
+ end_str
91
+ assert_html_equal expected, @body.html
92
+ end
93
+ end
94
+
95
+ class BodyWithCollectionTest < Test::Unit::TestCase
96
+ class Post
97
+ attr_accessor :title
98
+
99
+ def initialize(title)
100
+ @title = title
101
+ end
102
+ end
103
+
104
+ def setup
105
+ @collection = [Post.new('first'), Post.new('second'), Post.new('last')]
106
+ @table = TableHelper::CollectionTable.new(@collection)
107
+ @table.header :title
108
+
109
+ @body = TableHelper::Body.new(@table)
110
+ end
111
+
112
+ def test_should_build_row_using_object_location_for_default_index
113
+ build_post = nil
114
+ index = nil
115
+ @body.each {|row, build_post, index|}
116
+
117
+ @collection.each do |post|
118
+ html = @body.build_row(post)
119
+ assert_equal post, build_post
120
+ assert_equal @collection.index(post), index
121
+
122
+ expected = <<-end_str
123
+ <tr class="post ui-collection-result">
124
+ <td class="post-title">#{post.title}</td>
125
+ </tr>
126
+ end_str
127
+ assert_html_equal expected, html
128
+ end
129
+ end
130
+
131
+ def test_should_build_row_using_custom_value_for_index
132
+ post = nil
133
+ index = nil
134
+ @body.each {|row, post, index|}
135
+
136
+ html = @body.build_row(@collection.first, 1)
137
+ assert_equal @collection.first, post
138
+ assert_equal 1, index
139
+
140
+ expected = <<-end_str
141
+ <tr class="post ui-collection-result">
142
+ <td class="post-title">first</td>
143
+ </tr>
144
+ end_str
145
+ assert_html_equal expected, html
146
+ end
147
+
148
+ def test_should_build_row_with_missing_cells
149
+ header = @table.header :author_name
150
+
151
+ expected = <<-end_str
152
+ <tr class="post ui-collection-result">
153
+ <td class="post-title">first</td>
154
+ <td class="post-author_name ui-state-empty"></td>
155
+ </tr>
156
+ end_str
157
+ assert_html_equal expected, @body.build_row(@collection.first)
158
+ end
159
+
160
+ def test_should_build_html
161
+ expected = <<-end_str
162
+ <tbody>
163
+ <tr class="post ui-collection-result">
164
+ <td class="post-title">first</td>
165
+ </tr>
166
+ <tr class="post ui-collection-result">
167
+ <td class="post-title">second</td>
168
+ </tr>
169
+ <tr class="post ui-collection-result">
170
+ <td class="post-title">last</td>
171
+ </tr>
172
+ </tbody>
173
+ end_str
174
+ assert_html_equal expected, @body.html
175
+ end
176
+
177
+ def test_should_include_custom_attributes_in_body_tag
178
+ @body[:class] = 'pretty'
179
+
180
+ expected = <<-end_str
181
+ <tbody class="pretty">
182
+ <tr class="post ui-collection-result">
183
+ <td class="post-title">first</td>
184
+ </tr>
185
+ <tr class="post ui-collection-result">
186
+ <td class="post-title">second</td>
187
+ </tr>
188
+ <tr class="post ui-collection-result">
189
+ <td class="post-title">last</td>
190
+ </tr>
191
+ </tbody>
192
+ end_str
193
+ assert_html_equal expected, @body.html
194
+ end
195
+ end
196
+
197
+ class BodyWithCustomBuilderTest < Test::Unit::TestCase
198
+ def setup
199
+ @collection = [Object.new, Object.new, Object.new]
200
+ @table = TableHelper::CollectionTable.new(@collection)
201
+ @table.header :index
202
+
203
+ @body = TableHelper::Body.new(@table)
204
+ @body.each do |row, object, index|
205
+ row.index index.to_s
206
+ end
207
+ end
208
+
209
+ def test_should_use_custom_builder
210
+ expected = <<-end_str
211
+ <tbody>
212
+ <tr class="object ui-collection-result">
213
+ <td class="object-index">0</td>
214
+ </tr>
215
+ <tr class="object ui-collection-result">
216
+ <td class="object-index">1</td>
217
+ </tr>
218
+ <tr class="object ui-collection-result">
219
+ <td class="object-index">2</td>
220
+ </tr>
221
+ </tbody>
222
+ end_str
223
+ assert_html_equal expected, @body.html
224
+ end
225
+ end
226
+
227
+ class BodyWithAlternatingEvenRowsTest < Test::Unit::TestCase
228
+ class Post
229
+ attr_accessor :title
230
+
231
+ def initialize(title)
232
+ @title = title
233
+ end
234
+ end
235
+
236
+ def setup
237
+ @collection = [Post.new('first'), Post.new('second'), Post.new('last')]
238
+ table = TableHelper::CollectionTable.new(@collection)
239
+ table.header :title
240
+
241
+ @body = TableHelper::Body.new(table)
242
+ @body.alternate = :even
243
+ end
244
+
245
+ def test_should_alternate_even_row
246
+ expected = <<-end_str
247
+ <tr class="post ui-collection-result ui-state-alternate">
248
+ <td class="post-title">first</td>
249
+ </tr>
250
+ end_str
251
+ assert_html_equal expected, @body.build_row(@collection.first)
252
+ end
253
+
254
+ def test_should_not_alternate_odd_row
255
+ expected = <<-end_str
256
+ <tr class="post ui-collection-result">
257
+ <td class="post-title">second</td>
258
+ </tr>
259
+ end_str
260
+ assert_html_equal expected, @body.build_row(@collection[1])
261
+ end
262
+ end
263
+
264
+ class BodyWithAlternatingOddRowsTest < Test::Unit::TestCase
265
+ class Post
266
+ attr_accessor :title
267
+
268
+ def initialize(title)
269
+ @title = title
270
+ end
271
+ end
272
+
273
+ def setup
274
+ @collection = [Post.new('first'), Post.new('second'), Post.new('last')]
275
+ table = TableHelper::CollectionTable.new(@collection)
276
+ table.header :title
277
+
278
+ @body = TableHelper::Body.new(table)
279
+ @body.alternate = :odd
280
+ end
281
+
282
+ def test_should_alternate_odd_row
283
+ expected = <<-end_str
284
+ <tr class="post ui-collection-result ui-state-alternate">
285
+ <td class="post-title">second</td>
286
+ </tr>
287
+ end_str
288
+ assert_html_equal expected, @body.build_row(@collection[1])
289
+ end
290
+
291
+ def test_should_not_alternate_even_row
292
+ expected = <<-end_str
293
+ <tr class="post ui-collection-result">
294
+ <td class="post-title">first</td>
295
+ </tr>
296
+ end_str
297
+ assert_html_equal expected, @body.build_row(@collection.first)
298
+ end
299
+ end