tabulatr2 0.9.20 → 0.9.21

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -0
  3. data/.gitignore +1 -0
  4. data/CHANGELOG.md +4 -0
  5. data/Gemfile +4 -0
  6. data/app/assets/javascripts/tabulatr/_events.js +252 -0
  7. data/app/assets/javascripts/tabulatr/_pagination.js +85 -0
  8. data/app/assets/javascripts/tabulatr/_tabulatr.js +70 -372
  9. data/app/assets/javascripts/tabulatr/application.js +2 -0
  10. data/app/views/tabulatr/_tabulatr_actual_table.html.slim +7 -7
  11. data/app/views/tabulatr/_tabulatr_filter_dialog.html.slim +1 -1
  12. data/app/views/tabulatr/_tabulatr_static_table.html.slim +5 -3
  13. data/lib/tabulatr/data/data.rb +1 -1
  14. data/lib/tabulatr/data/dsl.rb +62 -86
  15. data/lib/tabulatr/data/filtering.rb +32 -52
  16. data/lib/tabulatr/data/sorting.rb +1 -1
  17. data/lib/tabulatr/params_builder.rb +50 -0
  18. data/lib/tabulatr/rails/action_view.rb +2 -2
  19. data/lib/tabulatr/rails/active_record.rb +3 -8
  20. data/lib/tabulatr/renderer/action.rb +1 -1
  21. data/lib/tabulatr/renderer/association.rb +3 -3
  22. data/lib/tabulatr/renderer/buttons.rb +1 -1
  23. data/lib/tabulatr/renderer/column.rb +40 -125
  24. data/lib/tabulatr/renderer/columns_from_block.rb +10 -7
  25. data/lib/tabulatr/renderer/renderer.rb +26 -15
  26. data/lib/tabulatr/version.rb +1 -1
  27. data/lib/tabulatr.rb +1 -0
  28. data/spec/dummy/app/controllers/products_controller.rb +5 -6
  29. data/spec/dummy/app/tabulatr_data/product_tabulatr_data.rb +6 -6
  30. data/spec/dummy/app/views/products/stupid_array.html.erb +20 -6
  31. data/spec/dummy/app/views/products/with_styling.html.erb +1 -1
  32. data/spec/dummy/app/views/products/without_filters.html.erb +1 -0
  33. data/spec/dummy/config/routes.rb +1 -0
  34. data/spec/features/tabulatrs_spec.rb +7 -2
  35. data/spec/lib/tabulatr/data/data_spec.rb +2 -2
  36. data/spec/lib/tabulatr/data/dsl_spec.rb +54 -4
  37. data/spec/lib/tabulatr/data/filtering_spec.rb +164 -7
  38. data/spec/lib/tabulatr/data/formatting_spec.rb +2 -2
  39. data/spec/lib/tabulatr/data/sorting_spec.rb +6 -6
  40. data/spec/lib/tabulatr/params_builder_spec.rb +19 -0
  41. data/spec/lib/tabulatr/renderer/association_spec.rb +29 -0
  42. data/spec/lib/tabulatr/renderer/renderer_spec.rb +8 -0
  43. data/spec/rails_helper.rb +4 -0
  44. metadata +13 -3
@@ -23,127 +23,78 @@
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
26
  def main_class
33
27
  target_class_name # to get auto setting @target_class
34
28
  @target_class
35
29
  end
36
30
 
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
-
47
- def column(name, header: nil, sort_sql: nil, filter_sql: nil, sql: nil, table_column_options: {},
48
- classes: nil, width: false, align: false, valign: false, wrap: nil, th_html: false,
49
- filter_html: false, filter_label: nil, filter: true, sortable: true, format: nil, map: true, cell_style: {},
50
- header_style: {},
51
- &block)
31
+ def column(name, opts = {}, &block)
52
32
  @table_columns ||= []
