filterrific 1.0.1 → 1.1.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.
@@ -20,8 +20,15 @@ def index
20
20
  * :debug => false # if true, prints out debug info. This also exists in view helper. Maybe one to
21
21
  logger/STDOUT, the other to view?
22
22
  * :param_prefix => "filterrific" # the param prefix used to shuttle params between view and controller.
23
-
23
+
24
24
  @publications = Publication.filterrific_find(@filterrific).paginate....
25
25
  @publications = current_user.publications.filterrific_find(@filterrific).paginate....
26
26
  ...
27
27
  end
28
+
29
+ @filterrific = Filterrific.new(User, params_hash)
30
+ @users = User.filterrific_find(@filterrific).where(...)
31
+
32
+ @filterrific = Filterrific.new(self, User)
33
+ @filterrific = filterrific_init(User, options) (loads and persists in session)
34
+ @users = User.filterrific_find(@filterrific).paginate(:page => params[:page])
data/doc/meta.md ADDED
@@ -0,0 +1,27 @@
1
+ Workflow to Maintain This Gem
2
+ =============================
3
+
4
+ I use the gem-release gem
5
+
6
+ For more info see: https://github.com/svenfuchs/gem-release#usage
7
+
8
+ Steps for an update
9
+ -------------------
10
+
11
+ 1. Update code and commit it.
12
+ 2. Add entry to CHANGELOG:
13
+ * h1 for major release
14
+ * h2 for minor release
15
+ * h3 for patch release
16
+ 3. Bump the version with one of these commands:
17
+ * `gem bump --version 1.1.1` # Bump the gem version to the given version number
18
+ * `gem bump --version major` # 0.0.1 -> 1.0.0
19
+ * `gem bump --version minor` # 0.0.1 -> 0.1.0
20
+ * `gem bump --version patch` # 0.0.1 -> 0.0.2
21
+ 4. Release it.
22
+ * `gem release`
23
+ 5. Create a git tag and push to origin.
24
+ `gem tag`
25
+
26
+
27
+ http://prioritized.net/blog/gemify-assets-for-rails/
data/lib/filterrific.rb CHANGED
@@ -1,11 +1,5 @@
1
- # = The Rails User Interface Solution for Filtering Your ActiveRecord Lists
2
- #
3
- module Filterrific
4
-
5
- if defined?(Rails) && Rails::VERSION::MAJOR == 3
6
- require 'filterrific/railtie'
7
- else
8
- raise "Filterrific requires Rails 3"
9
- end
1
+ require 'filterrific/version'
2
+ require 'filterrific/engine'
10
3
 
4
+ module Filterrific
11
5
  end
