tabulatr2 0.9.4 → 0.9.6

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/Gemfile +1 -2
  4. data/README.md +155 -95
  5. data/app/assets/javascripts/tabulatr/_storage.js +41 -0
  6. data/app/assets/javascripts/tabulatr/_tabulatr.js +598 -0
  7. data/app/assets/javascripts/tabulatr/application.js +3 -553
  8. data/app/assets/stylesheets/tabulatr/application.css.scss +21 -12
  9. data/app/assets/stylesheets/tabulatr.css +1 -0
  10. data/app/views/tabulatr/_tabulatr_actual_table.html.slim +18 -18
  11. data/app/views/tabulatr/_tabulatr_filter_dialog.html.slim +1 -1
  12. data/app/views/tabulatr/_tabulatr_fuzzy_search_field.html.slim +1 -1
  13. data/app/views/tabulatr/_tabulatr_static_table.html.slim +3 -3
  14. data/app/views/tabulatr/_tabulatr_table.html.slim +17 -12
  15. data/lib/tabulatr/data/data.rb +7 -9
  16. data/lib/tabulatr/data/dsl.rb +36 -21
  17. data/lib/tabulatr/data/filtering.rb +41 -13
  18. data/lib/tabulatr/data/formatting.rb +7 -20
  19. data/lib/tabulatr/data/pagination.rb +1 -2
  20. data/lib/tabulatr/data/proxy.rb +2 -0
  21. data/lib/tabulatr/data/sorting.rb +24 -13
  22. data/lib/tabulatr/engine.rb +1 -0
  23. data/lib/tabulatr/generators/tabulatr/templates/tabulatr.yml +2 -2
  24. data/lib/tabulatr/json_builder.rb +23 -25
  25. data/lib/tabulatr/rails/action_controller.rb +4 -0
  26. data/lib/tabulatr/rails/action_view.rb +3 -2
  27. data/lib/tabulatr/renderer/checkbox.rb +3 -1
  28. data/lib/tabulatr/renderer/column.rb +47 -5
  29. data/lib/tabulatr/renderer/columns_from_block.rb +24 -6
  30. data/lib/tabulatr/renderer/renderer.rb +26 -17
  31. data/lib/tabulatr/utility/unexpected_search_result_error.rb +9 -0
  32. data/lib/tabulatr/utility/utility.rb +4 -0
  33. data/lib/tabulatr/version.rb +1 -1
  34. data/spec/dummy/app/controllers/products_controller.rb +9 -0
  35. data/spec/dummy/app/views/products/local_storage.html.slim +4 -0
  36. data/spec/dummy/app/views/products/simple_index.html.erb +1 -1
  37. data/spec/dummy/app/views/products/stupid_array.html.erb +1 -1
  38. data/spec/dummy/config/application.rb +1 -1
  39. data/spec/dummy/config/locales/tabulatr.yml +2 -2
  40. data/spec/dummy/config/routes.rb +1 -0
  41. data/spec/features/tabulatrs_spec.rb +27 -27
  42. data/spec/lib/tabulatr/data/data_spec.rb +12 -16
  43. data/spec/lib/tabulatr/data/filtering_spec.rb +48 -7
  44. data/spec/lib/tabulatr/data/formatting_spec.rb +32 -0
  45. data/spec/lib/tabulatr/data/sorting_spec.rb +81 -0
  46. data/spec/lib/tabulatr/json_builder_spec.rb +23 -9
  47. data/spec/lib/tabulatr/renderer/checkbox_spec.rb +14 -0
  48. data/spec/lib/tabulatr/renderer/renderer_spec.rb +20 -8
  49. data/tabulatr.gemspec +4 -3
  50. metadata +45 -9
  51. data/lib/tabulatr/data/column_name_builder.rb +0 -86
@@ -19,15 +19,15 @@
19
19
  / OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  / WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
- table.table.tabulatr_table.tabulatr_static_table
22
+ table class="#{table_options[:html_class]} tabulatr_static_table"
23
23
  thead
24
24
  tr
25
25
  - columns.each do |column|
26
- th data-tabulatr-column-name=column.full_name style=column.header_style
26
+ th data-tabulatr-column-name=column.full_name style=column.html_header_style
27
27
  = column.human_name
