fluid_table 3.2.0

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