53
- table_column_options = {classes: classes, width: width, align: align, valign: valign, wrap: wrap,
54
- th_html: th_html, filter_html: filter_html, filter_label: filter_label, filter: filter, sortable: sortable,
55
- format: format, map: map, cell_style: cell_style, header_style: header_style,
56
- header: header
57
- }.merge(table_column_options)
58
- table_name = main_class.table_name
33
+ sql_options = determine_sql(opts, main_class.quoted_table_name, name)
34
+ opts = {
35
+ sort_sql: sql_options[:sort_sql],
36
+ filter: true,
37
+ sortable: true,
38
+ filter_sql: sql_options[:filter_sql]}.merge(opts)
59
39
  table_column = Tabulatr::Renderer::Column.from(
60
- table_column_options.merge(name: name,
40
+ name: name,
61
41
  klass: @base,
62
- sort_sql: sort_sql || sql || "#{main_class.quoted_table_name}.#{ActiveRecord::Base.connection.quote_column_name(name)}",
63
- filter_sql: filter_sql || sql || "#{main_class.quoted_table_name}.#{ActiveRecord::Base.connection.quote_column_name(name)}",
64
- table_name: table_name.to_sym,
65
- output: block_given? ? block : ->(record){record.send(name)}))
42
+ col_options: Tabulatr::ParamsBuilder.new(opts),
43
+ table_name: main_class.table_name.to_sym,
44
+ output: block_given? ? block : ->(record){record.send(name)})
66
45
  @table_columns << table_column
67
46
  end
68
47
 
69
- def association(assoc, name, header: nil, sort_sql: nil, filter_sql: nil, sql: nil, table_column_options: {},
70
- classes: nil, width: false, align: false, valign: false, wrap: nil, th_html: false,
71
- filter_html: false, filter_label: nil, filter: true, sortable: true, format: nil, map: true, cell_style: {},
72
- header_style: {},
73
- &block)
48
+ def association(assoc, name, opts = {}, &block)
74
49
  @table_columns ||= []
75
- table_column_options = {classes: classes, width: width, align: align, valign: valign, wrap: wrap,
76
- th_html: th_html, filter_html: filter_html, filter_label: filter_label, filter: filter, sortable: sortable,
77
- format: format, map: map, cell_style: cell_style, header_style: header_style,
78
- header: header
79
- }.merge(table_column_options)
80
50
  assoc_klass = main_class.reflect_on_association(assoc.to_sym)
81
- t_name = assoc_klass.try(:quoted_table_name)
51
+ sql_options = determine_sql(opts, assoc_klass.try(:quoted_table_name), name)
52
+ opts = {
53
+ sort_sql: sql_options[:sort_sql],
54
+ filter_sql: sql_options[:filter_sql]}.merge(opts)
82
55
  table_column = Tabulatr::Renderer::Association.from(
83
- table_column_options.merge(name: name, table_name: assoc,
56
+ name: name,
84
57
  klass: assoc_klass.try(:klass),
85
- sort_sql: sort_sql || sql || "#{t_name}.#{ActiveRecord::Base.connection.quote_column_name(name)}",
86
- filter_sql: filter_sql || sql || "#{t_name}.#{ActiveRecord::Base.connection.quote_column_name(name)}",
87
- output: block_given? ? block : ->(record){a=record.send(assoc); a.try(:read_attribute, name) || a.try(name)}))
58
+ col_options: Tabulatr::ParamsBuilder.new(opts),
59
+ table_name: assoc,
60
+ output: block_given? ? block : ->(record){a=record.send(assoc); a.try(:read_attribute, name) || a.try(name)})
88
61
  @table_columns << table_column
89
62
  end
90
63
 
91
- def actions(header: nil, name: nil, table_column_options: {},
92
- classes: nil, width: false, align: false, valign: false, wrap: nil, th_html: false,
93
- filter_html: false, filter_label: nil, filter: true, sortable: true, format: nil, map: true, cell_style: {},
94
- header_style: {},
95
- &block)
64
+ def actions(opts = {}, &block)
96
65
  raise 'give a block to action column' unless block_given?
97
66
  @table_columns ||= []
