effective_datatables 2.12.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +632 -512
  3. data/app/assets/javascripts/dataTables/buttons/buttons.html5.js +176 -177
  4. data/app/assets/javascripts/dataTables/buttons/buttons.print.js +2 -0
  5. data/app/assets/javascripts/dataTables/buttons/dataTables.buttons.js +14 -14
  6. data/app/assets/javascripts/dataTables/dataTables.bootstrap.js +1 -1
  7. data/app/assets/javascripts/dataTables/jquery.dataTables.js +246 -217
  8. data/app/assets/javascripts/effective_datatables.js +2 -3
  9. data/app/assets/javascripts/effective_datatables/events.js.coffee +7 -0
  10. data/app/assets/javascripts/effective_datatables/filters.js.coffee +6 -0
  11. data/app/assets/javascripts/effective_datatables/initialize.js.coffee +42 -39
  12. data/app/assets/javascripts/effective_datatables/reset.js.coffee +7 -0
  13. data/app/assets/javascripts/vendor/jquery.delayedChange.js +1 -1
  14. data/app/assets/stylesheets/dataTables/dataTables.bootstrap.css +0 -1
  15. data/app/assets/stylesheets/effective_datatables.scss +1 -2
  16. data/app/assets/stylesheets/effective_datatables/{_scopes.scss → _filters.scss} +1 -1
  17. data/app/assets/stylesheets/effective_datatables/_overrides.scss +1 -1
  18. data/app/controllers/effective/datatables_controller.rb +2 -4
  19. data/app/helpers/effective_datatables_helper.rb +56 -91
  20. data/app/helpers/effective_datatables_private_helper.rb +55 -64
  21. data/app/models/effective/datatable.rb +103 -177
  22. data/app/models/effective/datatable_column.rb +28 -0
  23. data/app/models/effective/datatable_column_tool.rb +110 -0
  24. data/app/models/effective/datatable_dsl_tool.rb +28 -0
  25. data/app/models/effective/datatable_value_tool.rb +142 -0
  26. data/app/models/effective/effective_datatable/attributes.rb +25 -0
  27. data/app/models/effective/effective_datatable/collection.rb +38 -0
  28. data/app/models/effective/effective_datatable/compute.rb +154 -0
  29. data/app/models/effective/effective_datatable/cookie.rb +29 -0
  30. data/app/models/effective/effective_datatable/dsl.rb +14 -8
  31. data/app/models/effective/effective_datatable/dsl/bulk_actions.rb +5 -6
  32. data/app/models/effective/effective_datatable/dsl/charts.rb +7 -9
  33. data/app/models/effective/effective_datatable/dsl/datatable.rb +107 -57
  34. data/app/models/effective/effective_datatable/dsl/filters.rb +50 -0
  35. data/app/models/effective/effective_datatable/format.rb +157 -0
  36. data/app/models/effective/effective_datatable/hooks.rb +0 -18
  37. data/app/models/effective/effective_datatable/params.rb +34 -0
  38. data/app/models/effective/effective_datatable/resource.rb +108 -0
  39. data/app/models/effective/effective_datatable/state.rb +178 -0
  40. data/app/views/effective/datatables/_actions_column.html.haml +9 -42
  41. data/app/views/effective/datatables/_bulk_actions_column.html.haml +1 -1
  42. data/app/views/effective/datatables/_bulk_actions_dropdown.html.haml +2 -3
  43. data/app/views/effective/datatables/_chart.html.haml +1 -1
  44. data/app/views/effective/datatables/_datatable.html.haml +7 -25
  45. data/app/views/effective/datatables/_filters.html.haml +21 -0
  46. data/app/views/effective/datatables/_reset.html.haml +2 -0
  47. data/app/views/effective/datatables/_resource_column.html.haml +8 -0
  48. data/app/views/effective/datatables/index.html.haml +0 -1
  49. data/config/effective_datatables.rb +9 -32
  50. data/lib/effective_datatables.rb +2 -6
  51. data/lib/effective_datatables/engine.rb +1 -1
  52. data/lib/effective_datatables/version.rb +1 -1
  53. data/lib/generators/effective_datatables/install_generator.rb +2 -2
  54. metadata +39 -19
  55. data/app/assets/javascripts/dataTables/colreorder/dataTables.colReorder.js +0 -27
  56. data/app/assets/javascripts/dataTables/jszip/jszip.js +0 -9155
  57. data/app/assets/javascripts/effective_datatables/scopes.js.coffee +0 -9
  58. data/app/models/effective/active_record_datatable_tool.rb +0 -242
  59. data/app/models/effective/array_datatable_tool.rb +0 -97
  60. data/app/models/effective/effective_datatable/ajax.rb +0 -101
  61. data/app/models/effective/effective_datatable/charts.rb +0 -20
  62. data/app/models/effective/effective_datatable/dsl/scopes.rb +0 -23
  63. data/app/models/effective/effective_datatable/helpers.rb +0 -24
  64. data/app/models/effective/effective_datatable/options.rb +0 -309
  65. data/app/models/effective/effective_datatable/rendering.rb +0 -365
  66. data/app/views/effective/datatables/_scopes.html.haml +0 -21
