table_helper 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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