98
- table_column_options = {classes: classes, width: width, align: align, valign: valign, wrap: wrap,
99
- th_html: th_html, filter_html: filter_html, filter_label: filter_label, filter: filter, sortable: sortable,
100
- format: format, map: map, cell_style: cell_style, header_style: header_style
101
- }.merge(table_column_options)
102
67
  table_column = Tabulatr::Renderer::Action.from(
103
- table_column_options.merge(
104
- name: (name || '_actions'), table_name: main_class.table_name.to_sym,
105
- klass: @base, header: header || '',
106
- filter: false, sortable: false,
107
- output: block))
68
+ name: '_actions',
69
+ table_name: main_class.table_name.to_sym,
70
+ klass: @base,
71
+ col_options: Tabulatr::ParamsBuilder.new({header: (opts[:header] || ''), filter: false, sortable: false}),
72
+ output: block)
108
73
  @table_columns << table_column
109
74
  end
110
75
 
111
- def buttons(header: nil, name: nil, table_column_options: {},
112
- classes: nil, width: false, align: false, valign: false, wrap: nil, th_html: false,
113
- filter_html: false, filter: true, sortable: true, format: nil, map: true, cell_style: {},
114
- header_style: {},
115
- &block)
76
+ def buttons(opts = {}, &block)
116
77
  raise 'give a block to action column' unless block_given?
117
78
  @table_columns ||= []
118
- table_column_options = {classes: classes, width: width, align: align, valign: valign, wrap: wrap,
119
- th_html: th_html, filter_html: filter_html, filter: filter, sortable: sortable,
120
- format: format, map: map, cell_style: cell_style, header_style: header_style
121
- }.merge(table_column_options)
122
79
  output = ->(r) {
123
80
  tdbb = Tabulatr::Data::ButtonBuilder.new
124
81
  self.instance_exec tdbb, r, &block
125
- bb = tdbb.val
126
- self.controller.render_to_string partial: '/tabulatr/tabulatr_buttons', locals: {buttons: bb}, formats: [:html]
82
+ self.controller.render_to_string partial: '/tabulatr/tabulatr_buttons', locals: {buttons: tdbb.val}, formats: [:html]
127
83
  }
84
+ opts = {header: opts[:header] || '', filter: false, sortable: false}.merge(opts)
128
85
  table_column = Tabulatr::Renderer::Buttons.from(
129
- table_column_options = table_column_options.merge(
130
- name: (name || '_buttons'), table_name: main_class.table_name.to_sym,
131
- klass: @base, header: header || '',
132
- filter: false, sortable: false,
133
- output: output))
86
+ name: (opts[:name] || '_buttons'),
87
+ table_name: main_class.table_name.to_sym,
88
+ klass: @base,
89
+ col_options: Tabulatr::ParamsBuilder.new(opts),
90
+ output: output)
134
91
  @table_columns << table_column
135
92
  end
136
93
 
137
- def checkbox(table_column_options: {},
138
- classes: nil, width: false, align: false, valign: false, wrap: nil, th_html: false,
139
- filter_html: false, filter: true, sortable: true, format: nil, map: true, cell_style: {},
140
- header_style: {})
94
+ def checkbox(opts = {})
141
95
  @table_columns ||= []
142
- table_column_options = {classes: classes, width: width, align: align, valign: valign, wrap: wrap,
143
- th_html: th_html, filter_html: filter_html, filter: filter, sortable: sortable,
144
- format: format, map: map, cell_style: cell_style, header_style: header_style
145
- }.merge(table_column_options)
146
- box = Tabulatr::Renderer::Checkbox.from(table_column_options = table_column_options.merge(klass: @base, filter: false, sortable: false))
96
+ box = Tabulatr::Renderer::Checkbox.from(klass: @base,
97
+ col_options: Tabulatr::ParamsBuilder.new(opts.merge(filter: false, sortable: false)))
147
98
  @table_columns << box
148
99
  end
149
100
 
@@ -162,6 +113,31 @@ module Tabulatr::Data::DSL
162
113
  @filters << Tabulatr::Renderer::Filter.new(name, partial: partial, &block)
163
114
  end
164
115
 