@@ -0,0 +1,15 @@
1
+ #
2
+ # Adds view helpers to ActionView
3
+ #
4
+ module Filterrific::ActionViewExtension
5
+
6
+ # Renders a spinner while the list is being updated
7
+ def render_filterrific_spinner
8
+ %(
9
+ <span class="filterrific_spinner" style="display:none;">
10
+ #{ image_tag('filterrific-spinner.gif') }
11
+ </span>
12
+ ).html_safe
13
+ end
14
+
15
+ end
@@ -0,0 +1,71 @@
1
+ #
2
+ # Adds filterrific methods to ActiveRecord::Base and sub classes.
3
+ #
4
+ require 'filterrific/param_set'
5
+
6
+ module Filterrific::ActiveRecordExtension
7
+
8
+ module ClassMethods
9
+
10
+ # Adds filterrific behavior to class when called like so:
11
+ #
12
+ # filterrific(
13
+ # :default_settings => { :sorted_by => "created_at_asc" },
14
+ # :filter_names => [:sorted_by, :search_query, :with_state]
15
+ # )
16
+ #
17
+ # @params[Hash] options
18
+ # Required keys are:
19
+ # * :filter_names: a list of filter_names to be exposed by Filterrific
20
+ # Optional keys are:
21
+ # * :default_settings: default filter settings
22
+ def filterrific(options)
23
+ cattr_accessor :filterrific_default_settings
24
+ cattr_accessor :filterrific_filter_names
25
+
26
+ options.stringify_keys!
27
+
28
+ # Raise exception if not filter_names are given
29
+ self.filterrific_filter_names = (
30
+ options['filter_names'] || options['scope_names'] || []
31
+ ).map { |e| e.to_s }
32
+ raise(ArgumentError, ":filter_names can't be empty") if filterrific_filter_names.blank?
33
+
34
+ self.filterrific_default_settings = (
35
+ options['default_settings'] || options['defaults'] || {}
36
+ ).stringify_keys
37
+ # Raise exception if defaults contain keys that are not present in filter_names
38
+ if (
39
+ invalid_defaults = (filterrific_default_settings.keys - filterrific_filter_names)
40
+ ).any?
41
+ raise(ArgumentError, "Invalid default keys: #{ invalid_defaults.inspect }")
42
+ end
43
+ end
44
+
45
+ # Returns ActiveRecord relation based on given filterrific_param_set.
46
+ # Use like so:
47
+ # ModelClass.filterrific_find(@filterrific_param_set)
48
+ #
49
+ # @param[Filterrific::ParamSet] filterrific_param_set
50
+ # @return[ActiveRecord::Relation] an ActiveRecord relation.
51
+ def filterrific_find(filterrific_param_set)
52
+ unless filterrific_param_set.is_a?(Filterrific::ParamSet)
53
+ raise(ArgumentError, "Invalid Filterrific::ParamSet: #{ filterrific_param_set.inspect }")
54
+ end
55
+
56
+ # set initial ar_proxy to including class
57
+ ar_proxy = self
58
+
59
+ # apply filterrific params
60
+ self.filterrific_filter_names.each do |filter_name|
61
+ filter_param = filterrific_param_set.send(filter_name)
62
+ next if filter_param.blank? # skip blank filter_params
63
+ ar_proxy = ar_proxy.send(filter_name, filter_param)
64
+ end
65
+
66
+ ar_proxy
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,26 @@
1
+ require 'rails'
2
+
3
+ module Filterrific
4
+ class Engine < ::Rails::Engine
5
+
6
+ # It's an engine so that we can add javascript and image assets
7
+ # to the asset pipeline.
8
+
9
+ require 'filterrific/param_set'
10
+
11
+ initializer "filterrific.active_record_extension" do |app|
12
+ require 'filterrific/active_record_extension'
13
+ class ::ActiveRecord::Base
14
+ extend Filterrific::ActiveRecordExtension::ClassMethods
15
+ end
16
+ end
17
+
18
+ initializer "filterrific.action_view_extension" do |app|
19
+ require 'filterrific/action_view_extension'
20
+ class ::ActionView::Base
21
+ include Filterrific::ActionViewExtension
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -1,21 +1,23 @@
1
+ require 'active_support/all'
2
+
1
3
  module Filterrific
2
4
 
3
- # FilterParamSet is a container to store FilterParams for a resource class that is filterrific
5
+ # FilterParamSet is a container to store FilterParams
4
6
  class ParamSet
5
7
 
6
8
  attr_accessor :resource_class
7
9
 
8
- def initialize(resource_class, filterrific_params = {})
10
+ def initialize(a_resource_class, filterrific_params = {})
9
11
 
10
- self.resource_class = resource_class
12
+ self.resource_class = a_resource_class
11
13
 
12
- # use either passed in options or resource class' default list_options
13
- # don't merge them. This causes trouble if an option is set to nil
14
- # by the user, then it will be overriden by default list_options
14
+ # Use either passed in filterrific_params or resource class' default_settings.
15
+ # Don't merge the hashes. This causes trouble if an option is set to nil
16
+ # by the user, then it will be overriden by default_settings.
15
17
  # You might wonder "what if I want to change only one thing from the defaults?"
16
18
  # Persistence, baby. By the time you submit changes to one dimension, all the others
17
19
  # will be already initialized with the defaults.
18
- filterrific_params = resource_class.default_filterrific_params if filterrific_params.blank?
20
+ filterrific_params = resource_class.filterrific_default_settings if filterrific_params.blank?
19
21
 
20
22
  # force all keys to strings
21
23
  filterrific_params.stringify_keys!
@@ -27,47 +29,54 @@ module Filterrific
27
29
  # evaulate Procs
28
30
  filterrific_params[key] = val.call
29
31
  when val.is_a?(Array)
30
- # type cast integers
32
+ # type cast integers in the array
31
33
  filterrific_params[key] = filterrific_params[key].map { |e| e =~ /^\d+$/ ? e.to_i : e }
32
34
  when val =~ /^\d+$/
33
- # type cast integers
35
+ # type cast integer
34
36
  filterrific_params[key] = filterrific_params[key].to_i