@@ -7,24 +7,6 @@ module Effective
7
7
  collection
8
8
  end
9
9
 
10
- # Override this function to perform custom searching on a column
11
- def search_column(collection, table_column, search_term, sql_column_or_index)
12
- if table_column[:array_column]
13
- array_tool.search_column_with_defaults(collection, table_column, search_term, sql_column_or_index)
14
- else
15
- table_tool.search_column_with_defaults(collection, table_column, search_term, sql_column_or_index)
16
- end
17
- end
18
-
19
- # Override this function to perform custom ordering on a column
20
- # direction will be :asc or :desc
21
- def order_column(collection, table_column, direction, sql_column_or_index)
22
- if table_column[:array_column]
23
- array_tool.order_column_with_defaults(collection, table_column, direction, sql_column_or_index)
24
- else
25
- table_tool.order_column_with_defaults(collection, table_column, direction, sql_column_or_index)
26
- end
27
- end
28
10
  end
29
11
  end
30
12
  end
@@ -0,0 +1,34 @@
1
+ module Effective
2
+ module EffectiveDatatable
3
+ module Params
4
+
5
+ private
6
+
7
+ def datatables_ajax_request?
8
+ view && view.params[:draw] && view.params[:columns] && view.params[:id] == to_param
9
+ end
10
+
11
+ def params
12
+ return {} unless view.present?
13
+ @params ||= {}.tap do |params|
14
+ Rack::Utils.parse_query(URI(view.request.referer.presence || '/').query).each { |k, v| params[k.to_sym] = v }
15
+ view.params.each { |k, v| params[k.to_sym] = v }
16
+ end
17
+ end
18
+
19
+ def filter_params
20
+ params.select { |name, value| _filters.key?(name.to_sym) }
21
+ end
22
+
23
+ def scope_param
24
+ params[:scope].to_sym if params.key?(:scope)
25
+ end
26
+
27
+ def search_params
28
+ params.select do |name, value|
29
+ columns.key?(name) && (name != :id) && !value.kind_of?(Hash) && value.class.name != 'ActionController::Parameters'.freeze
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,108 @@
1
+ module Effective
2
+ module EffectiveDatatable
3
+ module Resource
4
+
5
+ def admin_namespace?
6
+ controller_namespace == 'admin'
7
+ end
8
+
9
+ def controller_namespace
10
+ @attributes[:controller_namespace]
11
+ end
12
+
13
+ private
14
+
15
+ # This looks at all the columns and figures out the as:
16
+ def load_resource!
17
+ @resource = Effective::Resource.new(collection_class, namespace: controller_namespace)
18
+
19
+ if active_record_collection?
20
+ columns.each do |name, opts|
21
+ opts[:as] ||= resource.sql_type(name)
22
+ opts[:sql_column] = (resource.sql_column(name) || false) if opts[:sql_column].nil?
23
+
24
+ case opts[:as]
25
+ when *resource.macros
26
+ opts[:resource] = Effective::Resource.new(resource.associated(name), namespace: controller_namespace)
27
+ opts[:sql_column] ||= name
28
+ when Class
29
+ if opts[:as].ancestors.include?(ActiveRecord::Base)
30
+ opts[:resource] = Effective::Resource.new(opts[:as], namespace: controller_namespace)
31
+ opts[:as] = :resource
32
+ opts[:sql_column] ||= name
33
+ end
34
+ when :effective_addresses
35
+ opts[:resource] = Effective::Resource.new(resource.associated(name), namespace: controller_namespace)
36
+ opts[:sql_column] = :effective_addresses
37
+ when :effective_roles
38
+ opts[:sql_column] = :effective_roles
39
+ when :string # This is the fallback
40
+ # Anything that doesn't belong to the model or the sql table, we assume is a SELECT SUM|AVG|RANK() as fancy
41
+ if (resource.table && resource.column(name).blank?)
42
+ opts[:sql_as_column] = true
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ if array_collection?
49
+ row = collection.first
50
+
51
+ columns.each do |name, opts|
52
+ if opts[:as].kind_of?(Class) && opts[:as].ancestors.include?(ActiveRecord::Base)
53
+ opts[:resource] = Effective::Resource.new(opts[:as], namespace: controller_namespace)
54
+ opts[:as] = :resource
55
+ elsif opts[:as] == nil
56
+ if (value = Array(row[opts[:index]]).first).kind_of?(ActiveRecord::Base)
57
+ opts[:resource] = Effective::Resource.new(value, namespace: controller_namespace)
58
+ opts[:as] = :resource
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ columns.each do |name, opts|
65
+ opts[:as] ||= :string
66
+ opts[:as] = :email if (opts[:as] == :string && name == :email)
67
+
68
+ opts[:partial] ||= '/effective/datatables/resource_column' if (opts[:resource] && opts[:as] != :effective_addresses)
69
+
70
+ opts[:col_class] = "col-#{opts[:as]} col-#{name.to_s.parameterize} #{opts[:col_class]}".strip
71
+ end
72
+
73
+ load_resource_search!
74
+ end
75
+
76
+ def load_resource_search!
77
+ columns.each do |name, opts|
78
+
79
+ case opts[:search]
80
+ when false
81
+ opts[:search] = { as: :null }; next
82
+ when Symbol
83
+ opts[:search] = { as: opts[:search] }
84
+ when Array, ActiveRecord::Relation
85
+ opts[:search] = { collection: opts[:search] }
86
+ end
87
+
88
+ search = opts[:search]
89
+
90
+ if search[:collection].kind_of?(ActiveRecord::Relation)
91
+ search[:collection] = search[:collection].map { |obj| [obj.to_s, obj.to_param] }
92
+ elsif search[:collection].kind_of?(Array)
93
+ search[:collection].each { |obj| obj[1] = 'nil' if obj[1] == nil }
94
+ end
95
+
96
+ search[:as] ||= :select if (search.key?(:collection) && opts[:as] != :belongs_to_polymorphic)
97
+ search[:fuzzy] = true unless search.key?(:fuzzy)
98
+
99
+ if array_collection? && opts[:resource].present?
100
+ search.reverse_merge!(resource.search_form_field(name, collection.first[opts[:index]]))
101
+ else
102
+ search.reverse_merge!(resource.search_form_field(name, opts[:as]))
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,178 @@
1
+ module Effective
2
+ module EffectiveDatatable
3
+ module State
4
+
5
+ def scope
6
+ state[:scope]
7
+ end
8
+ alias_method :current_scope, :scope
9
+ alias_method :scopes, :scope
10
+
11
+ def filter
12
+ state[:filter]
13
+ end
14
+ alias_method :filters, :filter
15
+
16
+ def display_length
17
+ state[:length]
18
+ end
19
+
20
+ def display_start
21
+ state[:start]
22
+ end
23
+
24
+ def order_direction
25
+ state[:order_dir]
26
+ end
27
+
28
+ def order_index
29
+ state[:order_index]
30
+ end
31
+
32
+ def order_name
33
+ state[:order_name]
34
+ end
35
+
36
+ def search
37
+ state[:search]
38
+ end
39
+
40
+ def page
41
+ state[:start].to_i / state[:length] + 1
42
+ end
43
+
44
+ def per_page
45
+ state[:length]
46
+ end
47
+
48
+ private
49
+
50
+ # This is called first. Our initial state is set.
51
+ def initial_state
52
+ {
53
+ filter: {},
54
+ length: nil,
55
+ order_name: nil,
56
+ order_dir: nil,
57
+ order_index: nil,
58
+ params: 0,
59
+ scope: nil,
60
+ start: 0,
61
+ search: {},
62
+ visible: {}
63
+ }
64
+ end
65
+
66
+ def load_filters!
67
+ state[:filter] = _filters.inject({}) { |h, (name, opts)| h[name] = opts[:value]; h }
68
+ state[:scope] = _scopes.find { |_, opts| opts[:default] }.try(:first) || _scopes.keys.first
69
+ end
70
+
71
+ def load_filter_params!
72
+ filter_params.each { |name, value| state[:filter][name] = parse_filter_value(_filters[name], value) }
73
+ state[:scope] = scope_param if scope_param
74
+ end
75
+
76
+ def fill_empty_filters!
77
+ state[:filter].each do |name, value|
78
+ next unless (value.nil? && _filters[name][:required])
79
+ state[:filter][name] = _filters[name][:value]
80
+ end
81
+ end
82
+
83
+ def load_state!
84
+ if datatables_ajax_request?
85
+ load_filter_params!
86
+ load_ajax_state!
87
+ elsif cookie.present? && cookie[:state][:params] == params.length
88
+ load_cookie_state!
89
+ else
90
+ # Nothing to do for default state
91
+ end
92
+
93
+ load_filter_params! unless datatables_ajax_request?
94
+ fill_empty_filters!
95
+ end
96
+
97
+ def load_ajax_state!
98
+ state[:length] = params[:length].to_i
99
+
100
+ state[:order_dir] = (params[:order]['0'][:dir] == 'desc' ? :desc : :asc)
101
+ state[:order_index] = params[:order]['0'][:column].to_i
102
+
103
+ state[:scope] = _scopes.keys.find { |name| params[:scope] == name.to_s }
104
+ state[:start] = params[:start].to_i
105
+
106
+ state[:search] = {}
107
+ state[:visible] = {}
108
+
109
+ params[:columns].values.each do |params|
110
+ name = params[:name].to_sym
111
+
112
+ if params[:search][:value].present? && !['null'].include?(params[:search][:value])
113
+ state[:search][name] = params[:search][:value]
114
+ end
115
+
116
+ state[:visible][name] = (params[:visible] == 'true')
117
+ end
118
+
119
+ (params[:filter] || {}).each do |name, value|
120
+ name = name.to_sym
121
+ raise "unexpected filter name: #{name}" unless _filters.key?(name)
122
+
123
+ state[:filter][name] = parse_filter_value(_filters[name], value)
124
+ end
125
+
126
+ state[:params] = cookie[:state][:params]
127
+ end
128
+
129
+ def load_cookie_state!
130
+ @state = cookie[:state]
131
+ end
132
+
133
+ def load_columns!
134
+ state[:length] ||= EffectiveDatatables.default_length
135
+
136
+ if columns.present?
137
+ if order_index.present?
138
+ state[:order_name] = columns.keys[order_index]
139
+ end
140
+
141
+ state[:order_name] ||= columns.find { |name, opts| opts[:sort] }.first
142
+ raise "order column :#{order_name} must exist as a col or val" unless columns[order_name]
143
+
144
+ state[:order_index] = columns[order_name][:index]
145
+ end
146
+
147
+ # Set default order direction
148
+ state[:order_dir] ||= ['_at', '_on', 'date'].any? { |str| order_name.to_s.end_with?(str) } ? :desc : :asc
149
+
150
+ if state[:search].blank?
151
+ columns.each do |name, opts|
152
+ state[:search][name] = opts[:search][:value] if opts[:search].kind_of?(Hash) && opts[:search].key?(:value)
153
+ end
154
+ end
155
+
156
+ columns.each do |name, opts|
157
+ state[:visible][name] = opts[:visible] unless state[:visible].key?(name)
158
+ end
159
+
160
+ unless datatables_ajax_request?
161
+ search_params.each { |name, value| state[:search][name] = value }
162
+ state[:params] = params.length
163
+ end
164
+
165
+ state[:visible].delete_if { |name, _| columns.key?(name) == false }
166
+ state[:search].delete_if { |name, _| columns.key?(name) == false }
167
+ end
168
+
169
+ # The incoming value could be from the passed page params or from the AJAX request.
170
+ # When we parse an incoming filter term for this filter.
171
+ def parse_filter_value(filter, value)
172
+ return filter[:parse].call(value) if filter[:parse]
173
+ Effective::Attribute.new(filter[:value]).parse(value, name: filter[:name])
174
+ end
175
+
176
+ end
177
+ end
178
+ end
@@ -1,44 +1,11 @@
1
- :ruby
2
- if show_action == :authorize_each
3
- show_action = (EffectiveDatatables.authorized?(controller, :show, resource) rescue false)
4
- elsif show_action.respond_to?(:call)
5
- show_action = instance_exec(resource, &show_action)
6
- end
1
+ - if show_path
2
+ - if show_action == true || (EffectiveDatatables.authorized?(controller, :show, resource) rescue false)
3
+ = show_icon_to send(show_path, resource.to_param)
7
4
 