116
+ private
117
+
118
+ def target_class(name)
119
+ s = name.to_s
120
+ @target_class = s.camelize.constantize rescue "There's no class `#{s.camelize}' for `#{self.name}'"
121
+ @target_class_name = s.underscore
122
+ end
123
+
124
+ def target_class_name
125
+ return @target_class_name if @target_class_name.present?
126
+ if (s = /(.+)TabulatrData\Z/.match(self.name))
127
+ # try whether it's a class
128
+ target_class s[1].underscore
129
+ else
130
+ raise "Don't know which class should be target_class for `#{self.name}'."
131
+ end
132
+ end
133
+
134
+ def determine_sql(options, table_name, column_name)
135
+ options_hash = {}
136
+ [:sort_sql, :filter_sql].each do |sym|
137
+ options_hash[sym] = options[sym] || options[:sql] || "#{table_name}.#{ActiveRecord::Base.connection.quote_column_name(column_name)}"
138
+ end
139
+ options_hash
140
+ end
165
141
  end
166
142
 
167
143
  Tabulatr::Data.send :extend, Tabulatr::Data::DSL
@@ -43,39 +43,21 @@ module Tabulatr::Data::Filtering
43
43
 
44
44
  def apply_filters(filter_params)
45
45
  return unless filter_params
46
- assoc_filters = filter_params.delete :__association
47
- apply_association_filters(assoc_filters) if assoc_filters.present?
48
46
  filter_params.each do |param|
49
47
  name, value = param
50
48
  next unless value.present?
51
49
 
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
- column = filters.find{|f| f.name.to_sym == name.to_sym} if column.nil?
55
- apply_condition(column, value)
56
- end
57
- end
58
-
59
- def apply_association_filters(assoc_filters)
60
- assoc_filters.each do |assoc_filter|
61
- name, value = assoc_filter
62
- assoc, att = name.split(".").map(&:to_sym)
63
- table_name = table_name_for_association(assoc)
64
- column = table_columns.find{|c| c.table_name = table_name && name == name}
65
- apply_condition(column, value)
50
+ apply_condition(find_column(name), value)
66
51
  end
67
52
  end
68
53
 
69
54
  def apply_condition(n,v)
70
55
  case n.filter
71
56
  when :checkbox then apply_boolean_condition(n, v)
72
- when :decimal then apply_string_condition("#{n.filter_sql} = ?", v.to_f)
73
- when :integer then apply_string_condition("#{n.filter_sql} = ?", v.to_i)
74
- when :enum then apply_string_condition("#{n.filter_sql} = ?", v.to_i)
57
+ when :decimal then apply_string_condition("#{n.col_options.filter_sql} = ?", v.to_f)
58
+ when :integer, :enum then apply_string_condition("#{n.col_options.filter_sql} = ?", v.to_i)
75
59
  when :enum_multiselect then apply_array_condition(n, v)
76
- when :exact then apply_string_condition("#{n.filter_sql} = ?", v)
77
- when Hash then apply_string_condition("#{n.filter_sql} = ?", v)
78
- when Array then apply_string_condition("#{n.filter_sql} = ?", v)
60
+ when :exact, Hash, Array then apply_string_condition("#{n.col_options.filter_sql} = ?", v)
79
61
  when :like then apply_like_condition(n, v[:like])
80
62
  when :date then apply_date_condition(n, v[:date])
81
63
  when :range then apply_range_condition(n, v)
@@ -84,52 +66,39 @@ module Tabulatr::Data::Filtering
84
66
  end
85
67
  end
86
68
 
87
- def apply_boolean_condition(n, value)
88
- @relation = @relation.where("#{n.filter_sql} = ?", Tabulatr::Utility.string_to_boolean(value))
69
+ def apply_boolean_condition(column, value)
70
+ @relation = @relation.where("#{column.col_options.filter_sql} = ?", Tabulatr::Utility.string_to_boolean(value))
89
71
  end
90
72
 
91
73
  def apply_date_condition(n, cond)
92
74
  today = Date.today
75
+ yesterday = today - 1.day
93
76
  case cond[:simple]
94
77
  when 'none' then return
