fluid_table 3.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.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2010-2012 Decisiv, Inc.
2
+ Copyright (c) 2009 Brennan Dunn
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # FluidTable
2
+
3
+ FluidTable is a paginated data table helper for rails.
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'bundler'
2
+ require 'rake/testtask'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ desc 'Test fluid_table'
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.libs << 'lib' << 'test'
9
+ t.pattern = 'test/**/*_test.rb'
10
+ t.verbose = true
11
+ end
12
+
13
+ task :default => [:test]
@@ -0,0 +1,19 @@
1
+ require 'rails'
2
+ require 'action_controller'
3
+ require 'active_support/core_ext/proc'
4
+
5
+ require 'fluid_table/class_methods'
6
+ require 'fluid_table/instance_methods'
7
+ require 'fluid_table/column'
8
+ require 'fluid_table/context'
9
+ require 'fluid_table/version'
10
+
11
+ class FluidTable
12
+ include ActionView::Helpers::TagHelper
13
+
14
+ class_attribute :row_options, :class_columns, :table_options
15
+ attr_accessor :view, :records, :cloned_columns, :render_options, :cache_rendered_rows
16
+
17
+ extend ClassMethods
18
+ include InstanceMethods
19
+ end
@@ -0,0 +1,22 @@
1
+ class FluidTable
2
+ module ClassMethods
3
+
4
+ def define_column(*args, &proc)
5
+ options = [Hash,Proc].include?(args.last.class) ? args.pop : {}
6
+ identity, alt_name = *args
7
+ Column.new(self, identity, alt_name, options, &proc).tap do |column|
8
+ (self.class_columns ||= Array.new).push(column)
9
+ column.default_position = class_columns.index(column)
10
+ end
11
+ end
12
+
13
+ def render(view,records = nil)
14
+ new(view).render(records)
15
+ end
16
+
17
+ def valid?
18
+ !class_columns.empty?
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,200 @@
1
+ class FluidTable
2
+ class Column
3
+ include Comparable
4
+ include ActionView::Helpers::TagHelper
5
+
6
+ attr_accessor :table, :identity, :alt_name, :options, :html_options, :proc, :forced_options # basic init
7
+ attr_accessor :is_visible, :default_position, :configured_position # overrides
8
+ attr_accessor :table_instance # duped context
9
+
10
+ DefaultOptions = { :default => true, :cacheable => true }
11
+
12
+ def initialize(table, identity, alt_name = nil, options = {}, &proc)
13
+ self.table = table
14
+ self.identity = identity
15
+ self.alt_name = alt_name
16
+ self.options = options.reverse_merge(DefaultOptions)
17
+ self.html_options = options.delete(:html) || {}
18
+ self.proc = proc
19
+ self.forced_options = {}
20
+ end
21
+
22
+ def <=>(other_column)
23
+ if position == other_column.position
24
+ return -1 if positioned? && !other_column.positioned?
25
+ return 1 if !positioned? && other_column.positioned?
26
+ end
27
+ position <=> other_column.position
28
+ end
29
+
30
+ def current
31
+ table_instance.view.current
32
+ end
33
+
34
+ def position
35
+ configured_position || default_position
36
+ end
37
+
38
+ def cacheable?
39
+ options[:cacheable]
40
+ end
41
+
42
+ def visible?
43
+ return @visible unless @visible.nil?
44
+ @visible = options[:visible] ? options[:visible].bind(table_instance.view).call : true
45
+ end
46
+
47
+ def config_filter_key
48
+ :"filter_for_#{sort.field}"
49
+ end
50
+
51
+ def filter
52
+ options[:filter]
53
+ end
54
+
55
+ def filter_distinct?
56
+ filter == :distinct
57
+ end
58
+
59
+ def filterable?
60
+ filter.present?
61
+ end
62
+
63
+ def filter_data
64
+ return [] unless filterable?
65
+ if filter_distinct?
66
+ filter_data_for_distinct
67
+ else
68
+ filter.first.is_a?(Array) ? filter.map(&:first) : filter
69
+ end
70
+ end
71
+
72
+ def filter_data_for_current
73
+ return [] unless filterable?
74
+ data = filter_data
75
+ values = current_filter_values
76
+ data.sort.inject(ActiveSupport::OrderedHash.new) do |hsh, key|
77
+ checked = values.include?(key)
78
+ value = if !filter_distinct? && filter.first.is_a?(Array)
79
+ filter.detect{ |arr| arr.first == key }.try(:last)
80
+ else
81
+ checked ? 1 : 0
82
+ end
83
+ hsh[key] = [checked ? 1 : 0, key].join('|')
84
+ hsh
85
+ end
86
+ end
87
+
88
+ def filter_condition
89
+ filter_values = current_filter_values.dup
90
+ conditions = []
91
+ if filter_values.delete('none...')
92
+ conditions << "#{sort.table}.#{sort.field} IS NULL"
93
+ end
94
+ unless filter_values.empty?
95
+ conditions << "#{sort.table}.#{sort.field} IN (?)"
96
+ end
97
+ return nil if conditions.empty?
98
+ condition = "(#{conditions.join(' OR ')})"
99
+ unless filter_values.empty?
100
+ condition = [condition, filter_values]
101
+ end
102
+ condition
103
+ end
104
+
105
+ def positioned?
106
+ !!configured_position
107
+ end
108
+
109
+ def name
110
+ alt_name || identity.to_s.humanize
111
+ end
112
+
113
+ def sort_on
114
+ options[:sort_on]
115
+ end
116
+
117
+ def sort
118
+ return nil unless sort_on
119
+ @sort ||= begin
120
+ stable, sfield = sort_on.split('.')
121
+ Struct.new(:table, :field).new(stable, sfield)
122
+ end
123
+ end
124
+
125
+ def html(scope)
126
+ attributes = html_options.is_a?(Proc) ? html_options.call(Context.new(table_instance, scope)) : html_options
127
+ merge_options = forced_options.is_a?(Proc) ? forced_options.call(Context.new(table_instance, scope)) : forced_options
128
+ content_tag(:td, interior_content(scope).to_s.html_safe, attributes.merge(merge_options))
129
+ end
130
+
131
+ def display?
132
+ is_visible.nil? ? options[:default] : is_visible
133
+ end
134
+
135
+ def reset!
136
+ self.is_visible = self.configured_position = nil
137
+ end
138
+
139
+
140
+ private
141
+
142
+ def current_user_filters
143
+ table_instance.current_user_filters
144
+ end
145
+
146
+ def current_filter_values
147
+ return [] unless filterable?
148
+ value = current_user_filters[config_filter_key] || ''
149
+ value.split(',').map(&:strip)
150
+ end
151
+
152
+ # FIXME this absolutely does not belong here
153
+ def filter_data_for_distinct
154
+ base_finders = {:select => sort.field, :group => sort.field}
155
+ set = case table.klass_name
156
+ when 'FleetRecentEstimates', 'FleetRequestedEstimates'
157
+ case sort_on
158
+ when 'vehicles.fleet_info'
159
+ current.dealer.fleet_vehicles.all(base_finders)
160
+ when 'estimates.fleet_status'
161
+ current.dealer.fleet_estimates.all(base_finders)
162
+ when 'dealer_info.dealer_name'
163
+ Dealer.find_by_sql(%|
164
+ SELECT [dealer_name]
165
+ FROM [dealer_info]
166
+ WHERE [dealer_id] IN (SELECT [dealer_id] FROM [estimates] WHERE [vehicle_owner] = #{Dealer.connection.quote(current.dealer.id)})
167
+ ORDER BY [dealer_name] ASC|.squish)
168
+ end
169
+ when 'SvcRecentEstimates', 'SvcAssignedEstimates'
170
+ case sort_on
171
+ when 'estimates.assigned_to', 'estimates.customer_company_name'
172
+ table_instance.scoped_records.all(base_finders)
173
+ end
174
+ end || []
175
+ fields = set.map { |o| o.send(sort.field) } + current_filter_values
176
+ fields.uniq.reject { |o| o.blank? }.sort
177
+ end
178
+
179
+ def interior_content(scope)
180
+ if proc && table_instance
181
+ call_by_arity(scope)
182
+ elsif scope.respond_to?(identity)
183
+ scope.send identity
184
+ else
185
+ ''
186
+ end
187
+ end
188
+
189
+ def call_by_arity(scope)
190
+ call_arguments = case proc.arity
191
+ when 1 then [scope]
192
+ when 2 then [scope, table_instance]
193
+ when 3 then [scope, table_instance, self]
194
+ else []
195
+ end
196
+ proc.bind(table_instance.view).call(*call_arguments)
197
+ end
198
+
199
+ end
200
+ end
@@ -0,0 +1,28 @@
1
+ class FluidTable
2
+ class Context < Struct.new(:table,:record)
3
+
4
+ def view
5
+ table.view if table
6
+ end
7
+
8
+ def respond_to?(method)
9
+ view && view.respond_to?(method)
10
+ end
11
+
12
+
13
+ private
14
+
15
+ def method_missing(method, *args)
16
+ if table.view
17
+ if block_given?
18
+ table.view.send(method, *args) { |*block_args| yield(*block_args) }
19
+ else
20
+ table.view.send(method, *args)
21
+ end
22
+ else
23
+ block_given? ? super { |*block_args| yield(*block_args) } : super
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,86 @@
1
+ class FluidTable
2
+ module InstanceMethods
3
+
4
+ def initialize(view,render_options={})
5
+ self.view = view
6
+ load_cloned_columns
7
+ self.render_options = render_options
8
+ load_customizations
9
+ end
10
+
11
+ def displayed_columns
12
+ cloned_columns.select(&:display?).sort
13
+ end
14
+
15
+ def hidden_columns
16
+ cloned_columns - displayed_columns
17
+ end
18
+
19
+ # Overwrite this method in your implementation to add table headers, etc.
20
+ def render(records = find_records)
21
+ self.records = records
22
+ rendered_table = content_tag :table, content_tag(:tbody,render_table_body), table_options || {}
23
+ render_header + rendered_table + render_footer
24
+ end
25
+
26
+ # Stub methods
27
+ def find_records ; [] ; end
28
+ def customize_column(column) ; column ; end
29
+ def render_header ; ActiveSupport::SafeBuffer.new ; end
30
+ def render_footer ; ActiveSupport::SafeBuffer.new ; end
31
+ def if_array_empty?
32
+ content_tag :td, %|There's nothing here|, :colspan => displayed_columns.size
33
+ end
34
+
35
+ private
36
+
37
+ def load_cloned_columns
38
+ self.cloned_columns = class_columns.map { |cc| cloned = cc.dup ; cloned.table_instance = self ; cloned }
39
+ cloned_columns.reject! { |c| !c.visible? }
40
+ cloned_columns.sort!
41
+ end
42
+
43
+ def load_customizations
44
+ cloned_columns.each { |c| customize_column(c) }
45
+ end
46
+
47
+ def render_table_body
48
+ return render_empty_table if records.blank?
49
+ records.map do |record|
50
+ content_tag(:tr,render_row(record),render_tr_options(record))
51
+ end.join.html_safe
52
+ end
53
+
54
+ def render_empty_table
55
+ content_tag(:tr,if_array_empty?)
56
+ end
57
+
58
+ def render_row(record)
59
+ # The goal is to have one cache hit for the row, per record. The challenge is that different users can have some
60
+ # columns on/off in each row. The solution is to use a hash for the entire row. If the users displayed_columns has
61
+ # all the columns from the row cache hash (write_col) then it will not re-write the cache with more data.
62
+ if cache_rendered_rows
63
+ cache_key = ActiveSupport::Cache.expand_cache_key ['views',configurator_id,record]
64
+ row_cache = Rails.cache.fetch(cache_key) { HashWithIndifferentAccess.new }
65
+ write_col = displayed_columns.select(&:cacheable?).map(&:identity).any? { |id| !row_cache.keys.include?(id) }
66
+ row_cache = row_cache.dup if write_col
67
+ end
68
+ # Rendering the row like normal FluidTable but building up the hash cache if needed.
69
+ rendered_row = displayed_columns.map do |c|
70
+ cache_rendered_rows && c.cacheable? ? (row_cache[c.identity] ||= c.html(record)) : c.html(record)
71
+ end.join
72
+ # Write the cache back if caching and any cols were not in the orig cache. Return rendered row.
73
+ Rails.cache.write(cache_key,row_cache) if cache_rendered_rows && write_col
74
+ return rendered_row.html_safe
75
+ end
76
+
77
+ def render_tr_options(record)
78
+ case opt = row_options
79
+ when Proc then opt.call(Context.new(self,record))
80
+ when Hash then opt
81
+ else {}
82
+ end
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,3 @@
1
+ class FluidTable
2
+ VERSION = '3.2.0'
3
+ end
@@ -0,0 +1,14 @@
1
+ class User < Struct.new(:id, :name)
2
+
3
+ ALL = [
4
+ [1, 'Brennan Dunn'],
5
+ [2, 'Ken Collins'],
6
+ [3, 'Brian Knox'],
7
+ [4, 'Nagu Parasu']
8
+ ].map { |arr| new(*arr) }
9
+
10
+ def self.all_users
11
+ ALL
12
+ end
13
+
14
+ end
@@ -0,0 +1 @@
1
+ <%= UsersTable.render(self,User.all_users) %>
@@ -0,0 +1,27 @@
1
+ class UsersTable < FluidTable
2
+
3
+ self.table_options = { :class => 'dataTable' }
4
+
5
+ define_column :id, 'User #'
6
+
7
+ define_column :name do |user|
8
+ user.name.upcase
9
+ end
10
+
11
+ define_column :age, :html => Proc.new { |c| { :class => "table_id_#{c.table.object_id}" } }
12
+ define_column :gender
13
+
14
+ UsersTable.define_column :displayed_column
15
+ UsersTable.define_column :hidden_column, :default => false
16
+
17
+ # overwrites
18
+
19
+ def render_header
20
+ content_tag(:div, 'This is the header')
21
+ end
22
+
23
+ def render_footer
24
+ content_tag(:div, 'This is the footer')
25
+ end
26
+
27
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'bundler/setup'
4
+ Bundler.require(:default)
5
+ require 'minitest/autorun'
6
+ require 'mocha'
7
+ require 'nokogiri'
8
+
9
+ require 'support/user'
10
+ require 'support/users_table'
@@ -0,0 +1,92 @@
1
+ require 'test_helper'
2
+
3
+ class TestTable < FluidTable
4
+ define_column :id, 'User #'
5
+ define_column :name, 'User Name'
6
+ define_column(:google){ link_to_google }
7
+ end
8
+
9
+ describe FluidTable::Column do
10
+
11
+ def self.test_order
12
+ :alpha
13
+ end
14
+
15
+ describe 'Constructing column objects' do
16
+
17
+ before do
18
+ @test_table = TestTable.new(nil,nil)
19
+ @id_column = @test_table.class_columns.detect { |c| c.identity == :id }
20
+ @name_column = @test_table.class_columns.detect { |c| c.identity == :name }
21
+ @google_column = @test_table.class_columns.detect { |c| c.identity == :google }
22
+ end
23
+
24
+ it "should respond to it's identity" do
25
+ assert_equal :id, @id_column.identity
26
+ end
27
+
28
+ it 'should attempt to create a humanized string from the identity if an alt_name is not present' do
29
+ assert_equal 'User #', @id_column.name
30
+ assert_equal 'User Name', @name_column.name
31
+ end
32
+
33
+ it "should be able to parse its sort on table and field" do
34
+ @name_column.options[:sort_on] = 'foo.bar'
35
+ assert @name_column.sort
36
+ assert_equal 'foo', @name_column.sort.table
37
+ assert_equal 'bar', @name_column.sort.field
38
+ end
39
+
40
+ describe "Outputting a column's HTML" do
41
+
42
+ it 'should wrap output within a table cell' do
43
+ assert_equal_html '<td></td>', @id_column.html(mock)
44
+ end
45
+
46
+ # FIXME - I suck and my tests are order-dependent?
47
+ it 'should attempt to get cell content from scope object when proc is not available' do
48
+ assert_equal_html '<td>John Doe</td>', @name_column.html(mock({ :name => 'John Doe' }))
49
+ end
50
+
51
+ it "should call a column's proc (if exists) against the scope of the current view" do
52
+ view = mock.tap { |m| m.stubs :link_to_google => 'http://google.com' }
53
+ table = TestTable.new(view)
54
+ google_column = table.cloned_columns.detect { |c| c.identity == :google }
55
+ assert_equal_html '<td>http://google.com</td>', google_column.html(User.all_users.first)
56
+ end
57
+
58
+ it 'should be able to set custom HTML attributes when supplied a HASH' do
59
+ @name_column.html_options = { :class => 'foo' }
60
+ assert_match /class="foo"/, @name_column.html(mock)
61
+ @name_column.html_options = { :id => 'bar' }
62
+ assert_match /id="bar"/, @name_column.html(mock)
63
+ end
64
+
65
+ it 'should be able to set custom HTML attributes when supplied a PROC' do
66
+ @name_column.html_options = Proc.new { |context| { :id => "name_#{context.record.id}" } }
67
+ assert_match /id="name_5"/, @name_column.html(mock(:id => '5'))
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+
74
+ describe 'Columns within a FluidTable' do
75
+
76
+ it 'should not be valid? unless at least one column is defined' do
77
+ assert ! TestTable.class_columns.empty?
78
+ assert TestTable.valid?
79
+ TestTable.stubs(:class_columns).returns([])
80
+ assert ! TestTable.valid?
81
+ end
82
+
83
+ end
84
+
85
+
86
+ private
87
+
88
+ def assert_equal_html(expected, actual)
89
+ assert_equal expected.strip.gsub(/\n\s*/, ''), actual.strip.gsub(/\n\s*/, '')
90
+ end
91
+
92
+ end
@@ -0,0 +1,80 @@
1
+ require 'test_helper'
2
+
3
+ describe "Fluid table configuration" do
4
+
5
+ let(:table) { UsersTable.new(nil, nil) }
6
+
7
+ it 'should have an array of cloned class_columns for each table instance' do
8
+ table.cloned_columns.map(&:object_id).wont_equal UsersTable.class_columns.map(&:object_id)
9
+ end
10
+
11
+ it 'should not affect a class column when modifying an instance column' do
12
+ instance_column = get_column(:age)
13
+ transform_column(instance_column, false)
14
+ assert_equal false, instance_column.display?
15
+ assert_equal true, get_table_column(:age).display?
16
+ end
17
+
18
+ describe "Determining a column's default visibility" do
19
+
20
+ let(:displayed_column) { get_column(:displayed_column) }
21
+ let(:hidden_column) { get_column(:hidden_column) }
22
+
23
+ it 'should default to TRUE for a column display' do
24
+ assert displayed_column.display?
25
+ end
26
+
27
+ it "should allow for a column's definition to default display to FALSE" do
28
+ assert !hidden_column.display?
29
+ end
30
+
31
+ it 'should include correct columns when returning from #displayed_columns or #hidden_columns' do
32
+ assert table.displayed_columns.include?(displayed_column)
33
+ assert !table.displayed_columns.include?(hidden_column)
34
+ assert table.hidden_columns.include?(hidden_column)
35
+ assert !table.hidden_columns.include?(displayed_column)
36
+ end
37
+
38
+ end
39
+
40
+ describe 'When overriding position or visibility' do
41
+
42
+ it 'should call #customize_column with each column object on table instantiation' do
43
+ table.class_columns.each do |column|
44
+ table.expects(:customize_column).with(column)
45
+ end
46
+ table.send :load_customizations
47
+ end
48
+
49
+ it 'should override default column visibility settings when instructed' do
50
+ column = get_column(:age)
51
+ assert table.displayed_columns.include?(column)
52
+ transform_column(get_column(:age), false)
53
+ table.send :load_customizations
54
+ assert ! table.displayed_columns.include?(column)
55
+ end
56
+
57
+ it "should be able to set a column's position which trumps the default" do
58
+ age_column = get_column(:age)
59
+ transform_column(age_column, true, 0)
60
+ assert_equal age_column.identity, table.displayed_columns.first.identity
61
+ end
62
+
63
+ end
64
+
65
+ private
66
+
67
+ def get_column(identity)
68
+ table.cloned_columns.detect { |c| c.identity == identity }
69
+ end
70
+
71
+ def get_table_column(identity)
72
+ table.class_columns.detect { |c| c.identity == identity }
73
+ end
74
+
75
+ def transform_column(column, is_visible = true, position = nil)
76
+ column.is_visible = is_visible
77
+ column.configured_position = position
78
+ end
79
+
80
+ end
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+
3
+ describe FluidTable do
4
+
5
+ describe "#render" do
6
+
7
+ let(:text) { UsersTable.render(stub(:view), User.all_users) }
8
+ let(:html) { Nokogiri::HTML::DocumentFragment.parse(text) }
9
+
10
+ it "renders the header text in a header div" do
11
+ html.xpath(".//div").first.text.must_equal 'This is the header'
12
+ end
13
+
14
+ it "renders the footer text in a footer div" do
15
+ html.xpath(".//div").last.text.must_equal 'This is the footer'
16
+ end
17
+
18
+ it "renders a row for each user" do
19
+ html.xpath(".//tbody/tr").length.must_equal User.all_users.length
20
+ end
21
+
22
+ it "render 'Nothing here' if there are no users" do
23
+ User.stubs(:all_users).returns([])
24
+ html.xpath(".//td").map(&:text).must_equal ["There's nothing here"]
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,45 @@
1
+ require 'test_helper'
2
+
3
+ describe "FluidTable render" do
4
+
5
+ describe 'A FluidTable object' do
6
+
7
+ it 'should have the option of setting attributes to the generated table (as opposed to overwriting #render)' do
8
+ UsersTable.table_options = { :class => 'dataTable' }
9
+ assert_match %r{table class="dataTable"}, render_users
10
+ end
11
+
12
+ describe 'Ordering of class_columns' do
13
+
14
+ it 'should be defaultly positioned in the order of definition' do
15
+ assert_equal 0, UsersTable.class_columns.first.default_position
16
+ assert_equal UsersTable.class_columns.size-1, UsersTable.class_columns.last.default_position
17
+ end
18
+
19
+ it 'should have the #sort order be identical to the default positioning when there are no custom positions' do
20
+ assert_equal UsersTable.class_columns, UsersTable.class_columns.sort
21
+ end
22
+
23
+ end
24
+
25
+ describe 'Supplying row (<tr>) options' do
26
+
27
+ it 'should output standard hash options' do
28
+ UsersTable.row_options = { :class => 'foo' }
29
+ assert_match %r{tr class="foo"}, render_users
30
+ end
31
+
32
+ it 'should yield a context object when given a Proc' do
33
+ UsersTable.row_options = Proc.new { |context| { :id => "user_#{context.record.id}" } }
34
+ assert_match %r{tr id="user_#{User::ALL.size}"}, render_users
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
41
+ def render_users
42
+ UsersTable.render(mock, User::ALL)
43
+ end
44
+
45
+ end
metadata ADDED
@@ -0,0 +1,174 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluid_table
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease:
6
+ segments:
7
+ - 3
8
+ - 2
9
+ - 0
10
+ version: 3.2.0
11
+ platform: ruby
12
+ authors:
13
+ - Brennan Dunn
14
+ - Ken Collins
15
+ - Donald Ball
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2012-03-13 00:00:00 Z
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: rails
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ hash: 15
31
+ segments:
32
+ - 3
33
+ - 2
34
+ - 0
35
+ version: 3.2.0
36
+ type: :runtime
37
+ version_requirements: *id001
38
+ - !ruby/object:Gem::Dependency
39
+ name: rake
40
+ prerelease: false
41
+ requirement: &id002 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ hash: 63
47
+ segments:
48
+ - 0
49
+ - 9
50
+ - 2
51
+ version: 0.9.2
52
+ type: :development
53
+ version_requirements: *id002
54
+ - !ruby/object:Gem::Dependency
55
+ name: minitest
56
+ prerelease: false
57
+ requirement: &id003 !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ hash: 45
63
+ segments:
64
+ - 2
65
+ - 8
66
+ - 1
67
+ version: 2.8.1
68
+ type: :development
69
+ version_requirements: *id003
70
+ - !ruby/object:Gem::Dependency
71
+ name: mocha
72
+ prerelease: false
73
+ requirement: &id004 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ~>
77
+ - !ruby/object:Gem::Version
78
+ hash: 53
79
+ segments:
80
+ - 0
81
+ - 10
82
+ - 1
83
+ version: 0.10.1
84
+ type: :development
85
+ version_requirements: *id004
86
+ - !ruby/object:Gem::Dependency
87
+ name: nokogiri
88
+ prerelease: false
89
+ requirement: &id005 !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ~>
93
+ - !ruby/object:Gem::Version
94
+ hash: 3
95
+ segments:
96
+ - 1
97
+ - 5
98
+ - 0
99
+ version: 1.5.0
100
+ type: :development
101
+ version_requirements: *id005
102
+ description: Paginated data tables for rails
103
+ email:
104
+ - me@brennandunn.com
105
+ - ken@metaskills.net
106
+ - donald.ball@gmail.com
107
+ executables: []
108
+
109
+ extensions: []
110
+
111
+ extra_rdoc_files: []
112
+
113
+ files:
114
+ - .gitignore
115
+ - Gemfile
116
+ - LICENSE
117
+ - README.md
118
+ - Rakefile
119
+ - lib/fluid_table.rb
120
+ - lib/fluid_table/class_methods.rb
121
+ - lib/fluid_table/column.rb
122
+ - lib/fluid_table/context.rb
123
+ - lib/fluid_table/instance_methods.rb
124
+ - lib/fluid_table/version.rb
125
+ - test/support/user.rb
126
+ - test/support/users.html.erb
127
+ - test/support/users_table.rb
128
+ - test/test_helper.rb
129
+ - test/units/column_test.rb
130
+ - test/units/configuration_test.rb
131
+ - test/units/render_test.rb
132
+ - test/units/table_test.rb
133
+ homepage: http://github.com/Decisiv/fluid_table/
134
+ licenses: []
135
+
136
+ post_install_message:
137
+ rdoc_options:
138
+ - --charset=UTF-8
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ hash: 3
147
+ segments:
148
+ - 0
149
+ version: "0"
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ none: false
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ hash: 3
156
+ segments:
157
+ - 0
158
+ version: "0"
159
+ requirements: []
160
+
161
+ rubyforge_project:
162
+ rubygems_version: 1.8.17
163
+ signing_key:
164
+ specification_version: 3
165
+ summary: Paginated data tables for rails
166
+ test_files:
167
+ - test/support/user.rb
168
+ - test/support/users.html.erb
169
+ - test/support/users_table.rb
170
+ - test/test_helper.rb
171
+ - test/units/column_test.rb
172
+ - test/units/configuration_test.rb
173
+ - test/units/render_test.rb
174
+ - test/units/table_test.rb