28
28
  tbody
29
29
  - records.each do |record|
30
30
  tr data-id=record.try(:id)
31
31
  - columns.each do |column|
32
- td data-tabulatr-column-name=column.full_name data-tabulatr-type=column.coltype style=column.cell_style
32
+ td data-tabulatr-column-name=column.full_name data-tabulatr-type=column.coltype style=column.html_cell_style
33
33
  = column.value_for(record, self)
@@ -23,24 +23,29 @@
23
23
  klass: klass, classname: classname, table_id: table_id,
24
24
  formatted_name: formatted_name }
25
25
 
26
- .tabulatr-table-controls-wrapper data-table-id=table_id
27
- .tabulatr-filter-menu-wrapper data-table-id=table_id
26
+ .tabulatr-table-controls-wrapper.row data-table-id=table_id
27
+ .tabulatr-filter-menu-wrapper.col-xs-3.col-sm-1 data-table-id=table_id
28
28
  = render '/tabulatr/tabulatr_filter_menu', opts
29
29
 
30
- .tabulatr-batch-actions-menu-wrapper data-table-id=table_id
30
+ .tabulatr-batch-actions-menu-wrapper.col-xs-3.col-sm-1 data-table-id=table_id
31
31
  = render '/tabulatr/tabulatr_batch_actions_menu', opts
32
32
 
33
- .tabulatr-paginator-wrapper data-table-id=table_id
34
- = render '/tabulatr/tabulatr_paginator', opts
35
-
36
- .tabulatr-info-string-wrapper data-table-id=table_id
37
- = render '/tabulatr/tabulatr_info_string', opts
38
-
39
33
  - if tabulatr_data.search?
40
- .tabulatr-fuzzy-search-field-wrapper data-table-id=table_id
34
+ .tabulatr-fuzzy-search-field-wrapper.col-xs-6.col-sm-2 data-table-id=table_id
41
35
  = render '/tabulatr/tabulatr_fuzzy_search_field', opts
42
36
 
43
- .tabulatr-filter-dialog-wrapper data-table-id=table_id
44
- = render '/tabulatr/tabulatr_filter_dialog', opts
37
+ - if table_options[:pagination_position].in?([:top, :both])
38
+ .tabulatr-paginator-wrapper.col-xs-12.col-sm-8 data-table-id=table_id
39
+ = render '/tabulatr/tabulatr_paginator', opts
40
+
41
+
42
+ .tabulatr-filter-dialog-wrapper data-table-id=table_id
43
+ = render '/tabulatr/tabulatr_filter_dialog', opts
44
+
45
+ .tabulatr-info-string-wrapper data-table-id=table_id
46
+ = render '/tabulatr/tabulatr_info_string', opts
45
47
 
46
48
  = render '/tabulatr/tabulatr_actual_table', opts
49
+ - if table_options[:pagination_position].in?([:bottom, :both])
50
+ .tabulatr-paginator-wrapper.col-xs-12 data-table-id=table_id
51
+ = render '/tabulatr/tabulatr_paginator', opts
@@ -27,9 +27,7 @@ class Tabulatr::Data
27
27
  @relation = relation
28
28
  @base = relation.respond_to?(:klass) ? relation.klass : relation
29
29
  @table_name = @base.table_name
30
- @assocs = self.class.instance_variable_get('@assocs') || HashWithIndifferentAccess.new
31
- @columns = self.class.instance_variable_get('@columns') || HashWithIndifferentAccess.new
32
- @search = self.class.instance_variable_get('@search') || HashWithIndifferentAccess.new
30
+ @search = self.class.instance_variable_get('@search') || HashWithIndifferentAccess.new
33
31
  @includes = Set.new()
34
32
  @cname = @base.name.downcase
35
33
  @batch_actions = nil
@@ -56,7 +54,7 @@ class Tabulatr::Data
56
54
  join_required_tables(params)
57
55
 
58
56
  pagination = compute_pagination(params[:page], params[:pagesize])
59
- apply_pagination(pagination)
57
+ apply_pagination(pagination.slice(:offset, :pagesize))
60
58
 
61
59
  # TODO: batch actions and checked ids