95
- when 'today'
96
- since = today
97
- to = today.at_end_of_day
98
- when 'yesterday'
99
- since = today - 1.day
100
- to = since.at_end_of_day
101
- when 'this_week'
102
- since = today.at_beginning_of_week.beginning_of_day
103
- to = today.at_end_of_week.end_of_day
104
- when 'last_7_days'
105
- since = (today - 6.day).beginning_of_day
106
- to = today.at_end_of_day
107
- when 'this_month'
108
- since = today.at_beginning_of_month.beginning_of_day
109
- to = today.at_end_of_month.end_of_day
110
- when 'last_30_days'
111
- since = (today - 29.day).beginning_of_day
112
- to = today.at_end_of_day
113
- when 'from_to'
114
- since = (Date.parse(cond[:from]) rescue nil) if cond[:from].present?
115
- to = (Date.parse(cond[:to]) rescue nil) if cond[:to].present?
78
+ when 'today' then date_in_between(today, today.at_end_of_day, n)
79
+ when 'yesterday' then date_in_between(yesterday, yesterday.at_end_of_day, n)
80
+ when 'this_week' then date_in_between(today.at_beginning_of_week.beginning_of_day,
81
+ today.at_end_of_week.end_of_day, n)
82
+ when 'last_7_days' then date_in_between((today - 6.day).beginning_of_day, today.at_end_of_day, n)
83
+ when 'this_month' then date_in_between(today.at_beginning_of_month.beginning_of_day,
84
+ today.at_end_of_month.end_of_day, n)
85
+ when 'last_30_days' then date_in_between((today - 29.day).beginning_of_day, today.at_end_of_day, n)
86
+ when 'from_to' then date_in_between((Date.parse(cond[:from]) rescue nil), (Date.parse(cond[:to]) rescue nil), n)
116
87
  end
117
- @relation = @relation.where("#{n.filter_sql} >= ?", since) if since.present?
118
- @relation = @relation.where("#{n.filter_sql} <= ?", to) if to.present?
119
88
  end
120
89
 
121
90
  def apply_string_condition(replacement_string, value)
122
91
  @relation = @relation.where(replacement_string, value) if value.present?
123
92
  end
124
93
 
125
- def apply_like_condition(column_name, value)
94
+ def apply_like_condition(column, value)
126
95
  like ||= Tabulatr::Utility.like_statement
127
- apply_string_condition("#{column_name.filter_sql} #{like} ?", "%#{value}%") if value.present?
96
+ apply_string_condition("#{column.col_options.filter_sql} #{like} ?", "%#{value}%") if value.present?
128
97
  end
129
98
 
130
- def apply_range_condition(column_name, hash)
131
- apply_string_condition("#{column_name.filter_sql} >= ?", "#{hash[:from]}")
132
- apply_string_condition("#{column_name.filter_sql} <= ?", "#{hash[:to]}")
99
+ def apply_range_condition(column, hash)
100
+ apply_string_condition("#{column.col_options.filter_sql} >= ?", "#{hash[:from]}")
101
+ apply_string_condition("#{column.col_options.filter_sql} <= ?", "#{hash[:to]}")
133
102
  end
134
103
 
135
104
  def apply_array_condition(column, value)
@@ -165,6 +134,17 @@ module Tabulatr::Data::Filtering
165
134
  end
166
135
  end
167
136
 
137
+ def date_in_between(from, to, column)
138
+ @relation = @relation.where("#{column.col_options.filter_sql} >= ?", from) if from.present?
139
+ @relation = @relation.where("#{column.col_options.filter_sql} <= ?", to) if to.present?
140
+ end
141
+
142
+ def find_column(name)
143
+ table_name, method_name = name.split(':').map(&:to_sym)
144
+ table_columns.find { |c| c.table_name == table_name && c.name == method_name } ||
145
+ filters.find { |f| f.name.to_sym == name.to_sym }
146
+ end
147
+
168
148
  end
169
149
 
170
150
  Tabulatr::Data.send :include, Tabulatr::Data::Filtering
@@ -46,7 +46,7 @@ module Tabulatr::Data::Sorting
46
46
  end
47
47
 
48
48
  def sort_by(column, orientation)
49
- sort_sql = column.sort_sql
49
+ sort_sql = column.col_options.sort_sql
50
50
  if sort_sql.respond_to? :call