35
37
  end
36
38
  end
37
39
 
38
- # Define attr_accessor for each FilterrificScope
40
+ # Define attr_accessor for each filterrific_filter_name
39
41
  # on Filterrific::ParamSet instance and assign values from options
40
- resource_class.filterrific_scope_names.each do |scope_name|
41
- self.class.send(:attr_accessor, scope_name)
42
- v = filterrific_params[scope_name.to_s]
43
- self.send("#{ scope_name }=", v) if v.present?
42
+ resource_class.filterrific_filter_names.each do |filter_name|
43
+ self.class.send(:attr_accessor, filter_name)
44
+ v = filterrific_params[filter_name]
45
+ self.send("#{ filter_name }=", v) if v.present?
44
46
  end
45
47
 
46
48
  end
47
49
 
48
- # Returns FilterrificParams as hash (used for URL params and serialization)
50
+ # Returns Filterrific::ParamSet as hash (used for URL params and serialization)
49
51
  def to_hash
50
52
  {}.tap { |h|
51
- resource_class.filterrific_scope_names.each do |scope_name|
52
- param_value = self.send(scope_name)
53
+ resource_class.filterrific_filter_names.each do |filter_name|
54
+ param_value = self.send(filter_name)
53
55
  case
54
56
  when param_value.blank?
55
57
  # do nothing
56
58
  when param_value.is_a?(Proc)
57
59
  # evaluate Proc so it can be serialized
58
- h[scope_name.to_s] = param_value.call
60
+ h[filter_name] = param_value.call
59
61
  else
60
- h[scope_name.to_s] = param_value
62
+ h[filter_name] = param_value
61
63
  end
62
64
  end
63
65
  }
64
66
  end
65
67
 
66
- # Returns true if this Filterrific::ParamSet is not the model's default.
67
- def customized?
68
- resource_class.default_filterrific_params != to_hash
68
+ def to_json
69
+ to_hash.to_json
69
70
  end
70
71
 
72
+ # Returns true if this Filterrific::ParamSet is not the model's default.
73
+ # TODO: this doesn't work for procs. I need to evaluate the
74
+ # filterrific_default_settings before comparing them to to_hash.
75
+ #
76
+ # def customized?
77
+ # resource_class.filterrific_default_settings != to_hash
78
+ # end
79
+
71
80
  end
72
81
 
73
82
  end