62
60
 
@@ -104,10 +102,10 @@ class Tabulatr::Data
104
102
  # Params
105
103
  #++
106
104
 
107
- def filter_params(params) params["#{@cname}_filter"] end
108
- def search_param(params) params["#{@cname}_search"] end
109
- def sort_params(params) params["#{@cname}_sort"] end
110
- def batch_params(params) params["#{@cname}_batch"] end
105
+ def filter_params(params) params["#{Tabulatr::Utility.formatted_name(@base.name)}_filter"] end
106
+ def search_param(params) params["#{Tabulatr::Utility.formatted_name(@base.name)}_search"] end
107
+ def sort_params(params) params["#{Tabulatr::Utility.formatted_name(@base.name)}_sort"] end
108
+ def batch_params(params) params["#{Tabulatr::Utility.formatted_name(@base.name)}_batch"] end
111
109
  def check_params(params)
112
110
  tabulatr_checked = params["tabulatr_checked"]
113
111
  if tabulatr_checked.present?
@@ -119,6 +117,7 @@ class Tabulatr::Data
119
117
  tt = (params[:arguments].split(",").select{|s| s[':']}.map do |s|
120
118
  s.split(':').first
121
119
  end.uniq.map(&:to_sym))
120
+ tt.delete(@table_name.to_sym)
122
121
  @includes = @includes + tt
123
122
  # @relation = @relation.includes(@includes.map(&:to_sym)).references(@includes.map(&:to_sym))
124
123
  @relation = @relation.eager_load(@includes.map(&:to_sym))
@@ -132,7 +131,6 @@ class Tabulatr::Data
132
131
 
133
132
  end
134
133
 
135
- require_relative './column_name_builder'
136
134
  require_relative './dsl'
137
135
  require_relative './filtering'
138
136
  require_relative './invoker'
@@ -23,35 +23,50 @@
23
23
 
24
24
  module Tabulatr::Data::DSL
25
25
 
26
+ def target_class(name)
27
+ s = name.to_s
28
+ @target_class = s.camelize.constantize rescue "There's no class `#{s.camelize}' for `#{self.name}'"
29
+ @target_class_name = s.underscore
30
+ end
31
+
32
+ def main_class
33
+ target_class_name # to get auto setting @target_class
34
+ @target_class
35
+ end
36
+
37
+ def target_class_name
38
+ return @target_class_name if @target_class_name.present?
39
+ if (s = /(.+)TabulatrData\Z/.match(self.name))
40
+ # try whether it's a class
41
+ target_class s[1].underscore
42
+ else
43
+ raise "Don't know which class should be target_class for `#{self.name}'."
44
+ end
45
+ end
46
+
26
47
  def column(name, sort_sql: nil, filter_sql: nil, sql: nil, table_column_options: {}, &block)
27
- @columns ||= HashWithIndifferentAccess.new
28
- table_column = Tabulatr::Renderer::Column.from(table_column_options.merge(name: name, klass: @base))
29
48
  @table_columns ||= []
49
+ table_name = main_class.table_name
50
+ table_column = Tabulatr::Renderer::Column.from(
51
+ table_column_options.merge(name: name,
52
+ klass: @base, sort_sql: sort_sql || sql || "#{table_name}.#{name}",
53
+ filter_sql: filter_sql || sql || "#{table_name}.#{name}",
54
+ table_name: table_name.to_sym,
55
+ output: block_given? ? block : ->(record){record.send(name)}))
30
56
  @table_columns << table_column
31
-
32
- @columns[name.to_sym] = {
33
- name: name,
34
- sort_sql: sort_sql || sql,
35
- filter_sql: filter_sql || sql,
36
- output: block,
37
- table_column: table_column
38
- }
39
57
  end
40
58
 
41
59
  def association(assoc, name, sort_sql: nil, filter_sql: nil, sql: nil, table_column_options: {}, &block)
42
- @assocs ||= HashWithIndifferentAccess.new
43
- @assocs[assoc.to_sym] ||= {}
44
60
  @table_columns ||= []
