filterrific 1.4.3 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 |[![Build Status](https://travis-ci.org/jhund/filterrific_demo.svg?branch=rails-4.1)](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 |[![Build Status](https://travis-ci.org/jhund/filterrific_demo.svg?branch=rails-4.0)](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 |[![Build Status](https://travis-ci.org/jhund/filterrific_demo.svg?branch=rails-4.1)](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 |[![Build Status](https://travis-ci.org/jhund/filterrific_demo.svg?branch=rails-4.0)](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 |[![Build Status](https://travis-ci.org/jhund/filterrific_demo.svg?branch=rails-3.2)](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
|
[![Code Climate](https://codeclimate.com/github/jhund/filterrific.png)](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
|