effective_datatables 3.6.3 → 3.7.0
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/MIT-LICENSE +1 -1
- data/app/assets/javascripts/dataTables/locales/en.lang +33 -0
- data/app/assets/javascripts/dataTables/locales/es.lang +36 -0
- data/app/assets/javascripts/dataTables/locales/nl.lang +30 -0
- data/app/assets/javascripts/effective_datatables/filters.js.coffee +1 -0
- data/app/assets/javascripts/effective_datatables/flash.js.coffee +31 -0
- data/app/assets/javascripts/effective_datatables/initialize.js.coffee +41 -53
- data/app/assets/javascripts/effective_datatables/inline_crud.js.coffee +217 -0
- data/app/assets/javascripts/effective_datatables/overrides.js.coffee +7 -0
- data/app/assets/javascripts/effective_datatables/reorder.js.coffee +43 -0
- data/app/assets/javascripts/effective_datatables/reset.js.coffee +1 -1
- data/app/assets/stylesheets/effective_datatables/_overrides.scss +28 -0
- data/app/controllers/effective/datatables_controller.rb +39 -6
- data/app/datatables/effective_style_guide_datatable.rb +47 -0
- data/app/helpers/effective_datatables_helper.rb +49 -56
- data/app/helpers/effective_datatables_private_helper.rb +137 -11
- data/app/models/effective/datatable.rb +36 -16
- data/app/models/effective/datatable_column.rb +1 -0
- data/app/models/effective/datatable_value_tool.rb +20 -20
- data/app/models/effective/effective_datatable/attributes.rb +5 -13
- data/app/models/effective/effective_datatable/collection.rb +18 -3
- data/app/models/effective/effective_datatable/compute.rb +15 -6
- data/app/models/effective/effective_datatable/cookie.rb +19 -18
- data/app/models/effective/effective_datatable/dsl.rb +8 -3
- data/app/models/effective/effective_datatable/dsl/bulk_actions.rb +16 -23
- data/app/models/effective/effective_datatable/dsl/datatable.rb +70 -28
- data/app/models/effective/effective_datatable/dsl/filters.rb +12 -4
- data/app/models/effective/effective_datatable/format.rb +1 -4
- data/app/models/effective/effective_datatable/params.rb +9 -4
- data/app/models/effective/effective_datatable/resource.rb +129 -74
- data/app/models/effective/effective_datatable/state.rb +30 -15
- data/app/views/effective/datatables/_bulk_actions_dropdown.html.haml +3 -5
- data/app/views/effective/datatables/_datatable.html.haml +3 -5
- data/app/views/effective/datatables/_filters.html.haml +4 -24
- data/app/views/effective/datatables/_reorder_column.html.haml +5 -0
- data/app/views/effective/style_guide/_effective_datatables.html.haml +1 -0
- data/config/effective_datatables.rb +8 -21
- data/config/locales/en.yml +12 -0
- data/config/locales/es.yml +12 -0
- data/config/locales/nl.yml +12 -0
- data/config/routes.rb +5 -4
- data/lib/effective_datatables.rb +49 -2
- data/lib/effective_datatables/engine.rb +4 -2
- data/lib/effective_datatables/version.rb +1 -1
- metadata +17 -5
- data/app/views/effective/datatables/_reset.html.haml +0 -2
@@ -15,6 +15,8 @@ module Effective
|
|
15
15
|
|
16
16
|
# The collection itself. Only evaluated once.
|
17
17
|
attr_accessor :_collection
|
18
|
+
attr_accessor :_collection_apply_belongs_to
|
19
|
+
attr_accessor :_collection_apply_scope
|
18
20
|
|
19
21
|
# The view
|
20
22
|
attr_reader :view
|
@@ -31,10 +33,10 @@ module Effective
|
|
31
33
|
include Effective::EffectiveDatatable::Resource
|
32
34
|
include Effective::EffectiveDatatable::State
|
33
35
|
|
34
|
-
def initialize(view = nil, attributes =
|
36
|
+
def initialize(view = nil, attributes = nil)
|
35
37
|
(attributes = view; view = nil) if view.kind_of?(Hash)
|
36
38
|
|
37
|
-
@attributes =
|
39
|
+
@attributes = (attributes || {})
|
38
40
|
@state = initial_state
|
39
41
|
|
40
42
|
@_aggregates = {}
|
@@ -45,6 +47,7 @@ module Effective
|
|
45
47
|
@_form = {}
|
46
48
|
@_scopes = {}
|
47
49
|
|
50
|
+
raise 'expected a hash of arguments' unless @attributes.kind_of?(Hash)
|
48
51
|
raise 'collection is defined as a method. Please use the collection do ... end syntax.' unless collection.nil?
|
49
52
|
self.view = view if view
|
50
53
|
end
|
@@ -54,21 +57,23 @@ module Effective
|
|
54
57
|
@view = (view.respond_to?(:view_context) ? view.view_context : view)
|
55
58
|
raise 'expected view to respond to params' unless @view.respond_to?(:params)
|
56
59
|
|
57
|
-
|
60
|
+
assert_attributes!
|
58
61
|
load_attributes!
|
59
62
|
|
60
63
|
# We need early access to filter and scope, to define defaults from the model first
|
61
|
-
# This means filters do
|
64
|
+
# This means filters do know about attributes but not about columns.
|
62
65
|
initialize_filters if respond_to?(:initialize_filters)
|
63
66
|
load_filters!
|
64
67
|
load_state!
|
65
68
|
|
69
|
+
# Bulk actions called first so it can add the bulk_actions_col first
|
70
|
+
initialize_bulk_actions if respond_to?(:initialize_bulk_actions)
|
71
|
+
|
66
72
|
# Now we initialize all the columns. columns knows about attributes and filters and scope
|
67
73
|
initialize_datatable if respond_to?(:initialize_datatable)
|
68
74
|
load_columns!
|
69
75
|
|
70
76
|
# Execute any additional DSL methods
|
71
|
-
initialize_bulk_actions if respond_to?(:initialize_bulk_actions)
|
72
77
|
initialize_charts if respond_to?(:initialize_charts)
|
73
78
|
|
74
79
|
# Load the collection. This is the first time def collection is called on the Datatable itself
|
@@ -78,9 +83,10 @@ module Effective
|
|
78
83
|
# Figure out the class, and if it's activerecord, do all the resource discovery on it
|
79
84
|
load_resource!
|
80
85
|
|
81
|
-
#
|
82
|
-
|
86
|
+
# Check everything is okay
|
87
|
+
validate_datatable!
|
83
88
|
|
89
|
+
# Save for next time
|
84
90
|
save_cookie!
|
85
91
|
end
|
86
92
|
|
@@ -117,11 +123,18 @@ module Effective
|
|
117
123
|
)
|
118
124
|
end
|
119
125
|
|
120
|
-
#
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
126
|
+
# Inline crud
|
127
|
+
def inline?
|
128
|
+
attributes[:inline] == true
|
129
|
+
end
|
130
|
+
|
131
|
+
# Reordering
|
132
|
+
def reorder?
|
133
|
+
columns.key?(:_reorder)
|
134
|
+
end
|
135
|
+
|
136
|
+
def sortable?
|
137
|
+
!reorder? && attributes[:sortable] != false
|
125
138
|
end
|
126
139
|
|
127
140
|
# Whether the filters must be rendered as a <form> or we can keep the normal <div> behaviour
|
@@ -129,12 +142,12 @@ module Effective
|
|
129
142
|
_form[:verb].present?
|
130
143
|
end
|
131
144
|
|
132
|
-
def
|
133
|
-
attributes[:class] || EffectiveDatatables.html_class
|
145
|
+
def html_class
|
146
|
+
Array(attributes[:class] || EffectiveDatatables.html_class).join(' ').presence
|
134
147
|
end
|
135
148
|
|
136
149
|
def to_param
|
137
|
-
|
150
|
+
"#{self.class.name.underscore.parameterize}-#{[self.class, attributes].hash.abs.to_s.last(12)}"
|
138
151
|
end
|
139
152
|
|
140
153
|
def columns
|
@@ -150,7 +163,7 @@ module Effective
|
|
150
163
|
end
|
151
164
|
|
152
165
|
def resource
|
153
|
-
|
166
|
+
raise('depecated. Please use .effective_resource instead')
|
154
167
|
end
|
155
168
|
|
156
169
|
def fallback_effective_resource
|
@@ -167,5 +180,12 @@ module Effective
|
|
167
180
|
@value_tool ||= DatatableValueTool.new(self)
|
168
181
|
end
|
169
182
|
|
183
|
+
def validate_datatable!
|
184
|
+
if reorder?
|
185
|
+
raise 'cannot use reorder with an Array collection' unless active_record_collection?
|
186
|
+
raise 'cannot use reorder with a non-Integer column' if effective_resource.sql_type(columns[:_reorder][:reorder]) != :integer
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
170
190
|
end
|
171
191
|
end
|
@@ -74,13 +74,12 @@ module Effective
|
|
74
74
|
collection
|
75
75
|
end
|
76
76
|
|
77
|
-
def search_column(collection,
|
78
|
-
Rails.logger.info "VALUE TOOL: search_column #{column.to_s} #{
|
77
|
+
def search_column(collection, original, column, index)
|
78
|
+
Rails.logger.info "VALUE TOOL: search_column #{column.to_s} #{original} #{index}" if EffectiveDatatables.debug
|
79
79
|
|
80
|
-
macros = Effective::Resource.new('').macros
|
81
80
|
fuzzy = column[:search][:fuzzy]
|
82
81
|
|
83
|
-
term = Effective::Attribute.new(column[:as]).parse(
|
82
|
+
term = Effective::Attribute.new(column[:as]).parse(original, name: column[:name])
|
84
83
|
term_downcased = term.to_s.downcase
|
85
84
|
|
86
85
|
# term == 'nil' rescue false is a Rails 4.1 fix, where you can't compare a TimeWithZone to 'nil'
|
@@ -90,7 +89,8 @@ module Effective
|
|
90
89
|
|
91
90
|
# See effective_resources gem search() method # relation.rb
|
92
91
|
collection.select! do |row|
|
93
|
-
obj =
|
92
|
+
obj = row[index]
|
93
|
+
value = obj_to_value(row[index], column, row)
|
94
94
|
|
95
95
|
case column[:as]
|
96
96
|
when :boolean
|
@@ -101,7 +101,7 @@ module Effective
|
|
101
101
|
end
|
102
102
|
when :datetime, :date
|
103
103
|
end_at = (
|
104
|
-
case (
|
104
|
+
case (original.to_s.scan(/(\d+)/).flatten).length
|
105
105
|
when 1 ; term.end_of_year # Year
|
106
106
|
when 2 ; term.end_of_month # Year-Month
|
107
107
|
when 3 ; term.end_of_day # Year-Month-Day
|
@@ -111,35 +111,35 @@ module Effective
|
|
111
111
|
else term
|
112
112
|
end
|
113
113
|
)
|
114
|
-
|
114
|
+
value >= term && value <= end_at
|
115
115
|
when :time
|
116
|
-
(
|
116
|
+
(value.hour == term.hour) && (term.min == 0 ? true : (value.min == term.min))
|
117
117
|
when :decimal, :currency
|
118
|
-
if fuzzy && (term.round(0) == term) &&
|
118
|
+
if fuzzy && (term.round(0) == term) && original.to_s.include?('.') == false
|
119
119
|
if term < 0
|
120
|
-
|
120
|
+
value <= term && value > (term - 1.0)
|
121
121
|
else
|
122
|
-
|
122
|
+
value >= term && value < (term + 1.0)
|
123
123
|
end
|
124
124
|
else
|
125
|
-
|
125
|
+
value == term
|
126
126
|
end
|
127
127
|
when :duration
|
128
|
-
if fuzzy && (term % 60 == 0) &&
|
128
|
+
if fuzzy && (term % 60 == 0) && original.to_s.include?('m') == false
|
129
129
|
if term < 0
|
130
|
-
|
130
|
+
value <= term && value > (term - 60)
|
131
131
|
else
|
132
|
-
|
132
|
+
value >= term && value < (term + 60)
|
133
133
|
end
|
134
134
|
else
|
135
|
-
|
135
|
+
value == term
|
136
136
|
end
|
137
|
-
when *
|
137
|
+
when *datatable.association_macros, :resource
|
138
138
|
Array(obj).any? do |resource|
|
139
139
|
Array(term).any? do |term|
|
140
140
|
matched = false
|
141
141
|
|
142
|
-
if term.kind_of?(Integer) && resource.respond_to?(:to_param)
|
142
|
+
if term.kind_of?(Integer) && resource.respond_to?(:id) && resource.respond_to?(:to_param)
|
143
143
|
matched = (resource.id == term || resource.to_param == term)
|
144
144
|
end
|
145
145
|
|
@@ -148,9 +148,9 @@ module Effective
|
|
148
148
|
end
|
149
149
|
else # :string, :text, :email
|
150
150
|
if fuzzy
|
151
|
-
|
151
|
+
value.to_s.downcase.include?(term_downcased)
|
152
152
|
else
|
153
|
-
|
153
|
+
value == term || (value.to_s == term.to_s)
|
154
154
|
end
|
155
155
|
end
|
156
156
|
end || collection
|
@@ -4,22 +4,14 @@ module Effective
|
|
4
4
|
|
5
5
|
private
|
6
6
|
|
7
|
-
def
|
8
|
-
|
9
|
-
|
7
|
+
def assert_attributes!
|
8
|
+
if datatables_ajax_request? || datatables_inline_request?
|
9
|
+
raise 'expected attributes to be present' unless attributes.present?
|
10
|
+
end
|
10
11
|
end
|
11
12
|
|
12
13
|
def load_attributes!
|
13
|
-
|
14
|
-
raise 'expected cookie to be present' unless cookie
|
15
|
-
raise 'expected attributes cookie to be present' unless cookie[:attributes]
|
16
|
-
|
17
|
-
@attributes = cookie.delete(:attributes)
|
18
|
-
end
|
19
|
-
|
20
|
-
unless datatables_ajax_request?
|
21
|
-
@attributes[:_n] ||= view.controller_path.split('/')[0...-1].join('/').presence
|
22
|
-
end
|
14
|
+
@attributes[:namespace] ||= view.controller_path.split('/')[0...-1].join('/')
|
23
15
|
end
|
24
16
|
|
25
17
|
end
|
@@ -7,10 +7,23 @@ module Effective
|
|
7
7
|
@collection_class # Will be either User/Post/etc or Array
|
8
8
|
end
|
9
9
|
|
10
|
+
# User.all
|
10
11
|
def active_record_collection?
|
11
12
|
@active_record_collection == true
|
12
13
|
end
|
13
14
|
|
15
|
+
# [User<1>, User<2>, Post<1>, Page<3>]
|
16
|
+
def active_record_array_collection?
|
17
|
+
@active_record_array_collection == true
|
18
|
+
end
|
19
|
+
|
20
|
+
def active_record_polymorphic_array_collection?
|
21
|
+
return false unless active_record_array_collection?
|
22
|
+
return @active_record_polymorphic_array_collection unless @active_record_polymorphic_array_collection.nil?
|
23
|
+
@active_record_polymorphic_array_collection = collection.map { |obj| obj.class }.uniq.length > 1
|
24
|
+
end
|
25
|
+
|
26
|
+
# [[1, 'foo'], [2, 'bar']]
|
14
27
|
def array_collection?
|
15
28
|
@array_collection == true
|
16
29
|
end
|
@@ -21,11 +34,13 @@ module Effective
|
|
21
34
|
raise 'No collection defined. Please add a collection with collection do ... end' if collection.nil?
|
22
35
|
|
23
36
|
@collection_class = (collection.respond_to?(:klass) ? collection.klass : self.class)
|
37
|
+
|
24
38
|
@active_record_collection = (collection.ancestors.include?(ActiveRecord::Base) rescue false)
|
25
|
-
@
|
39
|
+
@active_record_array_collection = collection.kind_of?(Array) && collection.present? && collection.first.kind_of?(ActiveRecord::Base)
|
40
|
+
@array_collection = collection.kind_of?(Array) && (collection.blank? || collection.first.kind_of?(Array))
|
26
41
|
|
27
|
-
unless active_record_collection? || array_collection?
|
28
|
-
raise "Unsupported collection
|
42
|
+
unless active_record_collection? || active_record_array_collection? || array_collection?
|
43
|
+
raise "Unsupported collection. Expecting an ActiveRecord relation, an Array of ActiveRecord objects, or an Array of Arrays [[1, 'foo'], [2, 'bar']]"
|
29
44
|
end
|
30
45
|
|
31
46
|
_scopes.each do |scope, _|
|
@@ -14,11 +14,14 @@ module Effective
|
|
14
14
|
@total_records = (active_record_collection? ? column_tool.size(col) : value_tool.size(col))
|
15
15
|
|
16
16
|
# Apply scope
|
17
|
-
col = column_tool.scope(col)
|
17
|
+
col = column_tool.scope(col) if @_collection_apply_scope
|
18
18
|
|
19
19
|
# Apply column searching
|
20
20
|
col = column_tool.search(col)
|
21
|
-
|
21
|
+
|
22
|
+
unless value_tool.searched.present? || (column_tool.scoped.blank? && column_tool.searched.blank?)
|
23
|
+
@display_records = column_tool.size(col)
|
24
|
+
end
|
22
25
|
|
23
26
|
# Apply column ordering
|
24
27
|
col = column_tool.order(col)
|
@@ -61,11 +64,15 @@ module Effective
|
|
61
64
|
if state[:visible][name] == false && (name != order_name) # Sort by invisible array column
|
62
65
|
BLANK
|
63
66
|
elsif opts[:compute]
|
64
|
-
|
67
|
+
if array_collection?
|
68
|
+
dsl_tool.instance_exec(obj, obj[opts[:index]], &opts[:compute])
|
69
|
+
else
|
70
|
+
dsl_tool.instance_exec(obj, collection, &opts[:compute])
|
71
|
+
end
|
65
72
|
elsif (opts[:partial] || opts[:format])
|
66
|
-
|
73
|
+
array_collection? ? obj[opts[:index]] : obj
|
67
74
|
elsif opts[:resource]
|
68
|
-
resource =
|
75
|
+
resource = array_collection? ? obj[opts[:index]] : obj
|
69
76
|
|
70
77
|
if opts[:resource_field]
|
71
78
|
(associated, field) = name.to_s.split('.').first(2)
|
@@ -128,9 +135,11 @@ module Effective
|
|
128
135
|
length = values.length
|
129
136
|
values = values.reject { |value| value.nil? }
|
130
137
|
|
138
|
+
return BLANK if [:id, :year].include?(column[:name])
|
139
|
+
|
131
140
|
case aggregate[:name]
|
132
141
|
when :total
|
133
|
-
if [:
|
142
|
+
if [:percent].include?(column[:as])
|
134
143
|
BLANK
|
135
144
|
elsif values.all? { |value| value.kind_of?(Numeric) }
|
136
145
|
values.sum
|
@@ -5,26 +5,16 @@ module Effective
|
|
5
5
|
@cookie
|
6
6
|
end
|
7
7
|
|
8
|
-
def cookie_key
|
9
|
-
@cookie_key ||= (datatables_ajax_request? ? view.params[:cookie] : cookie_param)
|
10
|
-
end
|
11
|
-
|
12
|
-
# All possible dt cookie keys. Used to make sure the datatable has a cookie set for this session.
|
13
|
-
def cookie_keys
|
14
|
-
@cookie_keys ||= Array(@dt_cookie).compact.map(&:first)
|
15
|
-
end
|
16
|
-
|
17
|
-
def cookie_param
|
18
|
-
[self.class, attributes].hash.abs.to_s.last(12) # Not guaranteed to be 12 long
|
19
|
-
end
|
20
|
-
|
21
8
|
private
|
22
9
|
|
23
10
|
def load_cookie!
|
11
|
+
return unless EffectiveDatatables.save_state
|
12
|
+
|
24
13
|
@dt_cookie = view.cookies.signed['_effective_dt']
|
25
14
|
|
26
15
|
# Load global datatables cookie
|
27
16
|
if @dt_cookie.present?
|
17
|
+
|
28
18
|
@dt_cookie = Marshal.load(Base64.decode64(@dt_cookie))
|
29
19
|
raise 'invalid datatables cookie' unless @dt_cookie.kind_of?(Array)
|
30
20
|
|
@@ -37,21 +27,33 @@ module Effective
|
|
37
27
|
if @cookie.kind_of?(Array)
|
38
28
|
@cookie = initial_state.keys.zip(@cookie.second).to_h
|
39
29
|
end
|
30
|
+
|
40
31
|
end
|
41
32
|
|
42
33
|
def save_cookie!
|
34
|
+
return unless EffectiveDatatables.save_state
|
35
|
+
|
43
36
|
@dt_cookie ||= []
|
44
37
|
@dt_cookie << [cookie_key, cookie_payload]
|
45
38
|
|
46
|
-
while @dt_cookie.to_s.size > EffectiveDatatables.
|
39
|
+
while @dt_cookie.to_s.size > EffectiveDatatables.cookie_max_size.to_i
|
47
40
|
@dt_cookie.shift((@dt_cookie.length / 3) + 1)
|
48
41
|
end
|
49
42
|
|
50
|
-
|
43
|
+
# Generate cookie
|
44
|
+
domain = EffectiveDatatables.cookie_domain || :all
|
45
|
+
tld_length = EffectiveDatatables.cookie_tld_length
|
46
|
+
tld_length ||= (view.request.host == 'localhost' ? nil : view.request.host.to_s.split('.').count)
|
47
|
+
|
48
|
+
view.cookies.signed['_effective_dt'] = { value: Base64.encode64(Marshal.dump(@dt_cookie)), domain: domain, tld_length: tld_length }.compact
|
49
|
+
end
|
50
|
+
|
51
|
+
def cookie_key
|
52
|
+
@cookie_key ||= to_param
|
51
53
|
end
|
52
54
|
|
53
55
|
def cookie_payload
|
54
|
-
payload = state.except(:
|
56
|
+
payload = state.except(:visible)
|
55
57
|
|
56
58
|
# Turn visible into a bitmask. This is undone in load_columns!
|
57
59
|
payload[:vismask] = (
|
@@ -60,8 +62,7 @@ module Effective
|
|
60
62
|
end
|
61
63
|
)
|
62
64
|
|
63
|
-
# Just store the values
|
64
|
-
[attributes.delete_if { |k, v| v.nil? }] + payload.values
|
65
|
+
payload.values # Just store the values
|
65
66
|
end
|
66
67
|
|
67
68
|
end
|
@@ -3,15 +3,20 @@ module Effective
|
|
3
3
|
module Dsl
|
4
4
|
|
5
5
|
def bulk_actions(&block)
|
6
|
-
define_method('initialize_bulk_actions') { dsl_tool.instance_exec(&block) }
|
6
|
+
define_method('initialize_bulk_actions') { dsl_tool.instance_exec(&block); dsl_tool.bulk_actions_col }
|
7
7
|
end
|
8
8
|
|
9
9
|
def charts(&block)
|
10
10
|
define_method('initialize_charts') { dsl_tool.instance_exec(&block) }
|
11
11
|
end
|
12
12
|
|
13
|
-
def collection(&block)
|
14
|
-
define_method('initialize_collection') {
|
13
|
+
def collection(apply_belongs_to: true, apply_scope: true, &block)
|
14
|
+
define_method('initialize_collection') {
|
15
|
+
self._collection_apply_belongs_to = apply_belongs_to
|
16
|
+
self._collection_apply_scope = apply_scope
|
17
|
+
|
18
|
+
self._collection = dsl_tool.instance_exec(&block)
|
19
|
+
}
|
15
20
|
end
|
16
21
|
|
17
22
|
def datatable(&block)
|