45
- table_column = Tabulatr::Renderer::Association.from(table_column_options.merge(name: name, table_name: assoc, klass: @base))
61
+ assoc_klass = main_class.reflect_on_association(assoc.to_sym)
62
+ t_name = assoc_klass.try(:table_name)
63
+ table_column = Tabulatr::Renderer::Association.from(
64
+ table_column_options.merge(name: name, table_name: assoc,
65
+ klass: assoc_klass.try(:klass),
66
+ sort_sql: sort_sql || sql || "#{t_name}.#{name}",
67
+ filter_sql: filter_sql || sql || "#{t_name}.#{name}",
68
+ output: block_given? ? block : ->(record){record.send(assoc).try(name)}))
46
69
  @table_columns << table_column
47
-
48
- @assocs[assoc.to_sym][name.to_sym] = {
49
- name: name,
50
- sort_sql: sort_sql || sql,
51
- filter_sql: filter_sql || sql,
52
- output: block,
53
- table_column: table_column
54
- }
55
70
  end
56
71
 
57
72
  def search(*args, &block)
@@ -29,13 +29,15 @@ module Tabulatr::Data::Filtering
29
29
  if @search.is_a? Array
30
30
  query = query.strip.gsub(/['*%\s]+/, '%')
31
31
  a = @search.map do |name|
32
- nn = build_column_name name, use_for: :filter
32
+ column = table_columns.find{|c| c.name == name}
33
+ nn = column ? column.filter_sql : name
34
+ # nn = build_column_name name, use_for: :filter
33
35
  "(#{nn} #{like} '%#{query}%')"
34
36
  end
35
37
  a = a.join(' OR ')
36
38
  @relation = @relation.where(a)
37
39
  else # search is a proc
38
- @relation = @relation.where(@search.(query))
40
+ execute_provided_search_block!(query)
39
41
  end
40
42
  end
41
43
 
@@ -46,8 +48,10 @@ module Tabulatr::Data::Filtering
46
48
  filter_params.each do |filter|
47
49
  name, value = filter
48
50
  next unless value.present?
49
- nn = build_column_name name, use_for: :filter
50
- apply_condition(nn, value)
51
+
52
+ table_name, method_name = name.split(':').map(&:to_sym)
53
+ column = table_columns.find{|c| c.table_name == table_name && c.name == method_name}
54
+ apply_condition(column, value)
51
55
  end
52
56
  end
53
57
 
@@ -56,16 +60,16 @@ module Tabulatr::Data::Filtering
56
60
  name, value = assoc_filter
57
61
  assoc, att = name.split(".").map(&:to_sym)
58
62
  table_name = table_name_for_association(assoc)
59
- nn = build_column_name(att, table_name: table_name, assoc_name: assoc, use_for: :filter)
60
- apply_condition(nn, value)
63
+ column = table_columns.find{|c| c.table_name = table_name && name == name}
64
+ apply_condition(column, value)
61
65
  end
62
66
  end
63
67
 
64
68
  def apply_condition(n,v)
65
69
  if ['true', 'false'].include?(v)
66
- @relation = @relation.where(:"#{n}" => Tabulatr::Utility.string_to_boolean(v))
70
+ @relation = @relation.where(:"#{n.filter_sql}" => Tabulatr::Utility.string_to_boolean(v))
67
71
  elsif v.is_a?(String)
68
- apply_string_condition("#{n} = ?", v)
72
+ apply_string_condition("#{n.filter_sql} = ?", v)
69
73
  elsif v.is_a?(Hash)
70
74
  apply_hash_condition(n, v)
71
75
  else
@@ -100,8 +104,8 @@ module Tabulatr::Data::Filtering
100
104
  since = Date.parse(cond[:from]) if cond[:from].present?
101
105
  to = Date.parse(cond[:to]) if cond[:to].present?
102
106
  end
103
- @relation = @relation.where("#{n} >= ?", since) if since.present?
104
- @relation = @relation.where("#{n} <= ?", to) if to.present?
107
+ @relation = @relation.where("#{n.filter_sql} >= ?", since) if since.present?
108
+ @relation = @relation.where("#{n.filter_sql} <= ?", to) if to.present?
105
109
  end
106
110
 
107
111
  def apply_string_condition(replacement_string, value)
@@ -110,10 +114,34 @@ module Tabulatr::Data::Filtering
110
114
 
111
115
  def apply_hash_condition(column_name, hash)
112
116
  like ||= Tabulatr::Utility.like_statement
113
- apply_string_condition("#{column_name} #{like} ?", "%#{hash[:like]}%") if hash[:like].present?
117
+ apply_string_condition("#{column_name.filter_sql} #{like} ?", "%#{hash[:like]}%") if hash[:like].present?
114
118
  apply_date_condition(column_name, hash[:date])
115
- apply_string_condition("#{column_name} >= ?", "#{hash[:from]}")
116
- apply_string_condition("#{column_name} <= ?", "#{hash[:to]}")
119
+ apply_string_condition("#{column_name.filter_sql} >= ?", "#{hash[:from]}")
120
+ apply_string_condition("#{column_name.filter_sql} <= ?", "#{hash[:to]}")
121
+ end
122
+
123
+ private
124
+
125
+ def execute_provided_search_block!(query)
126
+ if @search.arity == 1
127
+ search_result = @search.(query)
128
+ elsif @search.arity == 2
129
+ search_result = @search.(query, @relation)
130
+ else
131
+ raise 'Search block needs either `query` or both `query` and `relation` block variables'
132
+ end
133
+ handle_search_result(search_result)
134
+ end
135
+
136
+ def handle_search_result(search_result)
137
+ return if search_result.nil?
138
+ if search_result.is_a?(ActiveRecord::Relation)
139
+ @relation = search_result
140
+ elsif search_result.is_a?(String) || search_result.is_a?(Hash) || search_result.is_a?(Array)
141
+ @relation = @relation.where(search_result)
142
+ else
143
+ Tabulatr::UnexpectedSearchResultError.raise_error(search_result.class)
144
+ end
117
145
  end
118
146
 
119
147
  end
@@ -28,31 +28,18 @@ module Tabulatr::Data::Formatting
28
28
  return @relation.map do |record|
29
29
  view.record = record
30
30
  h = HashWithIndifferentAccess.new
31
- @columns.each do |name, opts|
32
- h[name] = format_column(record, name, opts, view)
33
- end # @columns each
34
- @assocs.each do |table_name, columns|
35
- h[table_name] ||= {}
36
- columns.each do |name, opts|
37
- h[table_name][name] = format_association(record, table_name, name, opts, view)
38
- end
39
- end # @assocs each
31
+ table_columns.each do |tc|
32
+ h[tc.table_name] ||= HashWithIndifferentAccess.new
33
+ h[tc.table_name][tc.name] = format_column(record, tc.output, view)
34
+ end
40
35
  h[:_row_config] = format_row(view, @row)
36
+ h[:id] = record.id
41
37
  h
42
38
  end # @relation map
43
39
  end # apply_formats
44
40
 
45
- def format_column(record, name, opts, view)
46
- if opts[:output]
47
- view.instance_exec(record, &opts[:output])
48
- else
49
- opts[:table_column].value_for(record, view)
50
- end
51
- end
52
-
53
- def format_association(record, table_name, name, opts, view)
54
- return view.instance_exec(record, &opts[:output]) if opts[:output]
55
- opts[:table_column].value_for(record, view)
41
+ def format_column(record, output, view)
42
+ view.instance_exec(record, &output) if output.present?
56
43
  end
57
44
 
58
45
  def format_row(view, row)
@@ -23,7 +23,7 @@
23
23
 
24
24
  module Tabulatr::Data::Pagination
25
25
 
26
- def apply_pagination(offset: 0, pagesize: nil, pages: nil, page: 1, count: nil)
26
+ def apply_pagination(offset: 0, pagesize: nil)
27
27
  @relation = @relation.limit(pagesize).offset(offset)
28
28
  end
29
29
 
@@ -33,7 +33,6 @@ module Tabulatr::Data::Pagination
33
33
  pagesize, page = pagesize.to_i, page.to_i
34
34
 
35
35
  pages = (count/pagesize.to_f).ceil
36
-
37
36
  {
38
37
  offset: [0,((page-1)*pagesize).to_i].max,
39
38
  pagesize: pagesize,
@@ -27,6 +27,7 @@ class Data::Proxy < ActionView::Base
27
27
 
28
28
  def initialize(record=nil, locals: {})
29
29
  self.class._init
30
+ Rails.application.routes.mounted_helpers.instance_methods.each{|f| self.send(f).instance_variable_set('@scope', self)}
30
31
  @record = record
31
32
  locals.each do |nam, val|
32
33
  raise "cowardly refusing to override `#{nam}'" if respond_to? nam
@@ -40,6 +41,7 @@ class Data::Proxy < ActionView::Base
40
41
  include ActionView::Helpers
41
42
  include Rails.application.helpers
42
43
  include Rails.application.routes.url_helpers
44
+ include Rails.application.routes.mounted_helpers
43
45
  end
44
46
 
45
47
  end
@@ -23,26 +23,37 @@
23
23
 
24
24
  module Tabulatr::Data::Sorting
25
25
 
26
- def apply_sorting(sortparam, default_order=nil)
26
+ def apply_sorting(sortparam)
27
27
  if sortparam.present?
28
- sort_by, orientation = sortparam.split(' ')
29
- klass = sort_by.split('.').first
30
- col_name = sort_by.split('.').last
31
- assoc_name = nil
32
- if klass == @cname
33
- table_name = @base.table_name
28
+ clname, orientation = sortparam.split(' ')
29
+ if clname[':']
30
+ splitted = clname.split(':')
34
31
  else
35
- assoc_name = @base.reflect_on_association(klass.to_sym).try(:name)
36
- table_name = @base.reflect_on_association(klass.to_sym).try(:table_name)
32
+ splitted = clname.split('.')
37
33
  end
38
- nn = build_column_name(col_name, table_name: table_name, assoc_name: assoc_name, use_for: :sort)
39
- raise "Invalid sorting orientation" unless ['asc', 'desc'].member?(orientation.downcase)
40
- @relation = @relation.order("#{nn} #{orientation}")
34
+ if splitted.count == 2
35
+ assoc_name = splitted[0].to_sym
36
+ name = splitted[1].to_sym
37
+ column = table_columns.find{|c| c.table_name == assoc_name && c.name == name}
38
+ else
39
+ name = splitted[0].to_sym
40
+ column = table_columns.find{|c| c.name == name}
41
+ end
42
+ sort_by(column, orientation)
41
43
  else
42
- @relation = @relation.order(default_order || "#{@table_name}.#{@base.primary_key} desc")
44
+ @relation = @relation.reorder("#{@table_name}.#{@base.primary_key} desc")
43
45
  end
44
46
  end
45
47
 
48
+ def sort_by(column, orientation)
49
+ sort_sql = column.sort_sql
50
+ if sort_sql.respond_to? :call
51
+ @relation = sort_sql.call(@relation, orientation, "#{@table_name}.#{@base.primary_key}", @base)
52
+ else
53
+ @relation = @relation.reorder("#{sort_sql} #{orientation}")
54
+ end
55
+ end
56
+
46
57
  end
47
58
 
48
59
  Tabulatr::Data.send :include, Tabulatr::Data::Sorting
@@ -21,6 +21,7 @@
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  #++
23
23
 
24
+ require 'slim'
24
25
  class Tabulatr::Engine < Rails::Engine
25
26
 
26
27
  end
@@ -5,7 +5,7 @@ en:
5
5
  batch_actions: 'Batch actions'
6
6
  count: 'Showing: %{current} of total %{total}. %{per_page} items per page.'
7
7
  apply_filters: 'Apply'
8
- search: 'Search'
8
+ search: 'Search...'
9
9
  date_filter:
10
10
  none: ''
11
11
  today: "Today"
@@ -27,7 +27,7 @@ de:
27
27
  batch_actions: 'Batch-Aktionen'
28
28
  count: 'Zeige %{current} von insgesamt %{total}. %{per_page} pro Seite.'
29
29
  apply_filters: 'Anwenden'
30
- search: 'Suche'
30
+ search: 'Suche...'
31
31
  date_filter:
32
32
  none: ''
33
33
  today: "Heute"
@@ -62,34 +62,32 @@ module Tabulatr::JsonBuilder
62
62
  end
63
63
 
64
64
  def self.insert_attribute_in_hash at, f, r={}
65
+ action = at[:action].to_sym
66
+ relation = at[:relation].try(:to_sym)
65
67
  if at.has_key? :relation
66
- rel = at[:relation].to_sym
67
- action = at[:action].to_sym
68
- # if f.class.reflect_on_association(at[:relation].to_sym).collection?
69
- # if at[:action].to_sym == :count
70
- # r["#{at[:relation]}:#{at[:action]}"] = f.try(at[:relation]).count
71
- # else
72
- # r["#{at[:relation]}:#{at[:action]}"] = f.try(at[:relation]).map(&at[:action].to_sym).join(', ')
73
- # end
74
- # else
75
- # r["#{at[:relation]}:#{at[:action]}"] = f.try(at[:relation]).try(at[:action])
76
- # end
77
- begin
78
- raise Tabulatr::RequestDataNotIncludedError.raise_error(rel, f) if !f.has_key?(rel)
79
- raise Tabulatr::RequestDataNotIncludedError.raise_error(action, rel) if !f[rel].has_key?(action) && action != :id
80
- r["#{at[:relation]}:#{at[:action]}"] = f[rel][action]
81
- rescue TypeError, NoMethodError => e
82
- Tabulatr::RequestDataNotIncludedError.raise_error(at[:action], at[:relation])
83
- end
68
+ check_if_attribute_is_in_hash(f, relation)
69
+ check_if_attribute_is_in_hash(f[relation], action) if action != :id
70
+ set_value_at_key(r, f[relation][action], "#{at[:relation]}:#{at[:action]}")
84
71
  else
85
- begin
86
- action = at[:action].to_sym
87
- raise Tabulatr::RequestDataNotIncludedError.raise_error(action, f) if !f.has_key?(action) && [:checkbox, :id].exclude?(action)
88
- r[at[:action]] = f[action]
89
- rescue TypeError, NoMethodError => e
90
- raise Tabulatr::RequestDataNotIncludedError.raise_error(action, f)
91
- end
72
+ check_if_attribute_is_in_hash(f, action) if [:checkbox, :id].exclude?(action)
73
+ set_value_at_key(r, f[action], at[:action])
92
74
  end
93
75
  r
94
76
  end
77
+
78
+ private
79
+
80
+ def self.check_if_attribute_is_in_hash hash, key
81
+ if !hash.has_key?(key)
82
+ raise Tabulatr::RequestDataNotIncludedError.raise_error(key, hash)
83
+ end
84
+ end
85
+
86
+ def self.set_value_at_key hash, value, key
87
+ begin
88
+ hash[key] = value
89
+ rescue TypeError, NoMethodError => e
90
+ raise Tabulatr::RequestDataNotIncludedError.raise_error(key, value)
91
+ end
92
+ end
95
93
  end
@@ -22,6 +22,10 @@
22
22
  #++
23
23
 
24
24
  class ActionController::Base
25
+ before_filter do
26
+ @_tabulatr_table_index = 0
27
+ end
28
+
25
29
  def tabulatr_for(relation, tabulatr_data_class: nil, serializer: nil, render_action: nil, locals: {}, &block)
26
30
  klass = relation.respond_to?(:klass) ? relation.klass : relation
27
31
  respond_to do |format|
@@ -23,8 +23,9 @@
23
23
 
24
24
  class ActionView::Base
25
25
  # render the table in a view
26
- def table_for(klass, columns: [], **opts, &block)
27
- Tabulatr::Renderer.build_table(klass, self, opts, columns, &block)
26
+ def table_for(klass, columns: [], tabulatr_data_class: nil, **opts, &block)
27
+ @_tabulatr_table_index += 1
28
+ Tabulatr::Renderer.build_table(klass, self, opts, columns, tabulatr_data_class, &block)
28
29
  end
29
30
 
30
31
  def static_table_for(records, opts={}, &block)
@@ -22,8 +22,10 @@
22
22
  #++
23
23
 
24
24
  class Tabulatr::Renderer::Checkbox < Tabulatr::Renderer::Column
25
+ include ActionView::Helpers::FormTagHelper
26
+
25
27
  def human_name
26
- nil
28
+ check_box_tag('mark_all', '1', false, class: 'tabulatr_mark_all').html_safe
27
29
  end
28
30
 
29
31
  def coltype() 'checkbox' end
@@ -26,7 +26,8 @@ class Tabulatr::Renderer::Column
26
26
 
27
27
  attr_accessor *%i{name header width align valign wrap type th_html filter_html
28
28
  filter filter_width range_filter_symbol
29
- sortable table_name block klass format map classes cell_style header_style}
29
+ sortable table_name block klass format map classes cell_style header_style
30
+ sort_sql filter_sql output}
30
31
 
31
32
  def self.from(
32
33
  name: nil,
@@ -46,6 +47,9 @@ class Tabulatr::Renderer::Column
46
47
  klass: nil,
47
48
  cell_style: {},
48
49
  header_style: {},
50
+ sort_sql: nil,
51
+ filter_sql: nil,
52
+ output: nil,
49
53
  &block)
50
54
  b = block_given? ? block : nil
51
55
  self.new(
@@ -66,10 +70,41 @@ class Tabulatr::Renderer::Column
66
70
  klass: klass,
67
71
  block: b,
68
72
  cell_style: cell_style,
69
- header_style: header_style
73
+ header_style: header_style,
74
+ sort_sql: sort_sql,
75
+ filter_sql: filter_sql,
76
+ output: output
70
77
  ).apply_styles!
71
78
  end
72
79
 
80
+ def update_options(hash = {}, &block)
81
+ self.header = hash[:header] || self.header
82
+ self.classes = hash[:classes] || self.classes
83
+ self.width = hash[:width] || self.width
84
+ self.align = hash[:align] || self.align
85
+ self.valign = hash[:valign] || self.valign
86
+ self.wrap = hash[:wrap] || self.wrap
87
+ self.th_html = hash[:th_html] || self.th_html
88
+ self.filter_html = hash[:filter_html] || self.filter_html
89
+ self.filter = hash[:filter] || self.filter
90
+ self.sortable = hash[:sortable] || self.sortable
91
+ self.format = hash[:format] || self.format
92
+ self.map = hash[:map] || self.map
93
+ self.th_html = hash[:th_html] || self.th_html
94
+ self.output = block if block_given?
95
+ self.filter_sql = hash[:filter_sql] || self.filter_sql
96
+ self.sort_sql = hash[:sort_sql] || self.sort_sql
97
+ if self.cell_style == ''
98
+ self.cell_style = {}
99
+ end
100
+ self.cell_style = hash[:cell_style] || self.cell_style
101
+ if self.header_style == ''
102
+ self.header_style = {}
103
+ end
104
+ self.header_style = hash[:header_style] || self.header_style
105
+ self.apply_styles!
106
+ end
107
+
73
108
  def klassname() @_klassname ||= @klass.name.underscore end
74
109
  def human_name() header || klass.human_attribute_name(name) end
75
110
  def sort_param() "#{klassname}_sort" end
@@ -83,11 +118,19 @@ class Tabulatr::Renderer::Column
83
118
 
84
119
  def apply_styles!
85
120
  # raise cell_style.inspect
86
- self.cell_style = style_options.merge(self.cell_style).map{|e| e.join(':')}.join(';')
87
- self.header_style = style_options.merge(self.header_style).map{|e| e.join(':')}.join(';')
121
+ self.cell_style = style_options.merge(self.cell_style)
122
+ self.header_style = style_options.merge(self.header_style)
88
123
  self
89
124
  end
90
125
 
126
+ def html_cell_style
127
+ cell_style.map{|e| e.join(':')}.join(';')
128
+ end
129
+
130
+ def html_header_style
131
+ header_style.map{|e| e.join(':')}.join(';')
132
+ end
133
+
91
134
  def style_options
92
135
  default_style_attributes = {
93
136
  :'text-align' => align,
@@ -106,7 +149,6 @@ class Tabulatr::Renderer::Column
106
149
  return r
107
150
  end
108
151
  val = principal_value(record) or return ''
109
-
110
152
  if format.present? && val.respond_to?(:to_ary)
111
153
  val.map do |v|
112
154
  case format