51
51
  @relation = sort_sql.call(@relation, orientation, "#{@table_name}.#{@base.primary_key}", @base)
52
52
  else
@@ -0,0 +1,50 @@
1
+ module Tabulatr
2
+ class ParamsBuilder
3
+ ALLOWED_PARAMS = [:header, :filter, :sortable, :data_html,
4
+ :header_html, :filter_sql, :sort_sql, :sql, :width,
5
+ :align, :wrap, :format, :filter_label, :name, :classes]
6
+ DEPRECATED_PARAMS = []
7
+
8
+ attr_accessor *ALLOWED_PARAMS
9
+
10
+ def initialize(params = {})
11
+ apply_params(params)
12
+ end
13
+
14
+ def update(params = {})
15
+ apply_params(params)
16
+ end
17
+
18
+ private
19
+
20
+ def style_options
21
+ self.data_html ||= {}
22
+ self.header_html ||= {}
23
+ self.data_html[:style] ||= ''
24
+ self.header_html[:style] ||= ''
25
+ apply_style_option('text-align', align)
26
+ apply_style_option('width', width)
27
+ apply_style_option('white-space', wrap)
28
+ end
29
+
30
+ def apply_params(params)
31
+ params.each do |k, v|
32
+ if DEPRECATED_PARAMS.include?(k.to_sym)
33
+ self.public_send(k)
34
+ elsif ALLOWED_PARAMS.exclude?(k.to_sym)
35
+ raise ArgumentError, "`#{k}` is not allowed as a parameter"
36
+ else
37
+ self.public_send("#{k}=", v)
38
+ end
39
+ end
40
+ style_options
41
+ end
42
+
43
+ def apply_style_option(attribute, value)
44
+ if value.present?
45
+ self.header_html[:style].concat("#{attribute}: #{value};")
46
+ self.data_html[:style].concat("#{attribute}: #{value};")
47
+ end
48
+ end
49
+ end
50
+ end
@@ -23,9 +23,9 @@
23
23
 
24
24
  class ActionView::Base
25
25
  # render the table in a view
26
- def table_for(klass, columns: [], filter: [], tabulatr_data_class: nil, **opts, &block)
26
+ def table_for(klass, columns: [], filters: [], tabulatr_data_class: nil, **opts, &block)
27
27
  @_tabulatr_table_index += 1
28
- Tabulatr::Renderer.build_table(klass, self, opts, columns, filter, tabulatr_data_class, &block)
28
+ Tabulatr::Renderer.build_table(klass, self, opts, columns, filters, tabulatr_data_class, &block)
29
29
  end
30
30
 
31
31
  def static_table_for(records, opts={}, &block)
@@ -23,17 +23,12 @@
23
23
 
24
24
  # We monkey patch ActiveRecord::Base to add a function for finding using
25
25
  # the information of the params hash as created by a Tabulatr table
26
+
26
27
  if Object.const_defined? "ActiveRecord"
27
28
  class ActiveRecord::Base
28
29
  def self.tabulatr(relation, tabulatr_data_class = nil)
29
- tabulatr_data_class = "#{self.name}TabulatrData".constantize unless tabulatr_data_class
30
- begin
31
- td = tabulatr_data_class.new(relation)
32
- rescue NameError => e
33
- puts e.message
34
- # TODO: Better message
35
- raise "No class `#{self.name}TabulatrData' defined. Explanation here."
36
- end
30
+ tabulatr_data_class ||= "#{self.name}TabulatrData".constantize
31
+ td = tabulatr_data_class.new(relation)
37
32
  end
38
33
  end
39
34
  end
@@ -23,7 +23,7 @@
23
23
 
24
24
  class Tabulatr::Renderer::Action < Tabulatr::Renderer::Column
25
25
  def human_name
26
- header
26
+ col_options.header
27
27
  end
28
28
 
29
29
  def coltype() 'action' end
@@ -23,7 +23,7 @@
23
23
 
24
24
  class Tabulatr::Renderer::Association < Tabulatr::Renderer::Column
25
25
  def human_name
26
- header || klass.model_name.human + ' ' + klass.human_attribute_name(name)
26
+ col_options.header || klass.model_name.human + ' ' + klass.human_attribute_name(name)
27
27
  end
