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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +17 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +4 -0
- data/app/assets/javascripts/tabulatr/_events.js +252 -0
- data/app/assets/javascripts/tabulatr/_pagination.js +85 -0
- data/app/assets/javascripts/tabulatr/_tabulatr.js +70 -372
- data/app/assets/javascripts/tabulatr/application.js +2 -0
- data/app/views/tabulatr/_tabulatr_actual_table.html.slim +7 -7
- data/app/views/tabulatr/_tabulatr_filter_dialog.html.slim +1 -1
- data/app/views/tabulatr/_tabulatr_static_table.html.slim +5 -3
- data/lib/tabulatr/data/data.rb +1 -1
- data/lib/tabulatr/data/dsl.rb +62 -86
- data/lib/tabulatr/data/filtering.rb +32 -52
- data/lib/tabulatr/data/sorting.rb +1 -1
- data/lib/tabulatr/params_builder.rb +50 -0
- data/lib/tabulatr/rails/action_view.rb +2 -2
- data/lib/tabulatr/rails/active_record.rb +3 -8
- data/lib/tabulatr/renderer/action.rb +1 -1
- data/lib/tabulatr/renderer/association.rb +3 -3
- data/lib/tabulatr/renderer/buttons.rb +1 -1
- data/lib/tabulatr/renderer/column.rb +40 -125
- data/lib/tabulatr/renderer/columns_from_block.rb +10 -7
- data/lib/tabulatr/renderer/renderer.rb +26 -15
- data/lib/tabulatr/version.rb +1 -1
- data/lib/tabulatr.rb +1 -0
- data/spec/dummy/app/controllers/products_controller.rb +5 -6
- data/spec/dummy/app/tabulatr_data/product_tabulatr_data.rb +6 -6
- data/spec/dummy/app/views/products/stupid_array.html.erb +20 -6
- data/spec/dummy/app/views/products/with_styling.html.erb +1 -1
- data/spec/dummy/app/views/products/without_filters.html.erb +1 -0
- data/spec/dummy/config/routes.rb +1 -0
- data/spec/features/tabulatrs_spec.rb +7 -2
- data/spec/lib/tabulatr/data/data_spec.rb +2 -2
- data/spec/lib/tabulatr/data/dsl_spec.rb +54 -4
- data/spec/lib/tabulatr/data/filtering_spec.rb +164 -7
- data/spec/lib/tabulatr/data/formatting_spec.rb +2 -2
- data/spec/lib/tabulatr/data/sorting_spec.rb +6 -6
- data/spec/lib/tabulatr/params_builder_spec.rb +19 -0
- data/spec/lib/tabulatr/renderer/association_spec.rb +29 -0
- data/spec/lib/tabulatr/renderer/renderer_spec.rb +8 -0
- data/spec/rails_helper.rb +4 -0
- metadata +13 -3
data/lib/tabulatr/data/dsl.rb
CHANGED
@@ -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
|
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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
40
|
+
name: name,
|
61
41
|
klass: @base,
|
62
|
-
|
63
|
-
|
64
|
-
|
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,
|
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
|
-
|
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
|
-
|
56
|
+
name: name,
|
84
57
|
klass: assoc_klass.try(:klass),
|
85
|
-
|
86
|
-
|
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(
|
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
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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(
|
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
|
-
|
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
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
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(
|
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
|
-
|
143
|
-
|
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
|
-
|
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
|
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(
|
88
|
-
@relation = @relation.where("#{
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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(
|
94
|
+
def apply_like_condition(column, value)
|
126
95
|
like ||= Tabulatr::Utility.like_statement
|
127
|
-
apply_string_condition("#{
|
96
|
+
apply_string_condition("#{column.col_options.filter_sql} #{like} ?", "%#{value}%") if value.present?
|
128
97
|
end
|
129
98
|
|
130
|
-
def apply_range_condition(
|
131
|
-
apply_string_condition("#{
|
132
|
-
apply_string_condition("#{
|
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: [],
|
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,
|
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
|
30
|
-
|
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::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) &&
|
36
|
+
if v && v.respond_to?(:to_a) && name != :count
|
37
37
|
v.map(&:"#{name}")
|
38
38
|
else
|
39
39
|
v.try(name)
|
@@ -24,91 +24,29 @@
|
|
24
24
|
class Tabulatr::Renderer::Column
|
25
25
|
include ActiveModel::Model
|
26
26
|
|
27
|
-
attr_accessor *%i{name
|
28
|
-
|
29
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
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?
|
150
|
-
val.map do |v|
|
151
|
-
|
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].
|
83
|
+
typ = self.klass.columns_hash[self.name.to_s].type.to_sym rescue nil
|
184
84
|
case typ
|
185
|
-
when :integer
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
when
|
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
|