8
- if edit_action == :authorize_each
9
- edit_action = (EffectiveDatatables.authorized?(controller, :edit, resource) rescue false)
10
- elsif edit_action.respond_to?(:call)
11
- edit_action = instance_exec(resource, &edit_action)
12
- end
5
+ - if edit_path
6
+ - if edit_action == true || (EffectiveDatatables.authorized?(controller, :edit, resource) rescue false)
7
+ = edit_icon_to send(edit_path, resource.to_param)
13
8
 
14
- if destroy_action == :authorize_each
15
- destroy_action = (EffectiveDatatables.authorized?(controller, :destroy, resource) rescue false)
16
- elsif destroy_action.respond_to?(:call)
17
- destroy_action = instance_exec(resource, &destroy_action)
18
- end
19
-
20
- if unarchive_action == :authorize_each
21
- unarchive_action = (EffectiveDatatables.authorized?(controller, :unarchive, resource) rescue false)
22
- elsif unarchive_action.respond_to?(:call)
23
- unarchive_action = instance_exec(resource, &unarchive_action)
24
- end
25
-
26
- - if show_action && defined?(show_path)
27
- = show_icon_to show_path.gsub(':to_param', resource.to_param)
28
-
29
- - if edit_action && defined?(edit_path)
30
- = edit_icon_to edit_path.gsub(':to_param', resource.to_param)
31
-
32
- - if destroy_action && defined?(destroy_path)
33
- - if resource.respond_to?(:archived?) && !resource.archived?
34
- = archive_icon_to destroy_path.gsub(':to_param', resource.to_param)
35
- - elsif resource.respond_to?(:archived?) == false
36
- = destroy_icon_to destroy_path.gsub(':to_param', resource.to_param)
37
-
38
- - if unarchive_action && defined?(unarchive_path)
39
- - if resource.respond_to?(:archived?) && resource.archived?
40
- = unarchive_icon_to unarchive_path.gsub(':to_param', resource.to_param)
41
-
42
- - if actions_block
43
- = capture do
44
- - instance_exec(resource, &actions_block)
9
+ - if destroy_path
10
+ - if destroy_action == true || (EffectiveDatatables.authorized?(controller, :destroy, resource) rescue false)
11
+ = destroy_icon_to send(destroy_path, resource.to_param)
@@ -1,2 +1,2 @@
1
- - id = (resource.public_send(resource_method) rescue resource.object_id)
1
+ - id = (resource.try(:to_param) || resource.try(:id) || resource.object_id)
2
2
  = check_box_tag 'bulk_actions_resources[]', id, false, autocomplete: 'off', id: "bulk_actions_resource_#{id}", data: { role: 'bulk-actions-resource' }, onClick: 'event.stopPropagation();'
