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