@@ -0,0 +1,3 @@
1
+ module Filterrific
2
+ VERSION = "1.1.0"
3
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'filterrific/action_view_extension'
3
+
4
+ class ViewContext
5
+
6
+ include Filterrific::ActionViewExtension
7
+
8
+ end
9
+
10
+ describe Filterrific::ActionViewExtension do
11
+
12
+ it "renders filterrific spinner" do
13
+ ViewContext.new.should respond_to(:render_filterrific_spinner)
14
+ end
15
+
16
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+ require 'active_record'
3
+ require 'filterrific/active_record_extension'
4
+ ::ActiveRecord::Base.extend Filterrific::ActiveRecordExtension::ClassMethods
5
+
6
+ # Container for test data
7
+ class TestData
8
+
9
+ def self.filterrific_filter_names
10
+ %w[sorted_by search_query with_country_id]
11
+ end
12
+
13
+ def self.filterrific_default_settings
14
+ { 'sorted_by' => 'name_asc' }
15
+ end
16
+
17
+ end
18
+
19
+ describe Filterrific::ActiveRecordExtension do
20
+
21
+ let(:filterrific_class){
22
+ Class.new(ActiveRecord::Base) do
23
+ filterrific(
24
+ :filter_names => TestData.filterrific_filter_names,
25
+ :default_settings => TestData.filterrific_default_settings
26
+ )
27
+ end
28
+ }
29
+
30
+ describe "Class method extensions" do
31
+
32
+ it "adds a 'filterrific' class method" do
33
+ filterrific_class.should respond_to(:filterrific)
34
+ end
35
+
36
+ it "adds a 'filterrific_find' class method" do
37
+ filterrific_class.should respond_to(:filterrific_find)
38
+ end
39
+
40
+ end
41
+
42
+ describe "Filterrific initialization" do
43
+
44
+ it "initializes filterrific_filter_names" do
45
+ filterrific_class.filterrific_filter_names.should == TestData.filterrific_filter_names
46
+ end
47
+
48
+ it "initializes filterrific_default_settings" do
49
+ filterrific_class.filterrific_default_settings.should == TestData.filterrific_default_settings
50
+ end
51
+
52
+ it "raises when no filter_names are given" do
53
+ expect {
54
+ Class.new(ActiveRecord::Base) do
55
+ filterrific(
56
+ :filter_names => []
57
+ )
58
+ end
59
+ }.to raise_error(ArgumentError)
60
+ end
61
+
62
+ it "raises when default_settings contains keys that are not in filter_names" do
63
+ expect {
64
+ Class.new(ActiveRecord::Base) do
65
+ filterrific(
66
+ :filter_names => [:one, :two],
67
+ :default_settings => { :three => '' }
68
+ )
69
+ end
70
+ }.to raise_error(ArgumentError)
71
+ end
72
+
73
+ end
74
+
75
+ describe "filterrific_find" do
76
+
77
+ it "raises when given invalid params" do
78
+ expect {
79
+ filterrific_class.filterrific_find('an invalid argument')
80
+ }.to raise_error(ArgumentError)
81
+ end
82
+
83
+ end
84
+
85
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Filterrific do
4
+ pending "write it"
5
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+ require 'filterrific/param_set'
3
+
4
+ # Container for test data
5
+ class TestData
6
+
7
+ def self.filterrific_filter_names
8
+ %w[
9
+ filter_proc
10
+ filter_array_int
11
+ filter_array_string
12
+ filter_int
13
+ filter_string
14
+ ]
15
+ end
16
+
17
+ def self.filterrific_default_settings
18
+ { 'filter_int' => 42 }
19
+ end
20
+
21
+ def self.filterrific_params
22
+ {
23
+ 'filter_proc' => lambda { 1 + 1 },
24
+ 'filter_array_int' => %w[1 2 3],
25
+ 'filter_array_string' => %w[one two three],
26
+ 'filter_int' => '42',
27
+ 'filter_string' => 'forty-two'
28
+ }
29
+ end
30
+
31
+ def self.filterrific_params_after_sanitizing
32
+ {
33
+ 'filter_proc' => 2,
34
+ 'filter_array_int' => [1, 2, 3],
35
+ 'filter_array_string' => %w[one two three],
36
+ 'filter_int' => 42,
37
+ 'filter_string' => 'forty-two'
38
+ }
39
+ end
40
+
41
+ end
42
+
43
+ # Simulates a class that would include the filterrific directive
44
+ class ResourceClass
45
+
46
+ def self.filterrific_default_settings
47
+ TestData.filterrific_default_settings
48
+ end
49
+ def self.filterrific_filter_names
50
+ TestData.filterrific_filter_names
51
+ end
52
+
53
+ end
54
+
55
+ describe Filterrific::ParamSet do
56
+
57
+ let(:filterrific_param_set){
58
+ Filterrific::ParamSet.new(ResourceClass, TestData.filterrific_params)
59
+ }
60
+
61
+ describe "initialization" do
62
+
63
+ it "assigns resource class" do
64
+ filterrific_param_set.resource_class.should == ResourceClass
65
+ end
66
+
67
+ describe "dynamic filter_name attr_accessors" do
68
+
69
+ TestData.filterrific_filter_names.each do |filter_name|
70
+
71
+ it "defines a getter for '#{ filter_name }'" do
72
+ filterrific_param_set.should respond_to(filter_name)
73
+ end
74
+
75
+ it "defines a setter for '#{ filter_name }'" do
76
+ filterrific_param_set.should respond_to("#{ filter_name }=")
77
+ end
78
+
79
+ end
80
+
81
+ TestData.filterrific_params.keys.each do |key|
82
+
83
+ it "assigns sanitized param to '#{ key }' attr" do
84
+ filterrific_param_set.send(key).should == TestData.filterrific_params_after_sanitizing[key]
85
+ end
86
+
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+
93
+ describe "to_hash" do
94
+
95
+ it "returns all filterrific_params as hash" do
96
+ filterrific_param_set.to_hash.should == TestData.filterrific_params_after_sanitizing
97
+ end
98
+
99
+ end
100
+
101
+ describe "to_json" do
102
+
103
+ it "returns all filterrific_params as json string" do
104
+ filterrific_param_set.to_json.should == TestData.filterrific_params_after_sanitizing.to_json
105
+ end
106
+
107
+ end
108
+
109
+ end