filterrific 5.2.3 → 5.2.4
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/CHANGELOG.md +5 -0
- data/Rakefile +7 -7
- data/app/assets/javascripts/filterrific/filterrific.js +172 -0
- data/lib/filterrific/action_controller_extension.rb +14 -17
- data/lib/filterrific/action_view_extension.rb +33 -34
- data/lib/filterrific/active_record_extension.rb +6 -10
- data/lib/filterrific/engine.rb +5 -9
- data/lib/filterrific/has_reset_filterrific_url_mixin.rb +1 -4
- data/lib/filterrific/param_set.rb +18 -26
- data/lib/filterrific/version.rb +1 -3
- data/lib/filterrific.rb +2 -4
- data/spec/filterrific/action_controller_extension_spec.rb +78 -80
- data/spec/filterrific/action_view_extension_spec.rb +9 -12
- data/spec/filterrific/active_record_extension_spec.rb +24 -38
- data/spec/filterrific/param_set_spec.rb +89 -109
- data/spec/filterrific_spec.rb +1 -1
- data/spec/spec_helper.rb +5 -5
- metadata +3 -3
- data/app/assets/javascripts/filterrific/filterrific-jquery.js +0 -118
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a63c202db5dce43d16d986ad31a50abc21752cb7bd66771fce52c2ddc7a2614d
|
|
4
|
+
data.tar.gz: '090e98ad1d561910bbefd0095be81e8b0c622e29cd8cba5b12471379b9023b65'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2d74dc358e308d4d84f04c6637bcd7c682b2ff671d9e11b36d9c9384434b4670d963e945ba80314e6e6e497fc54f7873b37e674bd5794d7f1b298c3912ac2190
|
|
7
|
+
data.tar.gz: b8731da2f59b0d70838c412fdacc1cbf103c76c1eba8668df5253a4d35948e7b60dd9f9bfbb8a021c1c11efe5ab7b945dca3f6490c0b58b66847a3a09dde717c
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
|
7
7
|
|
|
8
8
|
Filterrific major versions match the Ruby on Rails major versions they work with.
|
|
9
9
|
|
|
10
|
+
## [5.2.4] - Jan 30, 2023
|
|
11
|
+
|
|
12
|
+
* Remove JS dependency on jquery
|
|
13
|
+
* Make code compliant with standard.rb rules
|
|
14
|
+
|
|
10
15
|
## [5.2.3] - Mar. 18, 2022
|
|
11
16
|
|
|
12
17
|
* Added support for Rails 7
|
data/Rakefile
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
begin
|
|
2
|
-
require
|
|
2
|
+
require "bundler/setup"
|
|
3
3
|
rescue LoadError
|
|
4
|
-
puts
|
|
4
|
+
puts "You must `gem install bundler` and `bundle install` to run rake tasks"
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
Bundler::GemHelper.install_tasks
|
|
8
8
|
|
|
9
|
-
require
|
|
9
|
+
require "rake/testtask"
|
|
10
10
|
Rake::TestTask.new do |test|
|
|
11
|
-
test.libs <<
|
|
12
|
-
test.pattern =
|
|
11
|
+
test.libs << "spec"
|
|
12
|
+
test.pattern = "spec/**/*_spec.rb"
|
|
13
13
|
test.verbose = true
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
require
|
|
17
|
-
task default:
|
|
16
|
+
require "wwtd/tasks"
|
|
17
|
+
task default: "wwtd:local"
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Javascript behaviors for Filterrific.
|
|
3
|
+
* http://filterrific.clearcove.ca
|
|
4
|
+
*
|
|
5
|
+
* Released under the MIT license
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Create global Filterrific namespace
|
|
10
|
+
if (typeof Filterrific === 'undefined') {
|
|
11
|
+
var Filterrific = {};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Define function to submit Filterrific filter form
|
|
15
|
+
Filterrific.submitFilterForm = function() {
|
|
16
|
+
var form = Filterrific.findParents(this, '#filterrific_filter')[0];
|
|
17
|
+
|
|
18
|
+
// send before event
|
|
19
|
+
form.dispatchEvent(new Event('loadingFilterrificResults'));
|
|
20
|
+
|
|
21
|
+
// turn on spinner
|
|
22
|
+
document.querySelector('.filterrific_spinner').style.display = 'block';
|
|
23
|
+
|
|
24
|
+
// Abort previous XMLHttpRequest request
|
|
25
|
+
if (Filterrific.lastRequest && Filterrific.lastRequest.readyState != 4) {
|
|
26
|
+
Filterrific.lastRequest.abort();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Submit XMLHttpRequest request
|
|
30
|
+
Filterrific.lastRequest = Filterrific.prepareRequest(form);
|
|
31
|
+
Filterrific.lastRequest.send();
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
Filterrific.prepareRequest = function(form) {
|
|
35
|
+
var url = form.getAttribute('action'),
|
|
36
|
+
formData = new FormData(form),
|
|
37
|
+
params = new URLSearchParams(formData),
|
|
38
|
+
xhr = new XMLHttpRequest();
|
|
39
|
+
|
|
40
|
+
if (url.indexOf('?') < 0) {
|
|
41
|
+
url += '?' + params;
|
|
42
|
+
} else {
|
|
43
|
+
url += '&' + params;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
xhr.open("GET", url, true);
|
|
47
|
+
xhr.setRequestHeader('Accept', 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01');
|
|
48
|
+
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
|
49
|
+
xhr.onreadystatechange = function() {
|
|
50
|
+
if (xhr.readyState === XMLHttpRequest.DONE) {
|
|
51
|
+
return Filterrific.processResponse(form, xhr);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return xhr;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
Filterrific.processResponse = function(form, xhr) {
|
|
59
|
+
var rawResponse = (_ref = xhr.response) != null ? _ref : xhr.responseText,
|
|
60
|
+
type = xhr.getResponseHeader('Content-Type'),
|
|
61
|
+
response;
|
|
62
|
+
|
|
63
|
+
if (typeof rawResponse === 'string' && typeof type === 'string') {
|
|
64
|
+
if (type.match(/\bjson\b/)) {
|
|
65
|
+
try {
|
|
66
|
+
response = JSON.parse(rawResponse);
|
|
67
|
+
} catch (_error) {}
|
|
68
|
+
} else if (type.match(/\b(?:java|ecma)script\b/)) {
|
|
69
|
+
script = document.createElement('script');
|
|
70
|
+
script.setAttribute('nonce', Filterrific.cspNonce());
|
|
71
|
+
script.text = rawResponse;
|
|
72
|
+
document.head.appendChild(script).parentNode.removeChild(script);
|
|
73
|
+
} else if (type.match(/\b(xml|html|svg)\b/)) {
|
|
74
|
+
parser = new DOMParser();
|
|
75
|
+
type = type.replace(/;.+/, '');
|
|
76
|
+
try {
|
|
77
|
+
response = parser.parseFromString(rawResponse, type);
|
|
78
|
+
} catch (_error) {}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// send after event
|
|
83
|
+
form.dispatchEvent(new Event('loadedFilterrificResults'));
|
|
84
|
+
document.querySelector('.filterrific_spinner').style.display = 'none';
|
|
85
|
+
|
|
86
|
+
return response;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
Filterrific.cspNonce = function() {
|
|
90
|
+
return document.querySelector("meta[name=csp-nonce]")?.content
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
Filterrific.findParents = function(elem, selector) {
|
|
94
|
+
var elements = [];
|
|
95
|
+
var ishaveselector = selector !== undefined;
|
|
96
|
+
|
|
97
|
+
while ((elem = elem.parentElement) !== null) {
|
|
98
|
+
if (elem.nodeType !== Node.ELEMENT_NODE) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!ishaveselector || elem.matches(selector)) {
|
|
103
|
+
elements.push(elem);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return elements;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
Filterrific.observe_field = function(inputs_selector, frequency, callback) {
|
|
111
|
+
frequency = frequency * 1000;
|
|
112
|
+
|
|
113
|
+
document.querySelectorAll(inputs_selector).forEach(input => {
|
|
114
|
+
var prev = input.value;
|
|
115
|
+
var check = function() {
|
|
116
|
+
// if removed clear the interval and don't fire the callback
|
|
117
|
+
if (removed()) {
|
|
118
|
+
if(ti) clearInterval(ti);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
var val = input.value;
|
|
122
|
+
if (prev != val) {
|
|
123
|
+
prev = val;
|
|
124
|
+
|
|
125
|
+
// invokes the callback on $this
|
|
126
|
+
if (callback && typeof callback === 'function') {
|
|
127
|
+
callback.call(input);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
var removed = function() {
|
|
133
|
+
return input.closest('html').length == 0
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
var reset = function() {
|
|
137
|
+
if (ti) {
|
|
138
|
+
clearInterval(ti);
|
|
139
|
+
ti = setInterval(check, frequency);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
check();
|
|
143
|
+
var ti = setInterval(check, frequency); // invoke check periodically
|
|
144
|
+
// reset counter after user interaction
|
|
145
|
+
// mousemove is for selects
|
|
146
|
+
input.addEventListener('keyup', reset);
|
|
147
|
+
input.addEventListener('click', reset);
|
|
148
|
+
input.addEventListener('mousemove', reset);
|
|
149
|
+
})
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
Filterrific.init = function() {
|
|
154
|
+
// Add change event handler to all Filterrific filter inputs.
|
|
155
|
+
var filterrificForm = document.querySelector('#filterrific_filter');
|
|
156
|
+
filterrificForm.querySelectorAll('input, textarea, select, textarea').forEach(input => {
|
|
157
|
+
input.addEventListener('change', Filterrific.submitFilterForm)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
// Add periodic observer to selected inputs.
|
|
161
|
+
// Use this for text fields you want to observe for change, e.g., a search input.
|
|
162
|
+
Filterrific.observe_field(
|
|
163
|
+
".filterrific-periodically-observed",
|
|
164
|
+
0.5,
|
|
165
|
+
Filterrific.submitFilterForm
|
|
166
|
+
);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
// Initialize event observers on document ready and turbolinks page:load
|
|
171
|
+
document.addEventListener('turbolinks:load', Filterrific.init);
|
|
172
|
+
document.addEventListener('DOMContentLoaded', Filterrific.init)
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
#
|
|
3
2
|
# Adds Filterrific methods ActionController instances
|
|
4
3
|
#
|
|
5
4
|
module Filterrific
|
|
6
5
|
module ActionControllerExtension
|
|
7
|
-
|
|
8
6
|
include HasResetFilterrificUrlMixin
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
protected
|
|
11
9
|
|
|
12
10
|
# @param model_class [Class]
|
|
13
11
|
# @param filterrific_params [ActionController::Params, Hash] typically the
|
|
@@ -33,29 +31,29 @@ module Filterrific
|
|
|
33
31
|
def initialize_filterrific(model_class, filterrific_params, opts = {})
|
|
34
32
|
f_params = (filterrific_params || {}).stringify_keys
|
|
35
33
|
opts = opts.stringify_keys
|
|
36
|
-
pers_id = if
|
|
34
|
+
pers_id = if opts["persistence_id"] == false
|
|
37
35
|
nil
|
|
38
36
|
else
|
|
39
|
-
opts[
|
|
37
|
+
opts["persistence_id"] || compute_default_persistence_id
|
|
40
38
|
end
|
|
41
39
|
|
|
42
|
-
if
|
|
40
|
+
if f_params.delete("reset_filterrific")
|
|
43
41
|
# Reset query and session_persisted params
|
|
44
|
-
session[pers_id] = nil
|
|
45
|
-
redirect_to url_for({})
|
|
42
|
+
session[pers_id] = nil if pers_id
|
|
43
|
+
redirect_to url_for({}) and return false # requires `or return` in calling action.
|
|
46
44
|
end
|
|
47
45
|
|
|
48
46
|
f_params = compute_filterrific_params(model_class, f_params, opts, pers_id)
|
|
49
47
|
|
|
50
48
|
filterrific = Filterrific::ParamSet.new(model_class, f_params)
|
|
51
|
-
filterrific.select_options = opts[
|
|
52
|
-
session[pers_id] = filterrific.to_hash
|
|
49
|
+
filterrific.select_options = opts["select_options"]
|
|
50
|
+
session[pers_id] = filterrific.to_hash if pers_id
|
|
53
51
|
filterrific
|
|
54
52
|
end
|
|
55
53
|
|
|
56
54
|
# Computes a default persistence id based on controller and action name
|
|
57
55
|
def compute_default_persistence_id
|
|
58
|
-
[controller_name, action_name].join(
|
|
56
|
+
[controller_name, action_name].join("#")
|
|
59
57
|
end
|
|
60
58
|
|
|
61
59
|
# Computes filterrific params using a number of strategies. Limits params
|
|
@@ -68,17 +66,17 @@ module Filterrific
|
|
|
68
66
|
# Defaults to true.
|
|
69
67
|
# @param persistence_id [String, nil]
|
|
70
68
|
def compute_filterrific_params(model_class, filterrific_params, opts, persistence_id)
|
|
71
|
-
opts = {
|
|
69
|
+
opts = {"sanitize_params" => true}.merge(opts.stringify_keys)
|
|
72
70
|
r = (
|
|
73
71
|
filterrific_params.presence || # start with passed in params
|
|
74
72
|
(persistence_id && session[persistence_id].presence) || # then try session persisted params if persistence_id is present
|
|
75
|
-
opts[
|
|
73
|
+
opts["default_filter_params"] || # then use passed in opts
|
|
76
74
|
model_class.filterrific_default_filter_params # finally use model_class defaults
|
|
77
75
|
).stringify_keys
|
|
78
|
-
r.slice!(*opts[
|
|
76
|
+
r.slice!(*opts["available_filters"].map(&:to_s)) if opts["available_filters"]
|
|
79
77
|
# Sanitize params to prevent reflected XSS attack
|
|
80
78
|
if opts["sanitize_params"]
|
|
81
|
-
r.each { |k,v| r[k] = sanitize_filterrific_param(r[k]) }
|
|
79
|
+
r.each { |k, v| r[k] = sanitize_filterrific_param(r[k]) }
|
|
82
80
|
end
|
|
83
81
|
r
|
|
84
82
|
end
|
|
@@ -94,7 +92,7 @@ module Filterrific
|
|
|
94
92
|
val.map { |e| sanitize_filterrific_param(e) }
|
|
95
93
|
when Hash
|
|
96
94
|
# Return Hash
|
|
97
|
-
val.
|
|
95
|
+
val.each_with_object({}) { |(k, v), m| m[k] = sanitize_filterrific_param(v); }
|
|
98
96
|
when NilClass
|
|
99
97
|
# Nothing to do, use val as is
|
|
100
98
|
val
|
|
@@ -105,6 +103,5 @@ module Filterrific
|
|
|
105
103
|
val
|
|
106
104
|
end
|
|
107
105
|
end
|
|
108
|
-
|
|
109
106
|
end
|
|
110
107
|
end
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
#
|
|
3
2
|
# Adds Filterrific view helpers to ActionView instances
|
|
4
3
|
#
|
|
5
4
|
module Filterrific
|
|
6
5
|
module ActionViewExtension
|
|
7
|
-
|
|
8
6
|
include HasResetFilterrificUrlMixin
|
|
9
7
|
|
|
10
8
|
# Sets all options on form_for to defaults that work with Filterrific
|
|
@@ -17,8 +15,8 @@ module Filterrific
|
|
|
17
15
|
options[:html][:method] ||= :get
|
|
18
16
|
options[:html][:id] ||= :filterrific_filter
|
|
19
17
|
options[:url] ||= url_for(
|
|
20
|
-
:
|
|
21
|
-
:
|
|
18
|
+
controller: controller.controller_name,
|
|
19
|
+
action: controller.action_name
|
|
22
20
|
)
|
|
23
21
|
form_for(record, options, &block)
|
|
24
22
|
end
|
|
@@ -27,7 +25,7 @@ module Filterrific
|
|
|
27
25
|
def render_filterrific_spinner
|
|
28
26
|
%(
|
|
29
27
|
<span class="filterrific_spinner" style="display:none;">
|
|
30
|
-
#{
|
|
28
|
+
#{image_tag("filterrific/filterrific-spinner.gif", alt: "", role: "presentation")}
|
|
31
29
|
</span>
|
|
32
30
|
).html_safe
|
|
33
31
|
end
|
|
@@ -65,23 +63,25 @@ module Filterrific
|
|
|
65
63
|
# Override the target URL attributes to be used for `url_for`. Default: {} (current URL).
|
|
66
64
|
def filterrific_sorting_link(filterrific, sort_key, opts = {})
|
|
67
65
|
opts = {
|
|
68
|
-
:
|
|
69
|
-
:
|
|
70
|
-
:
|
|
71
|
-
:
|
|
72
|
-
:
|
|
73
|
-
:
|
|
74
|
-
:
|
|
75
|
-
:
|
|
76
|
-
:
|
|
77
|
-
:
|
|
66
|
+
active_column_class: "filterrific_current_sort_column",
|
|
67
|
+
inactive_column_class: "filterrific_sort_column",
|
|
68
|
+
ascending_indicator: "⬆",
|
|
69
|
+
default_sort_direction: "asc",
|
|
70
|
+
descending_indicator: "⬇",
|
|
71
|
+
html_attrs: {},
|
|
72
|
+
label: sort_key.to_s.humanize,
|
|
73
|
+
sorting_scope_name: :sorted_by,
|
|
74
|
+
url_for_attrs: {},
|
|
75
|
+
as: :filterrific
|
|
78
76
|
}.merge(opts)
|
|
79
77
|
opts.merge!(
|
|
80
|
-
:
|
|
81
|
-
:
|
|
82
|
-
:
|
|
83
|
-
:
|
|
84
|
-
|
|
78
|
+
html_attrs: opts[:html_attrs].with_indifferent_access,
|
|
79
|
+
current_sorting: (current_sorting = filterrific.send(opts[:sorting_scope_name])),
|
|
80
|
+
current_sort_key: current_sorting ? current_sorting.gsub(/_asc|_desc/, "") : nil,
|
|
81
|
+
current_sort_direction: if current_sorting
|
|
82
|
+
current_sorting.end_with?("_desc") ? "desc" : "asc"
|
|
83
|
+
end,
|
|
84
|
+
current_sort_direction_indicator: (current_sorting.end_with?("_desc") ? opts[:descending_indicator] : opts[:ascending_indicator])
|
|
85
85
|
)
|
|
86
86
|
new_sort_key = sort_key.to_s
|
|
87
87
|
if new_sort_key == opts[:current_sort_key]
|
|
@@ -93,7 +93,7 @@ module Filterrific
|
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
protected
|
|
97
97
|
|
|
98
98
|
# Renders HTML to reverse sort order on currently sorted column.
|
|
99
99
|
# @param filterrific [Filterrific::ParamSet]
|
|
@@ -102,20 +102,20 @@ module Filterrific
|
|
|
102
102
|
# @return [String] an HTML fragment
|
|
103
103
|
def filterrific_sorting_link_reverse_order(filterrific, new_sort_key, opts)
|
|
104
104
|
# current sort column, toggle search_direction
|
|
105
|
-
new_sort_direction =
|
|
106
|
-
new_sorting = safe_join([new_sort_key, new_sort_direction],
|
|
105
|
+
new_sort_direction = opts[:current_sort_direction] == "asc" ? "desc" : "asc"
|
|
106
|
+
new_sorting = safe_join([new_sort_key, new_sort_direction], "_")
|
|
107
107
|
css_classes = safe_join([
|
|
108
108
|
opts[:active_column_class],
|
|
109
109
|
opts[:html_attrs].delete(:class)
|
|
110
|
-
].compact,
|
|
110
|
+
].compact, " ")
|
|
111
111
|
new_filterrific_params = filterrific.to_hash
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
.with_indifferent_access
|
|
113
|
+
.merge(opts[:sorting_scope_name] => new_sorting)
|
|
114
114
|
url_for_attrs = opts[:url_for_attrs].merge(opts[:as] => new_filterrific_params)
|
|
115
115
|
link_to(
|
|
116
|
-
safe_join([opts[:label], opts[:current_sort_direction_indicator]],
|
|
116
|
+
safe_join([opts[:label], opts[:current_sort_direction_indicator]], " "),
|
|
117
117
|
url_for(url_for_attrs),
|
|
118
|
-
opts[:html_attrs].reverse_merge(:
|
|
118
|
+
opts[:html_attrs].reverse_merge(class: css_classes, method: :get, remote: true)
|
|
119
119
|
)
|
|
120
120
|
end
|
|
121
121
|
|
|
@@ -126,21 +126,20 @@ module Filterrific
|
|
|
126
126
|
# @return [String] an HTML fragment
|
|
127
127
|
def filterrific_sorting_link_new_column(filterrific, new_sort_key, opts)
|
|
128
128
|
new_sort_direction = opts[:default_sort_direction]
|
|
129
|
-
new_sorting = safe_join([new_sort_key, new_sort_direction],
|
|
129
|
+
new_sorting = safe_join([new_sort_key, new_sort_direction], "_")
|
|
130
130
|
css_classes = safe_join([
|
|
131
131
|
opts[:inactive_column_class],
|
|
132
132
|
opts[:html_attrs].delete(:class)
|
|
133
|
-
].compact,
|
|
133
|
+
].compact, " ")
|
|
134
134
|
new_filterrific_params = filterrific.to_hash
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
.with_indifferent_access
|
|
136
|
+
.merge(opts[:sorting_scope_name] => new_sorting)
|
|
137
137
|
url_for_attrs = opts[:url_for_attrs].merge(opts[:as] => new_filterrific_params)
|
|
138
138
|
link_to(
|
|
139
139
|
opts[:label],
|
|
140
140
|
url_for(url_for_attrs),
|
|
141
|
-
opts[:html_attrs].reverse_merge(:
|
|
141
|
+
opts[:html_attrs].reverse_merge(class: css_classes, method: :get, remote: true)
|
|
142
142
|
)
|
|
143
143
|
end
|
|
144
|
-
|
|
145
144
|
end
|
|
146
145
|
end
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
#
|
|
3
2
|
# Adds Filterrific methods to ActiveRecord::Base model_class.
|
|
4
3
|
#
|
|
5
|
-
require
|
|
4
|
+
require "filterrific/param_set"
|
|
6
5
|
|
|
7
6
|
module Filterrific
|
|
8
7
|
module ActiveRecordExtension
|
|
9
|
-
|
|
10
8
|
# Adds Filterrific behavior to class when called like so:
|
|
11
9
|
#
|
|
12
10
|
# filterrific(
|
|
@@ -34,7 +32,6 @@ module Filterrific
|
|
|
34
32
|
validate_filterrific_available_filters
|
|
35
33
|
assign_filterrific_default_filter_params(opts)
|
|
36
34
|
validate_filterrific_default_filter_params
|
|
37
|
-
|
|
38
35
|
end
|
|
39
36
|
|
|
40
37
|
# Returns ActiveRecord relation based on filterrific_param_set.
|
|
@@ -46,7 +43,7 @@ module Filterrific
|
|
|
46
43
|
unless filterrific_param_set.is_a?(Filterrific::ParamSet)
|
|
47
44
|
raise(
|
|
48
45
|
ArgumentError,
|
|
49
|
-
"Invalid Filterrific::ParamSet: #{
|
|
46
|
+
"Invalid Filterrific::ParamSet: #{filterrific_param_set.inspect}"
|
|
50
47
|
)
|
|
51
48
|
end
|
|
52
49
|
|
|
@@ -69,7 +66,7 @@ module Filterrific
|
|
|
69
66
|
ar_rel
|
|
70
67
|
end
|
|
71
68
|
|
|
72
|
-
|
|
69
|
+
protected
|
|
73
70
|
|
|
74
71
|
# Defines a :sorted_by scope based on attrs
|
|
75
72
|
# @param attrs [Hash] with keys as
|
|
@@ -83,7 +80,7 @@ module Filterrific
|
|
|
83
80
|
# @return [void]
|
|
84
81
|
def assign_filterrific_available_filters(opts)
|
|
85
82
|
self.filterrific_available_filters = (
|
|
86
|
-
filterrific_available_filters + (opts[
|
|
83
|
+
filterrific_available_filters + (opts["available_filters"] || [])
|
|
87
84
|
).map(&:to_s).uniq.sort
|
|
88
85
|
end
|
|
89
86
|
|
|
@@ -97,7 +94,7 @@ module Filterrific
|
|
|
97
94
|
|
|
98
95
|
def assign_filterrific_default_filter_params(opts)
|
|
99
96
|
self.filterrific_default_filter_params = (
|
|
100
|
-
opts[
|
|
97
|
+
opts["default_filter_params"] || {}
|
|
101
98
|
).stringify_keys
|
|
102
99
|
end
|
|
103
100
|
|
|
@@ -106,9 +103,8 @@ module Filterrific
|
|
|
106
103
|
if (
|
|
107
104
|
inv_fdfps = filterrific_default_filter_params.keys - filterrific_available_filters
|
|
108
105
|
).any?
|
|
109
|
-
raise(ArgumentError, "Invalid default filter params: #{
|
|
106
|
+
raise(ArgumentError, "Invalid default filter params: #{inv_fdfps.inspect}")
|
|
110
107
|
end
|
|
111
108
|
end
|
|
112
|
-
|
|
113
109
|
end
|
|
114
110
|
end
|
data/lib/filterrific/engine.rb
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
require "filterrific/param_set"
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "filterrific/has_reset_filterrific_url_mixin"
|
|
4
4
|
|
|
5
|
-
require
|
|
6
|
-
|
|
7
|
-
require
|
|
8
|
-
require 'filterrific/action_view_extension'
|
|
9
|
-
require 'filterrific/active_record_extension'
|
|
5
|
+
require "filterrific/action_controller_extension"
|
|
6
|
+
require "filterrific/action_view_extension"
|
|
7
|
+
require "filterrific/active_record_extension"
|
|
10
8
|
|
|
11
9
|
module Filterrific
|
|
12
10
|
class Engine < ::Rails::Engine
|
|
13
|
-
|
|
14
11
|
# It's an engine so that we can add javascript and image assets
|
|
15
12
|
# to the asset pipeline.
|
|
16
13
|
|
|
@@ -27,6 +24,5 @@ module Filterrific
|
|
|
27
24
|
ActiveSupport.on_load :active_record do
|
|
28
25
|
extend Filterrific::ActiveRecordExtension
|
|
29
26
|
end
|
|
30
|
-
|
|
31
27
|
end
|
|
32
28
|
end
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
#
|
|
3
2
|
# Adds reset_filterrific_url to controllers and views
|
|
4
3
|
#
|
|
5
4
|
module Filterrific
|
|
6
5
|
module HasResetFilterrificUrlMixin
|
|
7
|
-
|
|
8
6
|
# Returns a url that can be used to reset the Filterrific params
|
|
9
7
|
def reset_filterrific_url(opts = {})
|
|
10
8
|
url_for(
|
|
11
|
-
{
|
|
9
|
+
{filterrific: {reset_filterrific: true}}.merge(opts)
|
|
12
10
|
)
|
|
13
11
|
end
|
|
14
|
-
|
|
15
12
|
end
|
|
16
13
|
end
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require 'active_support/all'
|
|
4
|
-
require 'digest/sha1'
|
|
1
|
+
require "active_support/all"
|
|
2
|
+
require "digest/sha1"
|
|
5
3
|
|
|
6
4
|
module Filterrific
|
|
7
|
-
|
|
8
5
|
# FilterParamSet is a container to store FilterParams
|
|
9
6
|
class ParamSet
|
|
10
|
-
|
|
11
7
|
attr_accessor :model_class
|
|
12
8
|
attr_accessor :select_options
|
|
13
9
|
|
|
@@ -27,16 +23,16 @@ module Filterrific
|
|
|
27
23
|
# You might wonder "what if I want to change only one thing from the defaults?"
|
|
28
24
|
# Persistence, baby. By the time you submit changes to one filter, all the others
|
|
29
25
|
# will be already initialized with the defaults.
|
|
30
|
-
filterrific_params = model_class.filterrific_default_filter_params
|
|
26
|
+
filterrific_params = model_class.filterrific_default_filter_params if filterrific_params.blank?
|
|
31
27
|
if defined?(ActionController::Parameters) && filterrific_params.is_a?(ActionController::Parameters)
|
|
32
28
|
permissible_filter_params = []
|
|
33
29
|
model_class.filterrific_available_filters.each do |p|
|
|
34
|
-
if filterrific_params[p].is_a?(ActionController::Parameters)
|
|
35
|
-
|
|
30
|
+
permissible_filter_params << if filterrific_params[p].is_a?(ActionController::Parameters)
|
|
31
|
+
{p => filterrific_params[p].keys}
|
|
36
32
|
elsif filterrific_params[p].is_a?(Array)
|
|
37
|
-
|
|
33
|
+
{p => []}
|
|
38
34
|
else
|
|
39
|
-
|
|
35
|
+
p
|
|
40
36
|
end
|
|
41
37
|
end
|
|
42
38
|
filterrific_params = filterrific_params.permit(permissible_filter_params).to_h.stringify_keys
|
|
@@ -60,14 +56,13 @@ module Filterrific
|
|
|
60
56
|
def to_hash
|
|
61
57
|
{}.tap { |h|
|
|
62
58
|
model_class.filterrific_available_filters.each do |filter_name|
|
|
63
|
-
param_value =
|
|
64
|
-
|
|
65
|
-
when param_value.blank?
|
|
59
|
+
param_value = send(filter_name)
|
|
60
|
+
if param_value.blank?
|
|
66
61
|
# do nothing
|
|
67
|
-
|
|
62
|
+
elsif param_value.is_a?(Proc)
|
|
68
63
|
# evaluate Proc so it can be serialized
|
|
69
64
|
h[filter_name] = param_value.call
|
|
70
|
-
|
|
65
|
+
elsif param_value.is_a?(OpenStruct)
|
|
71
66
|
# convert OpenStruct to hash
|
|
72
67
|
h[filter_name] = param_value.marshal_dump
|
|
73
68
|
else
|
|
@@ -83,25 +78,24 @@ module Filterrific
|
|
|
83
78
|
to_hash.to_json
|
|
84
79
|
end
|
|
85
80
|
|
|
86
|
-
|
|
81
|
+
protected
|
|
87
82
|
|
|
88
83
|
# Conditions params: Evaluates Procs and type casts integer values.
|
|
89
84
|
# @param fp [Hash] the filterrific params hash
|
|
90
85
|
# @return[Hash] the conditioned params hash
|
|
91
86
|
def condition_filterrific_params(fp)
|
|
92
87
|
fp.each do |key, val|
|
|
93
|
-
|
|
94
|
-
when val.is_a?(Proc)
|
|
88
|
+
if val.is_a?(Proc)
|
|
95
89
|
# evaluate Procs
|
|
96
90
|
fp[key] = val.call
|
|
97
|
-
|
|
91
|
+
elsif val.is_a?(Array)
|
|
98
92
|
# type cast integers in the array
|
|
99
|
-
fp[key] = fp[key].map { |e| e
|
|
100
|
-
|
|
93
|
+
fp[key] = fp[key].map { |e| e.to_s.match?(integer_detector_regex) ? e.to_i : e }
|
|
94
|
+
elsif val.is_a?(Hash)
|
|
101
95
|
# type cast Hash to OpenStruct so that nested params render correctly
|
|
102
96
|
# in the form
|
|
103
97
|
fp[key] = OpenStruct.new(fp[key])
|
|
104
|
-
|
|
98
|
+
elsif val.is_a?(String) && val.match?(integer_detector_regex)
|
|
105
99
|
# type cast integer
|
|
106
100
|
fp[key] = fp[key].to_i
|
|
107
101
|
end
|
|
@@ -121,10 +115,8 @@ module Filterrific
|
|
|
121
115
|
model_class.filterrific_available_filters.each do |filter_name|
|
|
122
116
|
self.class.send(:attr_accessor, filter_name)
|
|
123
117
|
v = fp[filter_name]
|
|
124
|
-
|
|
118
|
+
send("#{filter_name}=", v) if v.present?
|
|
125
119
|
end
|
|
126
120
|
end
|
|
127
|
-
|
|
128
121
|
end
|
|
129
|
-
|
|
130
122
|
end
|
data/lib/filterrific/version.rb
CHANGED