filterrific 1.4.3 → 2.0.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/CHANGELOG.md +39 -24
- data/README.md +11 -7
- data/lib/filterrific.rb +2 -6
- data/lib/filterrific/action_controller_extension.rb +58 -0
- data/lib/filterrific/action_view_extension.rb +114 -78
- data/lib/filterrific/active_record_extension.rb +64 -44
- data/lib/filterrific/engine.rb +10 -2
- data/lib/filterrific/param_set.rb +30 -30
- data/lib/filterrific/version.rb +3 -1
- metadata +8 -8
- data/doc/rails_conf_talk.md +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5771d0552efaa97955d18305f8199f7691148d93
|
4
|
+
data.tar.gz: 037e7d5b634f6183cd6315d35cdd5a8874c47f3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6da890161d37d8884debfff82b057f1fe304ebcf858b6a0493cc756739428cc33fdc131abd0f174fd4ea449704404adc10204c33fa22d89b96c623001aefdafc
|
7
|
+
data.tar.gz: 192de17ea6192932f6e6c262fa6888fa35b39188ec0c99dcaf76bedd94434671f2754968ac4e9cc0fad8d2fc461c95dbb122e1b11074afc7fe1567b46920258d
|
data/CHANGELOG.md
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
API changes
|
4
|
-
|
5
|
-
* Filterrific can build the `sorted_by` and `search_query` scopes automatically,
|
6
|
-
based on some configuration in the including model.
|
7
|
-
|
1
|
+
* using the "sorted_by" magic method will add entries to select_options automatically
|
8
2
|
filterrific(
|
9
|
-
|
3
|
+
default_filter_params: { sorted_by: 'name_asc' },
|
4
|
+
available_filters: [
|
5
|
+
:with_country,
|
6
|
+
...
|
7
|
+
],
|
10
8
|
search_query: {
|
11
9
|
match_terms: :any, # [:all]
|
12
10
|
auto_wildcard: :suffix, # [:prefix, :both, :none]
|
@@ -17,39 +15,56 @@ API changes
|
|
17
15
|
name_asc: 'Name (A-Z)',
|
18
16
|
name_desc: 'Name (Z-A)',
|
19
17
|
},
|
20
|
-
|
18
|
+
)
|
19
|
+
|
20
|
+
filterrific(
|
21
|
+
default_filter_params: { sorted_by: 'name_asc' },
|
22
|
+
custom_scopes: [
|
21
23
|
:with_country,
|
22
24
|
...
|
25
|
+
],
|
26
|
+
lookup_filters: [ # column_value_filters:, value_filters:
|
27
|
+
:with_country_id,
|
28
|
+
:with_state,
|
23
29
|
]
|
30
|
+
search_query: {
|
31
|
+
match_terms: :any, # [:all]
|
32
|
+
auto_wildcard: :suffix, # [:prefix, :both, :none]
|
33
|
+
columns: [:first_name, :email, :last_name],
|
34
|
+
case_sensitive: false, # [true]
|
35
|
+
},
|
36
|
+
sorted_by: {
|
37
|
+
name_asc: 'Name (A-Z)',
|
38
|
+
name_desc: 'Name (Z-A)',
|
39
|
+
},
|
24
40
|
)
|
25
41
|
|
26
|
-
* Filterrific can handle persistence of search params in session for you.
|
27
|
-
* Simplified `@filterrific.find` method to load collection from DB.
|
28
|
-
Replaces `Student.filterrific_find(@filterrific)`
|
29
42
|
|
30
|
-
@filterrific = initialize_filterrific(
|
31
|
-
Student,
|
32
|
-
default_settings: {},
|
33
|
-
filter_names: [],
|
34
|
-
params_key: :filterrific,
|
35
|
-
select_options: {},
|
36
|
-
session_persistence_key: 'asdf',
|
37
|
-
)
|
38
|
-
@students = @filterrific.find
|
39
43
|
|
40
|
-
|
41
|
-
`form_for` method. It is used via `form_for_filterrific` instead:
|
44
|
+
# 2.0.0
|
42
45
|
|
43
|
-
|
46
|
+
API changes:
|
44
47
|
|
48
|
+
* Filterrific model API option names have changed.
|
49
|
+
* Better initialization of Filterrific via `initialize_filterrific` method:
|
50
|
+
* It resets the filter params, so the `reset_filterrific` action is not required any more.
|
51
|
+
* It persists filter params in session.
|
52
|
+
* Simplified `@filterrific.find` method to load collection from DB.
|
53
|
+
Replaces `Student.filterrific_find(@filterrific)`
|
54
|
+
* The `form_for_filterrific` form builder doesn't override the standard
|
55
|
+
`form_for` method any more.
|
45
56
|
* Dropped support for Ruby 1.8.7 (because of 1.9 Hash syntax)
|
46
57
|
* Dropped support for Rails <= 3.0.0 (because of ActiveRecord
|
47
58
|
bug fixes in 3.1, and use of asset pipeline)
|
48
59
|
|
60
|
+
|
61
|
+
|
49
62
|
### 1.4.3
|
50
63
|
|
51
64
|
* Handle case where Filterrific filter params are empty.
|
52
65
|
|
66
|
+
|
67
|
+
|
53
68
|
### 1.4.2
|
54
69
|
|
55
70
|
* Updated initialization of ActiveRecord and ActionView extensions again
|
data/README.md
CHANGED
@@ -13,8 +13,6 @@ search, and sort your ActiveRecord lists:
|
|
13
13
|
Make sure to go to the fantastic [Filterrific documentation](http://filterrific.clearcove.ca)
|
14
14
|
to find out more!
|
15
15
|
|
16
|
-
TODO: look at Jeff's email suggestions (June 23, 2014)
|
17
|
-
|
18
16
|
### Installation
|
19
17
|
|
20
18
|
`gem install filterrific`
|
@@ -28,11 +26,11 @@ or with bundler in your Gemfile:
|
|
28
26
|
|
29
27
|
Every commit to Filterrific is automatically tested against the following scenarios:
|
30
28
|
|
31
|
-
| Rails version | Ruby environments
|
32
|
-
|
33
|
-
| Rails 4.1 | MRI 1.9.3, 2.0.0, 2.1.2 | mysql, mysql2, postgresql, sqlite3 |[](https://travis-ci.org/jhund/filterrific_demo)|
|
34
|
-
| Rails 4.0 | MRI 1.9.3, 2.0.0, 2.1.2 | mysql, mysql2, postgresql, sqlite3 |[](https://travis-ci.org/jhund/filterrific_demo)|
|
35
|
-
| Rails 3.2 | MRI 1.9.3, 2.0.0, 2.1.2
|
29
|
+
| Rails version | Ruby environments | Database adapters | Build status |
|
30
|
+
|---------------|--------------------------------|------------------------------------|--------------|
|
31
|
+
| Rails 4.1 | MRI 1.9.3, 2.0.0, 2.1.2, 2.2.0 | mysql, mysql2, postgresql, sqlite3 |[](https://travis-ci.org/jhund/filterrific_demo)|
|
32
|
+
| Rails 4.0 | MRI 1.9.3, 2.0.0, 2.1.2, 2.2.0 | mysql, mysql2, postgresql, sqlite3 |[](https://travis-ci.org/jhund/filterrific_demo)|
|
33
|
+
| Rails 3.2 | MRI 1.9.3, 2.0.0, 2.1.2 | mysql, mysql2, postgresql, sqlite3 |[](https://travis-ci.org/jhund/filterrific_demo)|
|
36
34
|
|
37
35
|
Filterrific version 1.4.0 should work on older versions of Rails and Ruby, however
|
38
36
|
the 1.x branch is not supported any more.
|
@@ -65,6 +63,12 @@ I'm happy to help you if you encounter problems when using filterrific. You'll m
|
|
65
63
|
|
66
64
|
[](https://codeclimate.com/github/jhund/filterrific)
|
67
65
|
|
66
|
+
### Related projects
|
67
|
+
|
68
|
+
* has_scope
|
69
|
+
* http://www.justinweiss.com/blog/2014/02/17/search-and-filter-rails-models-without-bloating-your-controller/
|
70
|
+
* https://github.com/laserlemon/periscope
|
71
|
+
|
68
72
|
### License
|
69
73
|
|
70
74
|
[MIT licensed](https://github.com/jhund/filterrific/blob/master/MIT-LICENSE).
|
data/lib/filterrific.rb
CHANGED
@@ -1,11 +1,7 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
1
3
|
require 'filterrific/version'
|
2
4
|
require 'filterrific/engine'
|
3
5
|
|
4
6
|
module Filterrific
|
5
|
-
|
6
|
-
# Wrapper around Filterrific::ParamSet initialization
|
7
|
-
def self.new(a_resource_class, filterrific_params = {})
|
8
|
-
Filterrific::ParamSet.new(a_resource_class, filterrific_params)
|
9
|
-
end
|
10
|
-
|
11
7
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Adds Filterrific methods ActionController instances
|
4
|
+
#
|
5
|
+
module Filterrific
|
6
|
+
module ActionControllerExtension
|
7
|
+
|
8
|
+
protected
|
9
|
+
|
10
|
+
# @param model_class [Class]
|
11
|
+
# @param filterrific_params [Hash] typically the Rails request params under
|
12
|
+
# the :filterrific key (params[:filterrific]), however can be any Hash.
|
13
|
+
# @param opts [Hash]
|
14
|
+
# @option opts [Array<String>, optional] :available_filters
|
15
|
+
# further restrict which of the filters specified in the model are
|
16
|
+
# available in this context.
|
17
|
+
# @option opts [Hash, optional] :default_filter_params
|
18
|
+
# overrides the defaults specified in the model.
|
19
|
+
# @option opts [String, Symbol, optional] :persistence_id
|
20
|
+
# defaults to "namespace/controller#action" string, used for session key
|
21
|
+
# and saved searches to isolate different filters' persisted params from
|
22
|
+
# each other.
|
23
|
+
# @option opts [Hash, optional] :select_options
|
24
|
+
# these are available in the view to populate select lists and other
|
25
|
+
# dynamic values.
|
26
|
+
# @return [Filterrific::ParamSet]
|
27
|
+
def initialize_filterrific(model_class, filterrific_params, opts)
|
28
|
+
f_params = (filterrific_params || {}).deep_stringify_keys
|
29
|
+
opts = opts.stringify_keys
|
30
|
+
pi = opts['persistence_id'] || compute_default_persistence_id
|
31
|
+
|
32
|
+
if (f_params.delete('reset_filterrific'))
|
33
|
+
# Reset query and session_persisted params
|
34
|
+
session[pi] = nil
|
35
|
+
redirect_to url_for({}) and return false # works with `or return` in calling action.
|
36
|
+
end
|
37
|
+
|
38
|
+
f_params = f_params.presence || # start with passed in params
|
39
|
+
session[pi].presence || # then try session persisted params
|
40
|
+
opts['default_filter_params'] || # then use passed in opts
|
41
|
+
model_class.filterrific_default_filter_params # finally use model_class defaults
|
42
|
+
|
43
|
+
f_params.deep_stringify_keys!
|
44
|
+
f_params.slice!(opts['available_filters'].map(&:to_s)) if opts['available_filters']
|
45
|
+
|
46
|
+
filterrific = Filterrific::ParamSet.new(model_class, f_params)
|
47
|
+
filterrific.select_options = opts['select_options']
|
48
|
+
session[pi] = filterrific.to_hash
|
49
|
+
filterrific
|
50
|
+
end
|
51
|
+
|
52
|
+
# Computes a default persistence id based on controller and action name
|
53
|
+
def compute_default_persistence_id
|
54
|
+
[controller_name, action_name].join('#')
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -1,23 +1,25 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
|
3
2
|
#
|
4
|
-
# Adds view helpers to ActionView
|
3
|
+
# Adds Filterrific view helpers to ActionView instances
|
5
4
|
#
|
6
5
|
module Filterrific
|
7
6
|
module ActionViewExtension
|
8
7
|
|
9
|
-
# Sets all options on form_for to defaults
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
8
|
+
# Sets all options on form_for to defaults that work with Filterrific
|
9
|
+
# @param record [Filterrific] the @filterrific object
|
10
|
+
# @param options [Hash] standard options for form_for
|
11
|
+
# @param block [Proc] the form body
|
12
|
+
def form_for_filterrific(record, options = {}, &block)
|
13
|
+
options[:as] ||= :filterrific
|
14
|
+
options[:html] ||= {}
|
15
|
+
options[:html][:method] ||= :get
|
16
|
+
options[:html][:id] ||= :filterrific_filter
|
17
|
+
options[:url] ||= url_for(
|
18
|
+
:controller => controller.controller_name,
|
19
|
+
:action => controller.action_name
|
20
|
+
)
|
21
|
+
form_for(record, options, &block)
|
19
22
|
end
|
20
|
-
# TODO: change this to form_for_filterrific!
|
21
23
|
|
22
24
|
# Renders a spinner while the list is being updated
|
23
25
|
def render_filterrific_spinner
|
@@ -27,36 +29,40 @@ module Filterrific
|
|
27
29
|
</span>
|
28
30
|
).html_safe
|
29
31
|
end
|
30
|
-
#alias: filterrific_spinner
|
31
32
|
|
32
33
|
# Renders a link which indicates the current sorting and which can be used to
|
33
34
|
# toggle the list sorting (set column and direction).
|
35
|
+
#
|
34
36
|
# NOTE: Make sure that this is used in the list partial that is re-rendered
|
35
37
|
# when the filterrific params are changed, so that the filterrific params in
|
36
38
|
# the URL are always current.
|
37
|
-
#
|
38
|
-
#
|
39
|
+
#
|
40
|
+
# NOTE: Currently the filterrific_sorting_link is not synchronized with a
|
41
|
+
# SELECT input you may have in the filter form for sorting. We recommend you
|
42
|
+
# use one or the other to avoid conflicting sort settings in the UI.
|
43
|
+
#
|
44
|
+
# @param filterrific [Filterrific::ParamSet] the current filterrific instance
|
45
|
+
# @param sort_key [String, Symbol] the key to sort by, without direction.
|
39
46
|
# Example: 'name', 'created_at'
|
40
|
-
# @param[Hash, optional]
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
options = {
|
47
|
+
# @param opts [Hash, optional]
|
48
|
+
# @options opts [String, optional] active_column_class
|
49
|
+
# CSS class applied to current sort column. Default: 'filterrific_current_sort_column'
|
50
|
+
# @options opts [String, optional] ascending_indicator
|
51
|
+
# HTML string to indicate ascending sort direction. Default: '⬆'
|
52
|
+
# @options opts [String, optional] default_sort_direction
|
53
|
+
# Override the default sorting when selecting a new sort column. Default: 'asc'.
|
54
|
+
# @options opts [String, optional] descending_indicator
|
55
|
+
# HTML string to indicate descending sort direction. Default: '⬇'
|
56
|
+
# @options opts [Hash, optional] html_attrs
|
57
|
+
# HTML attributes to be added to the sorting link. Default: {}
|
58
|
+
# @options opts [String, optional] label
|
59
|
+
# Override label. Default: `sort_key.to_s.humanize`.
|
60
|
+
# @options opts [String, Symbol, optional] sorting_scope_name
|
61
|
+
# Override the name of the scope used for sorting. Default: :sorted_by
|
62
|
+
# @options opts [Hash, optional] url_for_attrs
|
63
|
+
# Override the target URL attributes to be used for `url_for`. Default: {} (current URL).
|
64
|
+
def filterrific_sorting_link(filterrific, sort_key, opts = {})
|
65
|
+
opts = {
|
60
66
|
:active_column_class => 'filterrific_current_sort_column',
|
61
67
|
:inactive_column_class => 'filterrific_sort_column',
|
62
68
|
:ascending_indicator => '⬆',
|
@@ -66,52 +72,82 @@ module Filterrific
|
|
66
72
|
:label => sort_key.to_s.humanize,
|
67
73
|
:sorting_scope_name => :sorted_by,
|
68
74
|
:url_for_attrs => {},
|
69
|
-
}.merge(
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
75
|
+
}.merge(opts)
|
76
|
+
opts.merge!(
|
77
|
+
:html_attrs => opts[:html_attrs].with_indifferent_access,
|
78
|
+
:current_sorting => filterrific.send(opts[:sorting_scope_name]),
|
79
|
+
:current_sort_key => current_sorting ? current_sorting.gsub(/_asc|_desc/, '') : nil,
|
80
|
+
:current_sort_direction => current_sorting ? (current_sorting =~ /_desc\z/ ? 'desc' : 'asc') : nil,
|
81
|
+
)
|
74
82
|
new_sort_key = sort_key.to_s
|
75
|
-
if new_sort_key == current_sort_key
|
76
|
-
#
|
77
|
-
|
78
|
-
['desc', options[:ascending_indicator]]
|
79
|
-
else
|
80
|
-
['asc', options[:descending_indicator]]
|
81
|
-
end
|
82
|
-
new_sorting = [new_sort_key, new_sort_direction].join('_')
|
83
|
-
css_classes = [
|
84
|
-
options[:active_column_class],
|
85
|
-
options[:html_attrs].delete(:class)
|
86
|
-
].compact.join(' ')
|
87
|
-
new_filterrific_params = filterrific.to_hash
|
88
|
-
.with_indifferent_access
|
89
|
-
.merge(options[:sorting_scope_name] => new_sorting)
|
90
|
-
url_for_attrs = options[:url_for_attrs].merge(:filterrific => new_filterrific_params)
|
91
|
-
link_to(
|
92
|
-
[options[:label], current_sort_direction_indicator].join(' '),
|
93
|
-
url_for(url_for_attrs),
|
94
|
-
options[:html_attrs].reverse_merge(:class => css_classes, :method => :get, :remote => true)
|
95
|
-
)
|
83
|
+
if new_sort_key == opts[:current_sort_key]
|
84
|
+
# same sort column, reverse order
|
85
|
+
filterrific_sorting_link_reverse_order(filterrific, new_sort_key, opts)
|
96
86
|
else
|
97
|
-
# new sort column,
|
98
|
-
|
99
|
-
new_sorting = [new_sort_key, new_sort_direction].join('_')
|
100
|
-
css_classes = [
|
101
|
-
options[:inactive_column_class],
|
102
|
-
options[:html_attrs].delete(:class)
|
103
|
-
].compact.join(' ')
|
104
|
-
new_filterrific_params = filterrific.to_hash
|
105
|
-
.with_indifferent_access
|
106
|
-
.merge(options[:sorting_scope_name] => new_sorting)
|
107
|
-
url_for_attrs = options[:url_for_attrs].merge(:filterrific => new_filterrific_params)
|
108
|
-
link_to(
|
109
|
-
options[:label],
|
110
|
-
url_for(url_for_attrs),
|
111
|
-
options[:html_attrs].reverse_merge(:class => css_classes, :method => :get, :remote => true)
|
112
|
-
)
|
87
|
+
# new sort column, default sort order
|
88
|
+
filterrific_sorting_link_new_column(filterrific, new_sort_key, opts)
|
113
89
|
end
|
114
90
|
end
|
115
91
|
|
92
|
+
# Returns a url that can be used to reset the Filterrific params
|
93
|
+
def reset_filterrific_url(opts = {})
|
94
|
+
url_for(
|
95
|
+
{ filterrific: { reset_filterrific: true } }.merge(opts)
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
protected
|
100
|
+
|
101
|
+
# Renders HTML to reverse sort order on currently sorted column.
|
102
|
+
# @param filterrific [Filterrific::ParamSet]
|
103
|
+
# @param new_sort_key [String]
|
104
|
+
# @param opts [Hash]
|
105
|
+
# @return [String] an HTML fragment
|
106
|
+
def filterrific_sorting_link_reverse_order(filterrific, new_sort_key, opts)
|
107
|
+
# current sort column, toggle search_direction
|
108
|
+
new_sort_direction, current_sort_direction_indicator = if 'asc' == opts[:current_sort_direction]
|
109
|
+
['desc', opts[:ascending_indicator]]
|
110
|
+
else
|
111
|
+
['asc', opts[:descending_indicator]]
|
112
|
+
end
|
113
|
+
new_sorting = [new_sort_key, new_sort_direction].join('_')
|
114
|
+
css_classes = [
|
115
|
+
opts[:active_column_class],
|
116
|
+
opts[:html_attrs].delete(:class)
|
117
|
+
].compact.join(' ')
|
118
|
+
new_filterrific_params = filterrific.to_hash
|
119
|
+
.with_indifferent_access
|
120
|
+
.merge(opts[:sorting_scope_name] => new_sorting)
|
121
|
+
url_for_attrs = opts[:url_for_attrs].merge(:filterrific => new_filterrific_params)
|
122
|
+
link_to(
|
123
|
+
[opts[:label], opts[:current_sort_direction_indicator]].join(' '),
|
124
|
+
url_for(url_for_attrs),
|
125
|
+
opts[:html_attrs].reverse_merge(:class => css_classes, :method => :get, :remote => true)
|
126
|
+
)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Renders HTML to sort by a new column.
|
130
|
+
# @param filterrific [Filterrific::ParamSet]
|
131
|
+
# @param new_sort_key [String]
|
132
|
+
# @param opts [Hash]
|
133
|
+
# @return [String] an HTML fragment
|
134
|
+
def filterrific_sorting_link_new_column(filterrific, new_sort_key, opts)
|
135
|
+
new_sort_direction = opts[:default_sort_direction]
|
136
|
+
new_sorting = [new_sort_key, new_sort_direction].join('_')
|
137
|
+
css_classes = [
|
138
|
+
opts[:inactive_column_class],
|
139
|
+
opts[:html_attrs].delete(:class)
|
140
|
+
].compact.join(' ')
|
141
|
+
new_filterrific_params = filterrific.to_hash
|
142
|
+
.with_indifferent_access
|
143
|
+
.merge(opts[:sorting_scope_name] => new_sorting)
|
144
|
+
url_for_attrs = opts[:url_for_attrs].merge(:filterrific => new_filterrific_params)
|
145
|
+
link_to(
|
146
|
+
opts[:label],
|
147
|
+
url_for(url_for_attrs),
|
148
|
+
opts[:html_attrs].reverse_merge(:class => css_classes, :method => :get, :remote => true)
|
149
|
+
)
|
150
|
+
end
|
151
|
+
|
116
152
|
end
|
117
153
|
end
|
@@ -1,51 +1,58 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
#
|
2
|
-
# Adds
|
3
|
+
# Adds Filterrific methods to ActiveRecord::Base model_class.
|
3
4
|
#
|
4
5
|
require 'filterrific/param_set'
|
5
6
|
|
6
7
|
module Filterrific
|
7
8
|
module ActiveRecordExtension
|
8
9
|
|
9
|
-
# Adds
|
10
|
+
# Adds Filterrific behavior to class when called like so:
|
10
11
|
#
|
11
12
|
# filterrific(
|
12
|
-
# :
|
13
|
-
# :
|
13
|
+
# :available_filters => [:sorted_by, :search_query, :with_state]
|
14
|
+
# :default_filter_params => { :sorted_by => "created_at_asc" },
|
14
15
|
# )
|
15
16
|
#
|
16
|
-
# @params[Hash]
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
cattr_accessor :
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
17
|
+
# @params opts [Hash] with either string or symbol keys, will be stringified.
|
18
|
+
# @option opts [Array<String, Symbol>] available_filters: a list of filters to be exposed by Filterrific.
|
19
|
+
# @option opts [Hash, optional] default_filter_params: default filter parameters
|
20
|
+
# @return [void]
|
21
|
+
def filterrific(opts)
|
22
|
+
cattr_accessor :filterrific_available_filters
|
23
|
+
cattr_accessor :filterrific_default_filter_params
|
24
|
+
self.filterrific_available_filters = []
|
25
|
+
|
26
|
+
opts.stringify_keys!
|
27
|
+
|
28
|
+
define_sorted_by_scope(opts['sorted_by']) if opts['sorted_by']
|
29
|
+
#define_search_query_scope(opts['search_query']) if opts['search_query']
|
30
|
+
|
31
|
+
assign_filterrific_available_filters(opts)
|
32
|
+
validate_filterrific_available_filters
|
33
|
+
assign_filterrific_default_filter_params(opts)
|
34
|
+
validate_filterrific_default_filter_params
|
35
|
+
|
31
36
|
end
|
32
37
|
|
33
|
-
# Returns ActiveRecord relation based on
|
34
|
-
# Use like so:
|
35
|
-
# ModelClass.filterrific_find(@filterrific_param_set)
|
38
|
+
# Returns ActiveRecord relation based on filterrific_param_set.
|
39
|
+
# Use like so: `ModelClass.filterrific_find(@filterrific)`
|
36
40
|
#
|
37
|
-
# @param[Filterrific::ParamSet]
|
38
|
-
# @return[ActiveRecord::Relation]
|
41
|
+
# @param filterrific_param_set [Filterrific::ParamSet]
|
42
|
+
# @return [ActiveRecord::Relation] with filters applied
|
39
43
|
def filterrific_find(filterrific_param_set)
|
40
44
|
unless filterrific_param_set.is_a?(Filterrific::ParamSet)
|
41
|
-
raise(
|
45
|
+
raise(
|
46
|
+
ArgumentError,
|
47
|
+
"Invalid Filterrific::ParamSet: #{ filterrific_param_set.inspect }"
|
48
|
+
)
|
42
49
|
end
|
43
50
|
|
44
|
-
#
|
45
|
-
ar_rel = if
|
51
|
+
# Initialize ActiveRecord::Relation
|
52
|
+
ar_rel = if ActiveRecord::Relation === self
|
46
53
|
# self is already an ActiveRecord::Relation, use as is
|
47
54
|
self
|
48
|
-
elsif
|
55
|
+
elsif Rails::VERSION::MAJOR <= 3
|
49
56
|
# Active Record 3: send `:scoped` to class to get an ActiveRecord::Relation
|
50
57
|
scoped
|
51
58
|
else
|
@@ -53,8 +60,8 @@ module Filterrific
|
|
53
60
|
all
|
54
61
|
end
|
55
62
|
|
56
|
-
#
|
57
|
-
|
63
|
+
# Apply filterrific params
|
64
|
+
filterrific_available_filters.each do |filter_name|
|
58
65
|
filter_param = filterrific_param_set.send(filter_name)
|
59
66
|
next if filter_param.blank? # skip blank filter_params
|
60
67
|
ar_rel = ar_rel.send(filter_name, filter_param)
|
@@ -65,29 +72,42 @@ module Filterrific
|
|
65
72
|
|
66
73
|
protected
|
67
74
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
75
|
+
# Defines a :sorted_by scope based on attrs
|
76
|
+
# @param attrs [Hash] with keys as
|
77
|
+
def define_sorted_by_scope(attrs)
|
78
|
+
scope :sorted_by, lambda {}
|
72
79
|
end
|
73
80
|
|
74
|
-
|
75
|
-
|
76
|
-
|
81
|
+
# Assigns available filters.
|
82
|
+
# @param opts [Hash] the complete options hash passed to `filterrific`.
|
83
|
+
# This method uses the 'available_filters', 'sorted_by', and 'search_query' keys.
|
84
|
+
# @return [void]
|
85
|
+
def assign_filterrific_available_filters(opts)
|
86
|
+
self.filterrific_available_filters = (
|
87
|
+
filterrific_available_filters + (opts['available_filters'] || [])
|
88
|
+
).map(&:to_s).uniq.sort
|
89
|
+
end
|
90
|
+
|
91
|
+
# Validates presence of at least one available filter.
|
92
|
+
# @return [void]
|
93
|
+
def validate_filterrific_available_filters
|
94
|
+
if filterrific_available_filters.blank?
|
95
|
+
raise(ArgumentError, ":available_filters can't be empty")
|
96
|
+
end
|
77
97
|
end
|
78
98
|
|
79
|
-
def
|
80
|
-
self.
|
81
|
-
|
99
|
+
def assign_filterrific_default_filter_params(opts)
|
100
|
+
self.filterrific_default_filter_params = (
|
101
|
+
opts['default_filter_params'] || {}
|
82
102
|
).stringify_keys
|
83
103
|
end
|
84
104
|
|
85
|
-
def
|
86
|
-
# Raise exception if defaults contain keys that are not present in
|
105
|
+
def validate_filterrific_default_filter_params
|
106
|
+
# Raise exception if defaults contain keys that are not present in available_filters
|
87
107
|
if (
|
88
|
-
|
108
|
+
inv_fdfps = filterrific_default_filter_params.keys - filterrific_available_filters
|
89
109
|
).any?
|
90
|
-
raise(ArgumentError, "Invalid default
|
110
|
+
raise(ArgumentError, "Invalid default filter params: #{ inv_fdfps.inspect }")
|
91
111
|
end
|
92
112
|
end
|
93
113
|
|
data/lib/filterrific/engine.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
1
3
|
require 'filterrific/param_set'
|
4
|
+
|
5
|
+
require 'filterrific/action_controller_extension'
|
2
6
|
require 'filterrific/action_view_extension'
|
3
7
|
require 'filterrific/active_record_extension'
|
4
8
|
|
@@ -10,14 +14,18 @@ module Filterrific
|
|
10
14
|
|
11
15
|
isolate_namespace Filterrific
|
12
16
|
|
13
|
-
ActiveSupport.on_load :
|
14
|
-
|
17
|
+
ActiveSupport.on_load :action_controller do
|
18
|
+
include Filterrific::ActionControllerExtension
|
15
19
|
end
|
16
20
|
|
17
21
|
ActiveSupport.on_load :action_view do
|
18
22
|
include Filterrific::ActionViewExtension
|
19
23
|
end
|
20
24
|
|
25
|
+
ActiveSupport.on_load :active_record do
|
26
|
+
extend Filterrific::ActiveRecordExtension
|
27
|
+
end
|
28
|
+
|
21
29
|
initializer "filterrific" do |app|
|
22
30
|
app.config.assets.precompile += %w(filterrific-spinner.gif)
|
23
31
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
1
3
|
require 'active_support/all'
|
2
4
|
require 'digest/sha1'
|
3
5
|
|
@@ -6,35 +8,44 @@ module Filterrific
|
|
6
8
|
# FilterParamSet is a container to store FilterParams
|
7
9
|
class ParamSet
|
8
10
|
|
9
|
-
attr_accessor :
|
11
|
+
attr_accessor :model_class
|
10
12
|
attr_accessor :select_options
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
+
# Initializes a new Filterrific::ParamSet. This is the core of Filterrific
|
15
|
+
# where all the action happens.
|
16
|
+
# @param a_model_class [Class] the class you want to filter records of
|
17
|
+
# @param filterrific_params [Hash, optional] the filter params, uses
|
18
|
+
# model_class' default_settings
|
19
|
+
# @return [Filterrific::ParamSet]
|
20
|
+
def initialize(a_model_class, filterrific_params = {})
|
21
|
+
self.model_class = a_model_class
|
14
22
|
@select_options = {}
|
15
23
|
|
16
24
|
# Use either passed in filterrific_params or resource class' default_settings.
|
17
25
|
# Don't merge the hashes. This causes trouble if an option is set to nil
|
18
26
|
# by the user, then it will be overriden by default_settings.
|
19
27
|
# You might wonder "what if I want to change only one thing from the defaults?"
|
20
|
-
# Persistence, baby. By the time you submit changes to one
|
28
|
+
# Persistence, baby. By the time you submit changes to one filter, all the others
|
21
29
|
# will be already initialized with the defaults.
|
22
|
-
filterrific_params =
|
30
|
+
filterrific_params = model_class.filterrific_default_settings if filterrific_params.blank?
|
23
31
|
filterrific_params.stringify_keys!
|
24
32
|
filterrific_params = condition_filterrific_params(filterrific_params)
|
25
|
-
|
33
|
+
define_and_assign_attr_accessors_for_each_filter(filterrific_params)
|
26
34
|
end
|
27
35
|
|
28
|
-
# A shortcut to run the ActiveRecord query on
|
29
|
-
# you want to start with the
|
36
|
+
# A shortcut to run the ActiveRecord query on model_class. Use this if
|
37
|
+
# you want to start with the model_class, and not an existing ActiveRecord::Relation.
|
38
|
+
# Allows `@filterrific.find` in controller instead of
|
39
|
+
# `ModelClass.filterrific_find(@filterrific)`
|
30
40
|
def find
|
31
|
-
|
41
|
+
model_class.filterrific_find(self)
|
32
42
|
end
|
33
43
|
|
34
44
|
# Returns Filterrific::ParamSet as hash (used for URL params and serialization)
|
45
|
+
# @return [Hash] with stringified keys
|
35
46
|
def to_hash
|
36
47
|
{}.tap { |h|
|
37
|
-
|
48
|
+
model_class.filterrific_available_filters.each do |filter_name|
|
38
49
|
param_value = self.send(filter_name)
|
39
50
|
case
|
40
51
|
when param_value.blank?
|
@@ -49,27 +60,16 @@ module Filterrific
|
|
49
60
|
}
|
50
61
|
end
|
51
62
|
|
63
|
+
# Returns params as JSON string.
|
64
|
+
# @return [String]
|
52
65
|
def to_json
|
53
66
|
to_hash.to_json
|
54
67
|
end
|
55
68
|
|
56
|
-
# Returns a signature that is unique to self's params
|
57
|
-
def signature
|
58
|
-
Digest::SHA1.hexdigest(to_hash.to_a.sort.to_s)
|
59
|
-
end
|
60
|
-
|
61
|
-
# Returns true if this Filterrific::ParamSet is not the model's default.
|
62
|
-
# TODO: this doesn't work for procs. I need to evaluate the
|
63
|
-
# filterrific_default_settings before comparing them to to_hash.
|
64
|
-
#
|
65
|
-
# def customized?
|
66
|
-
# resource_class.filterrific_default_settings != to_hash
|
67
|
-
# end
|
68
|
-
|
69
69
|
protected
|
70
70
|
|
71
|
-
# Conditions params
|
72
|
-
# @param[Hash]
|
71
|
+
# Conditions params: Evaluates Procs and type casts integer values.
|
72
|
+
# @param fp [Hash] the filterrific params hash
|
73
73
|
# @return[Hash] the conditioned params hash
|
74
74
|
def condition_filterrific_params(fp)
|
75
75
|
fp.each do |key, val|
|
@@ -88,11 +88,11 @@ module Filterrific
|
|
88
88
|
fp
|
89
89
|
end
|
90
90
|
|
91
|
-
# Defines attr accessors for each
|
92
|
-
# values based on fp
|
93
|
-
# @param[Hash]
|
94
|
-
def
|
95
|
-
|
91
|
+
# Defines attr accessors for each available_filter on self and assigns
|
92
|
+
# values based on fp.
|
93
|
+
# @param fp [Hash] filterrific_params with stringified keys
|
94
|
+
def define_and_assign_attr_accessors_for_each_filter(fp)
|
95
|
+
model_class.filterrific_available_filters.each do |filter_name|
|
96
96
|
self.class.send(:attr_accessor, filter_name)
|
97
97
|
v = fp[filter_name]
|
98
98
|
self.send("#{ filter_name }=", v) if v.present?
|
data/lib/filterrific/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: filterrific
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jo Hund
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - '>='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.7.3
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 0.7.3
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -70,14 +70,14 @@ dependencies:
|
|
70
70
|
name: wwtd
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - '>='
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: 0.5.5
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- -
|
80
|
+
- - '>='
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 0.5.5
|
83
83
|
description: Filterrific is a Rails Engine plugin that makes it easy to filter, search,
|
@@ -101,9 +101,9 @@ files:
|
|
101
101
|
- doc/development_notes/model_api.rb
|
102
102
|
- doc/development_notes/view_api.txt
|
103
103
|
- doc/meta.md
|
104
|
-
- doc/rails_conf_talk.md
|
105
104
|
- doc/scratchpad.md
|
106
105
|
- lib/filterrific.rb
|
106
|
+
- lib/filterrific/action_controller_extension.rb
|
107
107
|
- lib/filterrific/action_view_extension.rb
|
108
108
|
- lib/filterrific/active_record_extension.rb
|
109
109
|
- lib/filterrific/engine.rb
|
@@ -126,7 +126,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
126
|
requirements:
|
127
127
|
- - '>='
|
128
128
|
- !ruby/object:Gem::Version
|
129
|
-
version:
|
129
|
+
version: 1.9.3
|
130
130
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
131
|
requirements:
|
132
132
|
- - '>='
|
data/doc/rails_conf_talk.md
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
# Filterrific Rails Conf Talk
|
2
|
-
|
3
|
-
|
4
|
-
Topics to cover:
|
5
|
-
|
6
|
-
* filter and sort ActiveRecord lists end to end with lots of code
|
7
|
-
* AR scope patterns
|
8
|
-
* advanced use cases
|
9
|
-
* export to CSV/XLS
|
10
|
-
* saved searches
|
11
|
-
* full text search
|
12
|
-
* multiple form inputs for single scope
|
13
|
-
* checkbox arrays (collection_check_boxes)
|
14
|
-
* filter persistence strategies
|
15
|
-
* session
|
16
|
-
* none (query param)
|
17
|
-
* database (saved searches)
|
18
|
-
|
19
|
-
Target audience:
|
20
|
-
|
21
|
-
* intermediate
|
22
|
-
|
23
|
-
|
24
|
-
Title ideas:
|
25
|
-
|
26
|
-
* Tame/awesomify your ActiveRecord lists with Filterrific
|
27
|
-
* Dive deep into ActiveRecord scopes, and learn about Filterrific
|
28
|
-
* Slice and dice your data in many ways with AR scopes and Filterrific
|
29
|
-
|
30
|
-
Other talk ideas (possibly lightning talks?):
|
31
|
-
|
32
|
-
* How to safely mutate objects in Rails
|
33
|
-
* Service objects, DCI, processors: all dependencies go in one direction
|