28
28
 
29
29
  def coltype() 'association' end
@@ -31,9 +31,9 @@ class Tabulatr::Renderer::Association < Tabulatr::Renderer::Column
31
31
  def association?() true end
32
32
 
33
33
  def principal_value(record, view)
34
- return super if output
34
+ return super if output || block
35
35
  v = record.send(table_name)
36
- if v && v.respond_to?(:to_a) && map && name != :count
36
+ if v && v.respond_to?(:to_a) && name != :count
37
37
  v.map(&:"#{name}")
38
38
  else
39
39
  v.try(name)
@@ -23,7 +23,7 @@
23
23
 
24
24
  class Tabulatr::Renderer::Buttons < Tabulatr::Renderer::Action
25
25
  def human_name
26
- header
26
+ col_options.header
27
27
  end
28
28
 
29
29
  def coltype() 'buttons' end
@@ -24,91 +24,29 @@
24
24
  class Tabulatr::Renderer::Column
25
25
  include ActiveModel::Model
26
26
 
27
- attr_accessor *%i{name header width align valign wrap type th_html filter_html
28
- filter_label filter filter_width range_filter_symbol
29
- sortable table_name block klass format map classes cell_style header_style
30
- sort_sql filter_sql output}
27
+ attr_accessor *%i{name klass table_name col_options output block}
28
+
29
+ delegate :filter, to: :col_options
31
30
 
32
31
  def self.from(
33
32
  name: nil,
34
33
  table_name: nil,
35
- header: nil,
36
- classes: nil,
37
- width: false,
38
- align: false,
39
- valign: false,
40
- wrap: nil,
41
- th_html: false,
42
- filter_html: false,
43
- filter_label: nil,
44
- filter: true,
45
- sortable: true,
46
- format: nil,
47
- map: true,
34
+ col_options: nil,
48
35
  klass: nil,
49
- cell_style: {},
50
- header_style: {},
51
- sort_sql: nil,
52
- filter_sql: nil,
53
36
  output: nil,
54
37
  &block)
55
38
  self.new(
56
39
  name: name,
57
40
  table_name: table_name,
58
- header: header,
59
- classes: classes,
60
- width: width,
61
- align: align,
62
- valign: valign,
63
- wrap: wrap,
64
- th_html: th_html,
65
- filter_html: filter_html,
66
- filter_label: filter_label,
67
- filter: filter,
68
- sortable: sortable,
69
- format: format,
70
- map: map,
41
+ col_options: col_options,
71
42
  klass: klass,
72
- block: block,
73
- cell_style: cell_style,
74
- header_style: header_style,
75
- sort_sql: sort_sql,
76
- filter_sql: filter_sql,
77
- output: output
78
- ).apply_styles!
79
- end
80
-
81
- def update_options(hash = {}, &block)
82
- self.header = hash[:header] || self.header
83
- self.classes = hash[:classes] || self.classes
84
- self.width = hash[:width] || self.width
85
- self.align = hash[:align] || self.align
86
- self.valign = hash[:valign] || self.valign
87
- self.wrap = hash[:wrap] || self.wrap
88
- self.th_html = hash[:th_html] || self.th_html
89
- self.filter_html = hash[:filter_html] || self.filter_html
90
- self.filter_label = hash[:filter_label] || self.filter_label
91
- self.filter = hash[:filter] || self.filter
92
- self.sortable = hash[:sortable] || self.sortable
93
- self.format = hash[:format] || self.format
94
- self.map = hash[:map] || self.map
95
- self.th_html = hash[:th_html] || self.th_html
96
- self.output = block if block_given?
97
- self.filter_sql = hash[:filter_sql] || self.filter_sql
98
- self.sort_sql = hash[:sort_sql] || self.sort_sql
99
- if self.cell_style == ''
100
- self.cell_style = {}
101
- end
102
- self.cell_style = hash[:cell_style] || self.cell_style
103
- if self.header_style == ''
104
- self.header_style = {}
105
- end
106
- self.header_style = hash[:header_style] || self.header_style
107
- self.apply_styles!
43
+ output: output,
44
+ block: block
45
+ )
108
46
  end
