table_helper 0.1.0 → 0.2.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.
@@ -4,27 +4,29 @@ module TableHelper
4
4
  # Represents the header of the table. In HTML, you can think of this as
5
5
  # the <tfoot> tag of the table.
6
6
  class Footer < HtmlElement
7
+ # The table this footer is a part of
8
+ attr_reader :table
9
+
7
10
  # The actual footer row
8
11
  attr_reader :row
9
12
 
10
- delegate :cell,
11
- :to => :row
12
-
13
13
  # Whether or not the footer should be hidden when the collection is
14
14
  # empty. Default is true.
15
15
  attr_accessor :hide_when_empty
16
16
 
17
- def initialize(collection) #:nodoc:
17
+ delegate :cell, :empty?, :to => :row
18
+
19
+ def initialize(table) #:nodoc:
18
20
  super()
19
21
 
20
- @collection = collection
21
- @row = Row.new
22
+ @table = table
23
+ @row = Row.new(self)
22
24
  @hide_when_empty = true
23
25
  end
24
26
 
25
27
  def html #:nodoc:
26
28
  html_options = @html_options.dup
27
- html_options[:style] = 'display: none;' if @collection.empty? && hide_when_empty
29
+ html_options[:style] = "display: none; #{html_options[:style]}".strip if table.empty? && hide_when_empty
28
30
 
29
31
  content_tag(tag_name, content, html_options)
30
32
  end
@@ -1,89 +1,45 @@
1
1
  require 'table_helper/row'
2
2
 
3
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('-', '_')
28
-
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)
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('-', '_'))
42
- end
43
- end
44
- end
45
-
46
4
  # Represents the header of the table. In HTML, you can think of this as
47
5
  # the <thead> tag of the table.
48
6
  class Header < HtmlElement
7
+ # The table this header is a part of
8
+ attr_reader :table
9
+
49
10
  # The actual header row
50
11
  attr_reader :row
51
12
 
52
- # The proxy class used externally to build the actual columns
53
- attr_reader :builder
54
-
55
13
  # Whether or not the header should be hidden when the collection is
56
14
  # empty. Default is true.
57
15
  attr_accessor :hide_when_empty
58
16
 
59
- # Creates a new header for a collection that contains objects of the
60
- # given class.
17
+ delegate :empty?, :to => :row
18
+
19
+ # Creates a new header for the given table.
61
20
  #
62
21
  # If the class is known, then the header will be pre-filled with
63
22
  # the columns defined in that class (assuming it's an ActiveRecord
64
23
  # class).
65
- def initialize(collection, klass = nil)
24
+ def initialize(table)
66
25
  super()
67
26
 
68
- @collection = collection
69
- @row = Row.new
70
- @builder = HeaderBuilder.new(self)
71
-
27
+ @table = table
28
+ @row = Row.new(self)
72
29
  @hide_when_empty = true
73
- @customized = true
74
30
 
75
31
  # If we know what class the objects in the collection are and we can
76
32
  # figure out what columns are defined in that class, then we can
77
33
  # pre-fill the header with those columns so that the user doesn't
78
34
  # 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
35
+ klass = table.klass
36
+ column(*klass.column_names.map(&:to_sym)) if klass && klass.respond_to?(:column_names)
37
+
38
+ @customized = false
84
39
  end
85
40
 
86
- # The current columns in this header, in the order in which they will be built
41
+ # The current columns in this header, in the order in which they will be
42
+ # built
87
43
  def columns
88
44
  row.cells
89
45
  end
@@ -95,61 +51,43 @@ module TableHelper
95
51
 
96
52
  # Clears all of the current columns from the header
97
53
  def clear
98
- # Remove all of the shortcut methods
99
- column_names.each {|name| builder.undef_column(name)}
100
54
  row.clear
101
55
  end
102
56
 
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)
57
+ # Creates one or more to columns in the header. This will clear any
58
+ # pre-existing columns if it is being customized for the first time after
59
+ # it was initially created.
60
+ def column(*names)
120
61
  # Clear the header row if this is being customized by the user
121
62
  unless @customized
122
63
  @customized = true
123
64
  clear
124
65
  end
125
66
 
126
- column = row.cell(name, *args)
127
- column.content_type = :header
128
- column[:scope] ||= 'col'
67
+ # Extract configuration
68
+ options = names.last.is_a?(Hash) ? names.pop : {}
69
+ content = names.last.is_a?(String) ? names.pop : nil
70
+ args = [content, options].compact
129
71
 
130
- builder.define_column(name)
72
+ names.collect! do |name|
73
+ column = row.cell(name, *args)
74
+ column.content_type = :header
75
+ column[:scope] ||= 'col'
76
+ column
77
+ end
131
78
 
132
- column
79
+ names.length == 1 ? names.first : names
133
80
  end
134
81
 