@@ -3,9 +3,8 @@
3
3
  Bulk Actions
4
4
  %span.caret
5
5
  %ul.dropdown-menu
6
- - if dropdown_block.respond_to?(:call)
7
- = capture do
8
- - instance_exec(&dropdown_block)
6
+ - if datatable._bulk_actions.present?
7
+ = datatable._bulk_actions.join.html_safe
9
8
  - else
10
9
  %li
11
10
  %a{href: '#'} No bulk actions
@@ -1 +1 @@
1
- .effective-datatables-chart{id: "#{chart[:name].to_s.parameterize}-chart-#{Time.zone.now.nsec}", data: {name: chart[:name], type: chart[:type], options: chart[:options].to_json, data: chart[:data].to_json}}
1
+ .effective-datatables-chart{id: "#{chart[:name].to_s.parameterize}-chart-#{Time.zone.now.nsec}", data: { name: chart[:name], as: chart[:as], options: chart[:options].to_json, data: chart_data.to_json }}
@@ -1,36 +1,18 @@
1
- :ruby
2
- effective_datatable_params = {
3
- id: "#{datatable.to_param}-table-#{Time.zone.now.nsec}",
4
- class: "#{datatable.table_html_class}",
5
- data: {
6
- 'effective-form-inputs' => defined?(EffectiveFormInputs),
7
- 'bulk-actions' => datatable_bulk_actions(datatable),
8
- 'columns' => datatable_columns(datatable),
9
- 'input-js-options' => local_assigns[:input_js_options],
10
- 'simple' => datatable.simple?.to_s,
11
- 'source' => effective_datatables.datatable_path(datatable, {format: 'json'}.merge(attributes: datatable.attributes)).chomp('?'),
12
- 'default-order' => datatable_default_order(datatable),
13
- 'display-entries' => datatable.display_entries,
14
- 'display-records' => (datatable.to_json[:recordsFiltered] || 0),
15
- 'total-records' => (datatable.to_json[:recordsTotal] || 0)
16
- }
17
- }
18
-
19
1
  %table.effective-datatable{effective_datatable_params}