109
47
 
110
48
  def klassname() @_klassname ||= @klass.name.underscore end
111
- def human_name() header || klass.human_attribute_name(name) end
49
+ def human_name() col_options.header || klass.human_attribute_name(name) end
112
50
  def sort_param() "#{klassname}_sort" end
113
51
  def full_name() [table_name, name].compact.join(":") end
114
52
  def coltype() 'column' end
@@ -118,49 +56,11 @@ class Tabulatr::Renderer::Column
118
56
  def checkbox?() false end
119
57
  def action?() false end
120
58
 
121
- def apply_styles!
122
- # raise cell_style.inspect
123
- self.cell_style = style_options.merge(self.cell_style)
124
- self.header_style = style_options.merge(self.header_style)
125
- self
126
- end
127
-
128
- def html_cell_style
129
- cell_style.map{|e| e.join(':')}.join(';')
130
- end
131
-
132
- def html_header_style
133
- header_style.map{|e| e.join(':')}.join(';')
134
- end
135
-
136
- def style_options
137
- default_style_attributes = {
138
- :'text-align' => align,
139
- width: width,
140
- :'vertical-align' => valign,
141
- :'white-space' => wrap
142
- }.select{|k,v| v}
143
-
144
- default_style_attributes || {}
145
- end
146
-
147
59
  def value_for(record, view)
148
60
  val = principal_value(record, view)
149
- if format.present? && val.respond_to?(:to_ary)
150
- val.map do |v|
151
- case format
152
- when Symbol then view.send(format, v)
153
- when String then format % v
154
- when Proc then format.(v)
155
- else val
156
- end
157
- end
158
- elsif format.present?
159
- case format
160
- when Symbol then view.send(format, val)
161
- when String then format % val
162
- when Proc then format.(val)
163
- else val
61
+ if self.col_options.format.present?
62
+ Array(val).map do |v|
63
+ format_value(v)
164
64
  end
165
65
  else
166
66
  val
@@ -180,22 +80,37 @@ class Tabulatr::Renderer::Column
180
80
  end
181
81
 
182
82
  def determine_appropriate_filter!
183
- typ = self.klass.columns_hash[self.name.to_s].try(:type).try(:to_sym)
83
+ typ = self.klass.columns_hash[self.name.to_s].type.to_sym rescue nil
184
84
  case typ
185
- when :integer
186
- if self.klass.respond_to?(:defined_enums) && self.klass.defined_enums.keys.include?(self.name.to_s)
187
- self.filter = :enum
188
- else
189
- self.filter = :integer
190
- end
191
- when :enum then self.filter = :enum
192
- when :float, :decimal then self.filter = :decimal
193
- when :string, :text then self.filter = :like
194
- when :date, :time, :datetime, :timestamp then self.filter = :date
195
- when :boolean then self.filter = :checkbox
196
- when nil then self.filter = :exact
85
+ when :integer then self.col_options.filter = filter_type_for_integer
86
+ when :enum then self.col_options.filter = :enum
87
+ when :float, :decimal then self.col_options.filter = :decimal
88
+ when :string, :text then self.col_options.filter = :like
89
+ when :date, :time, :datetime, :timestamp then self.col_options.filter = :date
90
+ when :boolean then self.col_options.filter = :checkbox
91
+ when nil then self.col_options.filter = :exact
197
92
  else raise "Unknown filter type for #{self.name}: »#{typ}«"
198
93
  end
199
94
  end
200
95
 
96
+
97
+ private
98
+
99
+ def filter_type_for_integer
100
+ if self.klass.respond_to?(:defined_enums) && self.klass.defined_enums.keys.include?(self.name.to_s)
101
+ :enum
102
+ else
103
+ :integer
104
+ end
105
+ end
106
+
107
+ def format_value(value)
108
+ case self.col_options.format
109
+ when Symbol then view.send(col_options.format, value)
110
+ when String then col_options.format % value
111
+ when Proc then col_options.format.(value)
112
+ else value
113
+ end
114
+ end
115
+
201
116
  end