filter_fu 0.5.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.
- data/.document +5 -0
- data/.gitignore +6 -0
- data/LICENSE +20 -0
- data/README.rdoc +75 -0
- data/Rakefile +60 -0
- data/VERSION +1 -0
- data/filter_fu.gemspec +68 -0
- data/lib/filter_fu/active_record.rb +62 -0
- data/lib/filter_fu/view_helper.rb +50 -0
- data/lib/filter_fu.rb +4 -0
- data/rails/init.rb +1 -0
- data/spec/active_record_spec.rb +159 -0
- data/spec/db/database.yml +3 -0
- data/spec/db/schema.rb +10 -0
- data/spec/dummies.rb +46 -0
- data/spec/filter_fu_spec.rb +13 -0
- data/spec/fixtures/employee.rb +14 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/view_helper_spec.rb +127 -0
- metadata +90 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Benedikt Deicke
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
= filter_fu
|
2
|
+
|
3
|
+
This Ruby on Rails plugin adds a filtered_by method to your models. It accepts a hash of filters that are applied using named_scopes. In addition the plugin adds some view helpers to easily build filter forms.
|
4
|
+
|
5
|
+
== Install
|
6
|
+
|
7
|
+
You can install filter_fu as a gem or as a rails plugin.
|
8
|
+
|
9
|
+
=== Install as gem
|
10
|
+
|
11
|
+
Add this line to your <tt>config/environment.rb</tt> and run <tt>rake gems:install</tt>
|
12
|
+
|
13
|
+
config.gem 'filter_fu', :source => 'http://www.gemcutter.org'
|
14
|
+
|
15
|
+
=== Install as plugin
|
16
|
+
|
17
|
+
Run this command in your currrent project's +RAILS_ROOT+
|
18
|
+
|
19
|
+
./script/plugin install git://github.com/benedikt/filter_fu.git
|
20
|
+
|
21
|
+
== Usage
|
22
|
+
|
23
|
+
=== Models
|
24
|
+
|
25
|
+
To enable filter_fu on a model, simply add one line to its definition:
|
26
|
+
|
27
|
+
class Project
|
28
|
+
filter_fu
|
29
|
+
end
|
30
|
+
|
31
|
+
Let's say you don't want your Projects list filtered by its +hidden+ column. You can easly
|
32
|
+
tell filter_fu to ignore certian named scopes or columns:
|
33
|
+
|
34
|
+
class Project
|
35
|
+
filter_fu :except => [:hidden]
|
36
|
+
end
|
37
|
+
|
38
|
+
Of course this also works the other way round using the <tt>:only</tt> option to only allow filtering
|
39
|
+
for the given named scopes or columns:
|
40
|
+
|
41
|
+
class Project
|
42
|
+
filter_fu :only => [:starts_on, :ends_on]
|
43
|
+
end
|
44
|
+
|
45
|
+
Once you enabled filter_fu on your model it provides a filtered_by method.
|
46
|
+
|
47
|
+
Project.filtered_by(params[:filter])
|
48
|
+
Project.filtered_by(params[:filter]).some.named_scopes
|
49
|
+
Project.some.other.named_scopes.filtered_by(params[:filter])
|
50
|
+
|
51
|
+
|
52
|
+
=== Helper
|
53
|
+
|
54
|
+
filter_fu comes with helpers to simplify the generation of filter forms:
|
55
|
+
|
56
|
+
<% filter_form do |f| %>
|
57
|
+
# f is a ActionView::Helpers::FormBuilder
|
58
|
+
<% end %>
|
59
|
+
|
60
|
+
You can also specify a name for the filter (Default is <tt>:filter</tt>). This way you're able to have multiple filters on the same page.
|
61
|
+
|
62
|
+
<% filter_form_for(:product_filter) do |f| %>
|
63
|
+
# f is a ActionView::Helpers::FormBuilder
|
64
|
+
<% end %>
|
65
|
+
|
66
|
+
filter_fu will automatically include all other parameters for the current page (ie. pagination parameters) within the form.
|
67
|
+
If you wish to explicitly exclude some you can do this by passing an array as :ignore_parameters option.
|
68
|
+
|
69
|
+
<% filter_form_for(:product_filter, :ignore_parameters => [:page]) do |f| %>
|
70
|
+
# f is a ActionView::Helpers::FormBuilder
|
71
|
+
<% end %>
|
72
|
+
|
73
|
+
== Copyright
|
74
|
+
|
75
|
+
Copyright (c) 2009 Benedikt Deicke. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "filter_fu"
|
8
|
+
gem.summary = %Q{Filter ActiveRecord models using named_scopes}
|
9
|
+
gem.description = %Q{This Ruby on Rails plugin adds a filtered_by method to your models. It accepts a hash of filters that are applied using named_scopes. In addition the plugin adds some view helpers to easily build filter forms.}
|
10
|
+
gem.email = "benedikt@synatic.net"
|
11
|
+
gem.homepage = "http://github.com/benedikt/filter_fu"
|
12
|
+
gem.authors = ["Benedikt Deicke"]
|
13
|
+
gem.add_development_dependency "rspec"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'spec/rake/spectask'
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
23
|
+
spec.libs << 'lib' << 'spec'
|
24
|
+
spec.spec_opts = ['--options', 'spec/spec.opts']
|
25
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
26
|
+
end
|
27
|
+
|
28
|
+
namespace :spec do
|
29
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
30
|
+
spec.libs << 'lib'
|
31
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
32
|
+
spec.rcov = true
|
33
|
+
spec.rcov_opts = ["--exclude", "^/,^spec/"]
|
34
|
+
end
|
35
|
+
|
36
|
+
Spec::Rake::SpecTask.new(:doc) do |spec|
|
37
|
+
spec.libs << 'lib' << 'spec'
|
38
|
+
spec.spec_opts = ["--color", "--format", "specdoc"]
|
39
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
task :spec => :check_dependencies
|
44
|
+
|
45
|
+
task :default => :spec
|
46
|
+
|
47
|
+
require 'rake/rdoctask'
|
48
|
+
Rake::RDocTask.new do |rdoc|
|
49
|
+
if File.exist?('VERSION')
|
50
|
+
version = File.read('VERSION')
|
51
|
+
else
|
52
|
+
version = ""
|
53
|
+
end
|
54
|
+
|
55
|
+
rdoc.rdoc_dir = 'rdoc'
|
56
|
+
rdoc.title = "filter_fu #{version}"
|
57
|
+
rdoc.rdoc_files.include('README*')
|
58
|
+
rdoc.rdoc_files.include('LICENSE*')
|
59
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
60
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.5.0
|
data/filter_fu.gemspec
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{filter_fu}
|
8
|
+
s.version = "0.5.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Benedikt Deicke"]
|
12
|
+
s.date = %q{2009-10-15}
|
13
|
+
s.description = %q{This Ruby on Rails plugin adds a filtered_by method to your models. It accepts a hash of filters that are applied using named_scopes. In addition the plugin adds some view helpers to easily build filter forms.}
|
14
|
+
s.email = %q{benedikt@synatic.net}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"filter_fu.gemspec",
|
27
|
+
"lib/filter_fu.rb",
|
28
|
+
"lib/filter_fu/active_record.rb",
|
29
|
+
"lib/filter_fu/view_helper.rb",
|
30
|
+
"rails/init.rb",
|
31
|
+
"spec/active_record_spec.rb",
|
32
|
+
"spec/db/database.yml",
|
33
|
+
"spec/db/schema.rb",
|
34
|
+
"spec/dummies.rb",
|
35
|
+
"spec/filter_fu_spec.rb",
|
36
|
+
"spec/fixtures/employee.rb",
|
37
|
+
"spec/spec.opts",
|
38
|
+
"spec/spec_helper.rb",
|
39
|
+
"spec/view_helper_spec.rb"
|
40
|
+
]
|
41
|
+
s.homepage = %q{http://github.com/benedikt/filter_fu}
|
42
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
43
|
+
s.require_paths = ["lib"]
|
44
|
+
s.rubygems_version = %q{1.3.5}
|
45
|
+
s.summary = %q{Filter ActiveRecord models using named_scopes}
|
46
|
+
s.test_files = [
|
47
|
+
"spec/active_record_spec.rb",
|
48
|
+
"spec/db/schema.rb",
|
49
|
+
"spec/dummies.rb",
|
50
|
+
"spec/filter_fu_spec.rb",
|
51
|
+
"spec/fixtures/employee.rb",
|
52
|
+
"spec/spec_helper.rb",
|
53
|
+
"spec/view_helper_spec.rb"
|
54
|
+
]
|
55
|
+
|
56
|
+
if s.respond_to? :specification_version then
|
57
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
58
|
+
s.specification_version = 3
|
59
|
+
|
60
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
61
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
62
|
+
else
|
63
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
64
|
+
end
|
65
|
+
else
|
66
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module FilterFu
|
2
|
+
module ActiveRecord
|
3
|
+
|
4
|
+
def self.included(base) # :nodoc:
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
VALID_FILTER_OPTIONS = [:only, :except]
|
11
|
+
|
12
|
+
def filter_fu(opts = {})
|
13
|
+
opts = opts.symbolize_keys!
|
14
|
+
opts.each_key { |option| raise "Invalid filter_fu option: #{option}" unless VALID_FILTER_OPTIONS.include?(option) }
|
15
|
+
raise "Use either :only or :except as a filter_fu option." if opts.has_key?(:only) && opts.has_key?(:except)
|
16
|
+
|
17
|
+
opts[:only] = [opts[:only]].flatten.collect(&:to_sym) if opts[:only]
|
18
|
+
opts[:except] = [opts[:except]].flatten.collect(&:to_sym) if opts[:except]
|
19
|
+
|
20
|
+
@filter_options = opts
|
21
|
+
|
22
|
+
extend SingletonMethods
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
module SingletonMethods
|
28
|
+
|
29
|
+
def filtered_by(filter)
|
30
|
+
return scoped({}) if !filter || filter.empty?
|
31
|
+
|
32
|
+
filter.inject(self) do |memo, (scope, arg)|
|
33
|
+
scope = scope.to_sym
|
34
|
+
next if protected?(scope)
|
35
|
+
if scopes.has_key?(scope)
|
36
|
+
memo.send(scope, arg)
|
37
|
+
else
|
38
|
+
memo.scoped(build_anonymous_scope(scope, arg))
|
39
|
+
end
|
40
|
+
end || scoped({})
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def build_anonymous_scope(scope, arg)
|
46
|
+
return {} unless column_names.include?(scope.to_s) && !arg.blank?
|
47
|
+
{ :conditions => { scope => arg } }
|
48
|
+
end
|
49
|
+
|
50
|
+
def protected?(scope)
|
51
|
+
if @filter_options.has_key?(:only)
|
52
|
+
return !@filter_options[:only].include?(scope)
|
53
|
+
elsif @filter_options.has_key?(:except)
|
54
|
+
return @filter_options[:except].include?(scope)
|
55
|
+
end
|
56
|
+
return false
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module FilterFu
|
4
|
+
module ViewHelper
|
5
|
+
|
6
|
+
def self.included(base) # :nodoc:
|
7
|
+
base.send :include, InstanceMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
|
12
|
+
def filter_form_for(*args, &block)
|
13
|
+
raise ArgumentError, 'Missing block' unless block_given?
|
14
|
+
|
15
|
+
opts = args.extract_options!
|
16
|
+
name = (args.first || :filter).to_sym
|
17
|
+
|
18
|
+
opts[:ignore_parameters] ||= []
|
19
|
+
opts[:ignore_parameters] += [:controller, :action, name]
|
20
|
+
|
21
|
+
opts[:html] ||= {}
|
22
|
+
opts[:html][:method] ||= :get
|
23
|
+
|
24
|
+
form_for(name, OpenStruct.new(params[name]), opts) do |f|
|
25
|
+
hidden_fields_for(params, opts)
|
26
|
+
block.call(f)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
alias_method :filter_form, :filter_form_for
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def hidden_fields_for(params, opts, prefix = nil)
|
34
|
+
params.each_pair do |k, v|
|
35
|
+
next if opts[:ignore_parameters].include?(k.to_sym)
|
36
|
+
|
37
|
+
k = "[#{k}]" if prefix
|
38
|
+
|
39
|
+
if v.kind_of?(Hash)
|
40
|
+
hidden_fields_for(v, opts, "#{prefix}#{k}")
|
41
|
+
else
|
42
|
+
concat(hidden_field_tag("#{prefix}#{k}", v))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
data/lib/filter_fu.rb
ADDED
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'filter_fu'
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/fixtures/employee')
|
3
|
+
|
4
|
+
describe FilterFu::ActiveRecord do
|
5
|
+
|
6
|
+
it "should add a filter_fu class method to ActiveRecord::Base" do
|
7
|
+
ActiveRecord::Base.should respond_to(:filter_fu)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "filter_fu" do
|
11
|
+
|
12
|
+
it "should accept an options hash" do
|
13
|
+
lambda { ActiveRecord::Base.filter_fu({}) }.should_not raise_error(ArgumentError)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should not require an options hash" do
|
17
|
+
lambda { ActiveRecord::Base.filter_fu }.should_not raise_error(ArgumentError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should add a new singleton method called filtered_by" do
|
21
|
+
Employee.should respond_to(:filtered_by)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should not accept invalid options" do
|
25
|
+
lambda { ActiveRecord::Base.filter_fu(:invalid_option => 'some value') }.should raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
%w(only except).each do |option|
|
29
|
+
it "should accept :#{option} as an option" do
|
30
|
+
lambda { ActiveRecord::Base.filter_fu(option => 'some value') }.should_not raise_error
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not accept :except and :only option at the same time" do
|
35
|
+
lambda { ActiveRecord::Base.filter_fu(:only => 'some value', :except => 'some other value') }.should raise_error(/Use either :only or :except/)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "filtered_by" do
|
41
|
+
|
42
|
+
before(:each) do
|
43
|
+
@plain_class = Class.new(Employee)
|
44
|
+
@class = @plain_class.clone
|
45
|
+
@class.filter_fu if @class.respond_to?(:filter_fu)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should require a hash with filter params" do
|
49
|
+
lambda { @class.filtered_by }.should raise_error(ArgumentError)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should not fail if the hash with filter params is nil" do
|
53
|
+
lambda { @class.filtered_by(nil) }.should_not raise_error(NoMethodError)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should not filter anything if the hash is nil" do
|
57
|
+
@class.filtered_by(nil).should == @class.all
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should not filter anything if the hash is empty" do
|
61
|
+
@class.filtered_by({}).should == @class.all
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should not return nil if all scopes were skipped" do
|
65
|
+
# This is ugly. Is there a better solution to this?
|
66
|
+
klass = @plain_class.filter_fu :only => :salary
|
67
|
+
klass.filtered_by({ :country => 'Whatever' }).should_not be_nil
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should handle HashWithIndifferentAccess correctly" do
|
71
|
+
@class.filtered_by(HashWithIndifferentAccess.new('salary' => 100000)).should == @class.all(:conditions => 'salary = 100000')
|
72
|
+
@class.filtered_by(HashWithIndifferentAccess.new('boss' => true)).should == @class.all(:conditions => "position = 'Boss'")
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should return an instace of ActiveRecord::NamedScope::Scope" do
|
76
|
+
@class.filtered_by({ :boss => nil }).class.should == ActiveRecord::NamedScope::Scope
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should call named scopes if specified in the filter params" do
|
80
|
+
@class.should_receive(:boss)
|
81
|
+
@class.filtered_by({ :boss => '' })
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should pass the value to the named scope" do
|
85
|
+
@class.should_receive(:country).with('some value')
|
86
|
+
@class.filtered_by({ :country => 'some value'})
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should only call the specified named scope if it is available" do
|
90
|
+
@class.should_not_receive(:unavailable)
|
91
|
+
@class.filtered_by({ :unavailable => '' })
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should allow multiple scopes at once and combine them" do
|
95
|
+
@class.filtered_by({ :position => 'Worker', :country => 'Country 1'}).should == @class.all(:conditions => { :position => 'Worker', :country => 'Country 1'})
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "when the given named scope is not available" do
|
99
|
+
|
100
|
+
it "should create an anonymous scope using the filter options as conditions" do
|
101
|
+
@class.filtered_by(:salary => 100000).should == @class.all(:conditions => 'salary = 100000')
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should not create an anonymous scope if there is no column for it" do
|
105
|
+
lambda { @class.filtered_by(:non_existing_column => 'some value').all }.should_not raise_error(/no such column/)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should not create an anonymous scope if the value is blank" do
|
109
|
+
@class.filtered_by(:salary => '').should == @class.all()
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should create an anonymous scope that is able to handle an array of possible values for the filter" do
|
113
|
+
@class.filtered_by(:salary => [80000, 90000]).should == @class.all(:conditions => 'salary IN (80000, 90000)')
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should not filter by scopes defined in :except option" do
|
119
|
+
klass = @plain_class.clone
|
120
|
+
klass.filter_fu :except => :dont_access_me
|
121
|
+
klass.named_scope :dont_access_me, {}
|
122
|
+
klass.named_scope :access_me, {}
|
123
|
+
|
124
|
+
klass.should_not_receive(:dont_access_me)
|
125
|
+
klass.filtered_by(:dont_access_me => '')
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should filter by scopes not defined in :except option" do
|
129
|
+
klass = @plain_class.clone
|
130
|
+
klass.filter_fu :except => :dont_access_me
|
131
|
+
klass.named_scope :dont_access_me, {}
|
132
|
+
klass.named_scope :access_me, {}
|
133
|
+
|
134
|
+
klass.should_receive(:access_me)
|
135
|
+
klass.filtered_by(:access_me => '')
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should only filter by scopes define in :only option" do
|
139
|
+
klass = @plain_class.clone
|
140
|
+
klass.filter_fu :only => :only_access_me
|
141
|
+
klass.named_scope :only_access_me, {}
|
142
|
+
klass.named_scope :dont_access_me, {}
|
143
|
+
|
144
|
+
klass.should_receive(:only_access_me)
|
145
|
+
klass.filtered_by(:only_access_me => '')
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should not filter by scopes not defined in :only option" do
|
149
|
+
klass = @plain_class.clone
|
150
|
+
klass.filter_fu :only => :only_access_me
|
151
|
+
klass.named_scope :only_access_me, {}
|
152
|
+
klass.named_scope :dont_access_me, {}
|
153
|
+
|
154
|
+
klass.should_not_receive(:dont_access_me)
|
155
|
+
klass.filtered_by(:dont_access_me => '')
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
data/spec/db/schema.rb
ADDED
data/spec/dummies.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# Taken from mislav's will_paginate http://github.com/mislav/will_paginate/tree/master
|
2
|
+
|
3
|
+
class DummyRequest
|
4
|
+
attr_accessor :symbolized_path_parameters
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@get = true
|
8
|
+
@params = {}
|
9
|
+
@symbolized_path_parameters = { :controller => 'foo', :action => 'bar' }
|
10
|
+
end
|
11
|
+
|
12
|
+
def get?
|
13
|
+
@get
|
14
|
+
end
|
15
|
+
|
16
|
+
def post
|
17
|
+
@get = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def relative_url_root
|
21
|
+
''
|
22
|
+
end
|
23
|
+
|
24
|
+
def params(more = nil)
|
25
|
+
@params.update(more) if more
|
26
|
+
@params
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class DummyController
|
31
|
+
attr_reader :request
|
32
|
+
attr_accessor :controller_name
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
@request = DummyRequest.new
|
36
|
+
@url = ActionController::UrlRewriter.new(@request, @request.params)
|
37
|
+
end
|
38
|
+
|
39
|
+
def params
|
40
|
+
@request.params
|
41
|
+
end
|
42
|
+
|
43
|
+
def url_for(params)
|
44
|
+
@url.rewrite(params)
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe FilterFu do
|
4
|
+
|
5
|
+
it "should include FilterFu::ViewHelper into ActionView::Base" do
|
6
|
+
ActionView::Base.ancestors.should include(FilterFu::ViewHelper)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should include FilterFu::ActiveRecord into ActiveRecord::Base" do
|
10
|
+
ActiveRecord::Base.ancestors.should include(FilterFu::ActiveRecord)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Employee < ActiveRecord::Base
|
2
|
+
|
3
|
+
named_scope :boss, :conditions => "position = 'Boss'"
|
4
|
+
named_scope :country, lambda { |country| { :conditions => ["country = ?", country] } }
|
5
|
+
|
6
|
+
end
|
7
|
+
|
8
|
+
20.times do |n|
|
9
|
+
position = (n == 0) ? "Boss" : "Worker"
|
10
|
+
salary = (n == 0) ? 100000 : (((n+1) % 5) * 10000)
|
11
|
+
Employee.create(:name => "Employee #{n}", :country => "Country #{(n % 10) + 1}", :position => position, :salary => salary)
|
12
|
+
end
|
13
|
+
|
14
|
+
#puts Employee.all.inspect
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require File.dirname(__FILE__) + '/../../../../spec/spec_helper'
|
6
|
+
rescue LoadError
|
7
|
+
puts "You need to install rspec in your base app"
|
8
|
+
exit
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'filter_fu'
|
12
|
+
|
13
|
+
Spec::Runner.configure do |config|
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), 'debug.log'))
|
18
|
+
|
19
|
+
configuration = YAML.load_file(File.join(File.dirname(__FILE__), 'db', 'database.yml'))
|
20
|
+
ActiveRecord::Base.establish_connection(configuration[ENV["DB"] || "sqlite3"])
|
21
|
+
|
22
|
+
ActiveRecord::Base.silence do
|
23
|
+
ActiveRecord::Migration.verbose = false
|
24
|
+
load(File.join(File.dirname(__FILE__), "db", "schema.rb"))
|
25
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'dummies'
|
3
|
+
|
4
|
+
describe FilterFu::ViewHelper, :type => :helper do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@controller = DummyController.new
|
8
|
+
@request = @controller.request
|
9
|
+
|
10
|
+
helper.controller = @controller
|
11
|
+
helper.request = @request
|
12
|
+
|
13
|
+
helper.output_buffer = ""
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should provide a filter_form_for method" do
|
17
|
+
helper.should respond_to(:filter_form_for)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should prodive a filter_form method as an alias for filter_form" do
|
21
|
+
helper.should respond_to(:filter_form)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should require a block" do
|
25
|
+
lambda { helper.filter_form_for }.should raise_error(ArgumentError, /Missing block/)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should accept options" do
|
29
|
+
lambda { helper.filter_form_for({}) {} }.should_not raise_error(ArgumentError)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should not require options" do
|
33
|
+
lambda { helper.filter_form_for() {} }.should_not raise_error(ArgumentError)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should accept a name together with options" do
|
37
|
+
lambda { helper.filter_form_for(:other, {}) {} }.should_not raise_error(ArgumentError)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should call the associated block" do
|
41
|
+
lambda {
|
42
|
+
helper.filter_form_for() { throw :done }
|
43
|
+
}.should throw_symbol(:done)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should pass a ActionView::Helpers::FormBuilder to the block" do
|
47
|
+
helper.filter_form_for { |f| f.should be_kind_of(ActionView::Helpers::FormBuilder) }
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should include the erb of the block" do
|
51
|
+
html = eval_erb("<% filter_form_for { %><div>Some random HTML</div><% } %>")
|
52
|
+
html.should have_tag('div', 'Some random HTML')
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should include a form tag" do
|
56
|
+
html = eval_erb("<% filter_form_for { %> <% } %>")
|
57
|
+
html.should have_tag('form')
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should set the form method attribute to GET" do
|
61
|
+
html = eval_erb("<% filter_form_for { %> <% } %>")
|
62
|
+
html.should have_tag('form[method=?]', 'get')
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should set the form action attribute to the current url" do
|
66
|
+
# Controller and Action are foo and bar as defined in the dummies
|
67
|
+
html = eval_erb("<% filter_form_for { %> <% } %>")
|
68
|
+
html.should have_tag('form[action=?]', '/foo/bar')
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should use :filter as the default namespace in form fields" do
|
72
|
+
html = eval_erb("<% filter_form_for { |f| %><%= f.text_field :name %><% } %>")
|
73
|
+
html.should have_tag('input[name=?]', 'filter[name]')
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should use another name as namespace if it's provided as the first argument" do
|
77
|
+
html = eval_erb("<% filter_form_for(:other) { |f| %><%= f.text_field :name %><% } %>")
|
78
|
+
html.should have_tag('input[name=?]', 'other[name]')
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should pass options to the form_for helper" do
|
82
|
+
html = eval_erb("<% filter_form_for(:html => { :class => 'filter' }) { |f| %> <% } %>")
|
83
|
+
html.should have_tag('form[class=?]', 'filter')
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should preserve the page's parameters with hidden fields" do
|
87
|
+
helper.params = { :some_param => 'some value', :some_other_param => 'some other value' }
|
88
|
+
html = eval_erb("<% filter_form_for() { |f| %> <% } %>")
|
89
|
+
html.should have_tag('input[type=?][name=?][value=?]', 'hidden', 'some_param', 'some value')
|
90
|
+
html.should have_tag('input[type=?][name=?][value=?]', 'hidden', 'some_other_param', 'some other value')
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should preserve the page's nested parameters with hidden fields" do
|
94
|
+
helper.params = { :some_param => 'some value', :nested => { :some_other_param => 'some other value', :deeply_nested => { :down_here => 'yet another value' } } }
|
95
|
+
html = eval_erb("<% filter_form_for() { |f| %> <% } %>")
|
96
|
+
html.should have_tag('input[type=?][name=?][value=?]', 'hidden', 'some_param', 'some value')
|
97
|
+
html.should have_tag('input[type=?][name=?][value=?]', 'hidden', 'nested[some_other_param]', 'some other value')
|
98
|
+
html.should have_tag('input[type=?][name=?][value=?]', 'hidden', 'nested[deeply_nested][down_here]', 'yet another value')
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should not preserve the page's parameters for the current filter" do
|
102
|
+
helper.params = { :other => { :name => 'some value' }}
|
103
|
+
html = eval_erb("<% filter_form_for(:other) { |f| %> <% } %>")
|
104
|
+
html.should_not have_tag('input[type=?][name=?]', 'hidden', 'other')
|
105
|
+
html.should_not have_tag('input[type=?][name=?][value=?]', 'hidden', 'other[name]', 'some value')
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should not preserve the controller and action params" do
|
109
|
+
helper.params = { :controller => 'foo', :action => 'bar' }
|
110
|
+
html = eval_erb("<% filter_form_for() { |f| %> <% } %>")
|
111
|
+
html.should_not have_tag('input[type=?][name=?][value=?]', 'hidden', 'controller', 'foo')
|
112
|
+
html.should_not have_tag('input[type=?][name=?][value=?]', 'hidden', 'action', 'bar')
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should not preserve params specified in :ignore_parameters" do
|
116
|
+
helper.params = { :some_param => 'some value', :some_other_param => 'some other value' }
|
117
|
+
html = eval_erb("<% filter_form_for(:ignore_parameters => [:some_other_param]) { |f| %> <% } %>")
|
118
|
+
html.should have_tag('input[type=?][name=?][value=?]', 'hidden', 'some_param', 'some value')
|
119
|
+
html.should_not have_tag('input[type=?][name=?][value=?]', 'hidden', 'some_other_param', 'some other value')
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should use the current filter params as defaults for the form" do
|
123
|
+
helper.params = { :filter => { :some_param => 'some value' } }
|
124
|
+
html = eval_erb("<% filter_form_for() { |f| %><%= f.text_field :some_param %><% } %>")
|
125
|
+
html.should have_tag('input[type=?][name=?][value=?]', 'text', 'filter[some_param]', 'some value')
|
126
|
+
end
|
127
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: filter_fu
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Benedikt Deicke
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-15 00:00:00 +02:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: This Ruby on Rails plugin adds a filtered_by method to your models. It accepts a hash of filters that are applied using named_scopes. In addition the plugin adds some view helpers to easily build filter forms.
|
26
|
+
email: benedikt@synatic.net
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- README.rdoc
|
34
|
+
files:
|
35
|
+
- .document
|
36
|
+
- .gitignore
|
37
|
+
- LICENSE
|
38
|
+
- README.rdoc
|
39
|
+
- Rakefile
|
40
|
+
- VERSION
|
41
|
+
- filter_fu.gemspec
|
42
|
+
- lib/filter_fu.rb
|
43
|
+
- lib/filter_fu/active_record.rb
|
44
|
+
- lib/filter_fu/view_helper.rb
|
45
|
+
- rails/init.rb
|
46
|
+
- spec/active_record_spec.rb
|
47
|
+
- spec/db/database.yml
|
48
|
+
- spec/db/schema.rb
|
49
|
+
- spec/dummies.rb
|
50
|
+
- spec/filter_fu_spec.rb
|
51
|
+
- spec/fixtures/employee.rb
|
52
|
+
- spec/spec.opts
|
53
|
+
- spec/spec_helper.rb
|
54
|
+
- spec/view_helper_spec.rb
|
55
|
+
has_rdoc: true
|
56
|
+
homepage: http://github.com/benedikt/filter_fu
|
57
|
+
licenses: []
|
58
|
+
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options:
|
61
|
+
- --charset=UTF-8
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.3.5
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: Filter ActiveRecord models using named_scopes
|
83
|
+
test_files:
|
84
|
+
- spec/active_record_spec.rb
|
85
|
+
- spec/db/schema.rb
|
86
|
+
- spec/dummies.rb
|
87
|
+
- spec/filter_fu_spec.rb
|
88
|
+
- spec/fixtures/employee.rb
|
89
|
+
- spec/spec_helper.rb
|
90
|
+
- spec/view_helper_spec.rb
|