135
82
  # Creates and returns the generated html for the header
136
83
  def html
137
84
  html_options = @html_options.dup
138
- html_options[:style] = 'display: none;' if @collection.empty? && hide_when_empty
85
+ html_options[:style] = 'display: none;' if table.empty? && hide_when_empty
139
86
 
140
87
  content_tag(tag_name, content, html_options)
141
88
  end
142
89
 
143
90
  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
150
- end
151
- end
152
-
153
91
  def tag_name
154
92
  'thead'
155
93
  end
@@ -17,9 +17,7 @@ module TableHelper
17
17
  class HtmlElement
18
18
  include ActionView::Helpers::TagHelper
19
19
 
20
- delegate :[],
21
- :[]=,
22
- :to => '@html_options'
20
+ delegate :[], :[]=, :to => '@html_options'
23
21
 
24
22
  def initialize(html_options = {}) #:nodoc:
25
23
  @html_options = html_options.symbolize_keys
@@ -5,8 +5,6 @@ module TableHelper
5
5
  class RowBuilder < BlankSlate #:nodoc:
6
6
  reveal :respond_to?
7
7
 
8
- attr_reader :row
9
-
10
8
  # Creates a builder for the given row
11
9
  def initialize(row)
12
10
  @row = row
@@ -14,7 +12,7 @@ module TableHelper
14
12
 
15
13
  # Proxies all missed methods to the row
16
14
  def method_missing(*args)
17
- row.send(*args)
15
+ @row.send(*args)
18
16
  end
19
17
 
20
18
  # Defines the builder method for the given cell name. For example, if
@@ -30,9 +28,9 @@ module TableHelper
30
28
  klass.class_eval do
31
29
  define_method(method_name) do |*args|
32
30
  if args.empty?
33
- row.cells[name]
31
+ @row.cells[name]
34
32
  else
35
- row.cell(name, *args)
33
+ @row.cell(name, *args)
36
34
  end
37
35
  end
38
36
  end unless klass.method_defined?(method_name)
@@ -58,9 +56,16 @@ module TableHelper
58
56
  # The current cells in this row, in the order in which they will be built
59
57
  attr_reader :cells
60
58
 
61
- def initialize #:nodoc:
62
- super
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()
63
67
 
68
+ @parent = parent
64
69
  @cells = ActiveSupport::OrderedHash.new
65
70
  @builder = RowBuilder.new(self)
66
71
  end
@@ -70,6 +75,10 @@ module TableHelper
70
75
  def cell(name, *args)
71
76
  name = name.to_s if name
72
77
 
78
+ options = args.last.is_a?(Hash) ? args.pop : {}
79
+ options[:namespace] = table.object_name
80
+ args << options
81
+
73
82
  cell = Cell.new(name, *args)
74
83
  cells[name] = cell
75
84
  builder.define_cell(name) if name
@@ -3,33 +3,41 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
3
3
  class TableHelperTest < ActionView::TestCase
4
4
  tests TableHelper
5
5
 
6
+ class Post
7
+ end
8
+
6
9
  def test_should_build_collection_table
7
- html = collection_table(['first', 'second', 'last']) do |header, body|
8
- header.column :title
9
-
10
- body.build do |row, post_title, index|
10
+ html = collection_table(['first', 'second', 'last'], Post) do |t|
11
+ t.header :title
12
+ t.rows.each do |row, post_title, index|
11
13
  row.title post_title
12
14
  end
15
+ t.footer :total, t.collection.length
13
16
  end
14
17
 
15
18
  expected = <<-end_str
16
- <table cellpadding="0" cellspacing="0">
19
+ <table cellpadding="0" cellspacing="0" class="posts ui-collection">
17
20
  <thead>
18
21
  <tr>
19
- <th class="title" scope="col">Title</th>
22
+ <th class="post-title" scope="col">Title</th>
20
23
  </tr>
21
24
  </thead>
22
25
  <tbody>
23
- <tr class="row">
24
- <td class="title">first</td>
26
+ <tr class="post ui-collection-result">
27
+ <td class="post-title">first</td>
25
28
  </tr>
26
- <tr class="row">
27
- <td class="title">second</td>
29
+ <tr class="post ui-collection-result">
30
+ <td class="post-title">second</td>
28
31
  </tr>
29
- <tr class="row">
30
- <td class="title">last</td>
32
+ <tr class="post ui-collection-result">
33
+ <td class="post-title">last</td>
31
34
  </tr>
32
35
  </tbody>
36
+ <tfoot>
37
+ <tr>
38
+ <td class="post-total">3</td>
39
+ </tr>
40
+ </tfoot>
33
41
  </table>
34
42
  end_str
35
43
  assert_html_equal expected, html
@@ -2,8 +2,13 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
2
 
3
3
  class BodyRowByDefaultTest < Test::Unit::TestCase
4
4
  def setup