20
2
  %thead
21
- - if datatable.table_columns.any? { |_, opts| opts[:th].present? } == false
3
+ - if datatable.columns.any? { |_, opts| opts[:th].present? } == false
22
4
  %tr
23
- - datatable.table_columns.each do |name, opts|
5
+ - datatable.columns.each do |name, opts|
24
6
  %th= opts[:label] || name
25
7
  - else
26
- - max_depth = datatable.table_columns.map { |_, opts| opts[:th].try(:[], :depth) || 0 }.max
8
+ - max_depth = datatable.columns.map { |_, opts| opts[:th].try(:[], :depth) || 0 }.max
27
9
  - [*0..max_depth].each do |depth|
28
10
  %tr
29
- - table_columns = datatable.table_columns.select { |_, opts| (opts[:th].try(:[], :depth) || 0) == depth }
30
- - table_columns.each do |name, opts|
11
+ - columns = datatable.columns.select { |_, opts| (opts[:th].try(:[], :depth) || 0) == depth }
12
+ - columns.each do |name, opts|
31
13
  %th{(opts[:th] || {}).merge(title: (opts[:label] || name))}
32
14
  = opts[:label] || name
33
- - (opts[:append_th] || []).each do |faux_col|
15
+ - (opts[:th_append] || []).each do |faux_col|
34
16
  %th{(faux_col[:th] || {}).merge(title: faux_col[:label])}
35
17
  = faux_col[:label]
36
18
 
@@ -40,7 +22,7 @@
40
22
  - row.each do |col|
41
23
  %td= col.to_s.html_safe
42
24
 
43
- - if datatable.aggregates.present?
25
+ - if datatable.to_json[:aggregates].present?
44
26
  %tfoot
45
27
  - datatable.to_json[:aggregates].each do |row|
46
28
  %tr