tabulatr2 0.9.4 → 0.9.6

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