5
- header = TableHelper::Header.new([])
6
- @row = TableHelper::BodyRow.new(Object.new, header)
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
7
12
  end
8
13
 
9
14
  def test_should_not_alternate
@@ -11,7 +16,17 @@ class BodyRowByDefaultTest < Test::Unit::TestCase
11
16
  end
12
17
 
13
18
  def test_should_have_a_class_name
14
- assert_equal 'row', @row[:class]
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
15
30
  end
16
31
  end
17
32
 
@@ -23,8 +38,10 @@ class BodyRowTest < Test::Unit::TestCase
23
38
  end
24
39
 
25
40
  def setup
26
- header = TableHelper::Header.new([])
41
+ table = TableHelper::CollectionTable.new([])
42
+ header = table.header
27
43
  header.column :title
44
+
28
45
  @row = TableHelper::BodyRow.new(Post.new, header)
29
46
  end
30
47
 
@@ -34,18 +51,33 @@ class BodyRowTest < Test::Unit::TestCase
34
51
 
35
52
  def test_should_override_default_cell_content_if_cell_specified
36
53
  @row.builder.title 'Hello World'
37
- assert_equal '<tr class="row"><td class="title">Hello World</td></tr>', @row.html
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]
38
68
  end
39
69
  end
40
70
 
41
71
  class BodyRowWithNoColumnsTest < Test::Unit::TestCase
42
72
  def setup
43
- header = TableHelper::Header.new([])
73
+ table = TableHelper::CollectionTable.new([])
74
+ header = table.header
75
+
44
76
  @row = TableHelper::BodyRow.new(Object.new, header)
45
77
  end
46
78
 
47
79
  def test_should_not_build_cells
48
- assert_equal '<tr class="row"></tr>', @row.html
80
+ assert_equal '<tr class="ui-collection-result"></tr>', @row.html
49
81
  end
50
82
  end
51
83
 
@@ -57,49 +89,67 @@ class BodyRowWithCustomAttributeTest < Test::Unit::TestCase
57
89
  end
58
90
 
59
91
  def setup
60
- header = TableHelper::Header.new([])
92
+ table = TableHelper::CollectionTable.new([])
93
+ header = table.header
61
94
  header.column :title
62
95
  header.column :author_name
96
+
63
97
  @row = TableHelper::BodyRow.new(Post.new, header)
64
98
  end
65
99
 
66
100
  def test_should_use_attribute_values_as_cell_content
67
101
  @row.builder.author_name 'John Doe'
68
- assert_equal '<tr class="row"><td class="title">Default Value</td><td class="author_name">John Doe</td></tr>', @row.html
102
+ assert_equal '<tr class="ui-collection-result"><td class="title">Default Value</td><td class="author_name">John Doe</td></tr>', @row.html
69
103
  end
70
104
  end
71
105
 
72
106
  class BodyRowWithMissingCellsTest < Test::Unit::TestCase
73
107
  def setup
74
- header = TableHelper::Header.new([])
108
+ table = TableHelper::CollectionTable.new([])
109
+ header = table.header
75
110
  header.column :title
76
111
  header.column :author_name
112
+
77
113
  @row = TableHelper::BodyRow.new(Object.new, header)
78
114
  end
79
115
 
80
116
  def test_should_build_missing_cells_if_cells_not_specified
81
- assert_equal '<tr class="row"><td class="title empty"></td><td class="author_name empty"></td></tr>', @row.html
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
82
118
  end
83
119
 
84
120
  def test_should_skip_missing_cells_if_colspan_replaces_missing_cells
85
121
  @row.builder.title 'Hello World', :colspan => 2
86
- assert_equal '<tr class="row"><td class="title" colspan="2">Hello World</td></tr>', @row.html
122
+ assert_equal '<tr class="ui-collection-result"><td class="title" colspan="2">Hello World</td></tr>', @row.html
87
123
  end
88
124
 
89
125
  def test_should_not_skip_missing_cells_if_colspan_doesnt_replace_missing_cells
90
126
  @row.builder.title 'Hello World'
91
- assert_equal '<tr class="row"><td class="title">Hello World</td><td class="author_name empty"></td></tr>', @row.html
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
92
128
  end
93
129
  end
94
130
 
95
131
  class BodyRowAlternatingTest < Test::Unit::TestCase
96
132
  def setup
97
- header = TableHelper::Header.new([])
98
- @row = TableHelper::BodyRow.new(Object.new, header)
133
+ table = TableHelper::CollectionTable.new([])
134
+ @header = table.header
135
+
136
+ @row = TableHelper::BodyRow.new(Object.new, @header)
99
137
  @row.alternate = true
100
138
  end
101
139
 
102
140
  def test_should_add_alternate_class
103
- assert_equal '<tr class="row alternate"></tr>', @row.html
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
104
154
  end
105
155
  end