buscar 1.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.
- data/.document +5 -0
- data/.rvmrc +1 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +67 -0
- data/LICENSE +20 -0
- data/README.markdown +3 -0
- data/Rakefile +59 -0
- data/VERSION +1 -0
- data/buscar.gemspec +114 -0
- data/lib/buscar.rb +3 -0
- data/lib/buscar/helpers.rb +79 -0
- data/lib/buscar/index.rb +171 -0
- data/lib/buscar/railtie.rb +18 -0
- data/spec/blueprint.rb +34 -0
- data/spec/helpers_spec.rb +82 -0
- data/spec/index_spec.rb +442 -0
- data/spec/matchers.rb +50 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/test_db.rb +60 -0
- metadata +360 -0
data/.document
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm gemset use buscar
|
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
group :development do
|
4
|
+
gem 'bundler', '~> 1.0.0'
|
5
|
+
gem 'jeweler', '~> 1.5.1'
|
6
|
+
gem 'rspec', '>= 2.2.0'
|
7
|
+
gem 'rcov'
|
8
|
+
gem 'machinist', '>= 1.0.6'
|
9
|
+
gem 'faker', '>= 0.3.1'
|
10
|
+
gem 'activesupport', '>= 3.0.3'
|
11
|
+
gem 'actionpack', '>= 3.0.3'
|
12
|
+
gem 'activerecord', '>= 3.0.3'
|
13
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
abstract (1.0.0)
|
5
|
+
actionpack (3.0.3)
|
6
|
+
activemodel (= 3.0.3)
|
7
|
+
activesupport (= 3.0.3)
|
8
|
+
builder (~> 2.1.2)
|
9
|
+
erubis (~> 2.6.6)
|
10
|
+
i18n (~> 0.4)
|
11
|
+
rack (~> 1.2.1)
|
12
|
+
rack-mount (~> 0.6.13)
|
13
|
+
rack-test (~> 0.5.6)
|
14
|
+
tzinfo (~> 0.3.23)
|
15
|
+
activemodel (3.0.3)
|
16
|
+
activesupport (= 3.0.3)
|
17
|
+
builder (~> 2.1.2)
|
18
|
+
i18n (~> 0.4)
|
19
|
+
activerecord (3.0.3)
|
20
|
+
activemodel (= 3.0.3)
|
21
|
+
activesupport (= 3.0.3)
|
22
|
+
arel (~> 2.0.2)
|
23
|
+
tzinfo (~> 0.3.23)
|
24
|
+
activesupport (3.0.3)
|
25
|
+
arel (2.0.6)
|
26
|
+
builder (2.1.2)
|
27
|
+
diff-lcs (1.1.2)
|
28
|
+
erubis (2.6.6)
|
29
|
+
abstract (>= 1.0.0)
|
30
|
+
faker (0.3.1)
|
31
|
+
git (1.2.5)
|
32
|
+
i18n (0.5.0)
|
33
|
+
jeweler (1.5.1)
|
34
|
+
bundler (~> 1.0.0)
|
35
|
+
git (>= 1.2.5)
|
36
|
+
rake
|
37
|
+
machinist (1.0.6)
|
38
|
+
rack (1.2.1)
|
39
|
+
rack-mount (0.6.13)
|
40
|
+
rack (>= 1.0.0)
|
41
|
+
rack-test (0.5.6)
|
42
|
+
rack (>= 1.0)
|
43
|
+
rake (0.8.7)
|
44
|
+
rcov (0.9.9)
|
45
|
+
rspec (2.2.0)
|
46
|
+
rspec-core (~> 2.2)
|
47
|
+
rspec-expectations (~> 2.2)
|
48
|
+
rspec-mocks (~> 2.2)
|
49
|
+
rspec-core (2.2.1)
|
50
|
+
rspec-expectations (2.2.0)
|
51
|
+
diff-lcs (~> 1.1.2)
|
52
|
+
rspec-mocks (2.2.0)
|
53
|
+
tzinfo (0.3.23)
|
54
|
+
|
55
|
+
PLATFORMS
|
56
|
+
ruby
|
57
|
+
|
58
|
+
DEPENDENCIES
|
59
|
+
actionpack (>= 3.0.3)
|
60
|
+
activerecord (>= 3.0.3)
|
61
|
+
activesupport (>= 3.0.3)
|
62
|
+
bundler (~> 1.0.0)
|
63
|
+
faker (>= 0.3.1)
|
64
|
+
jeweler (~> 1.5.1)
|
65
|
+
machinist (>= 1.0.6)
|
66
|
+
rcov
|
67
|
+
rspec (>= 2.2.0)
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 jarrett
|
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.markdown
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
require 'jeweler'
|
13
|
+
Jeweler::Tasks.new do |gem|
|
14
|
+
gem.name = "buscar"
|
15
|
+
gem.summary = %Q{Searching, sorting, and pagination for Rails}
|
16
|
+
gem.description = %Q{Simplifies searching, sorting, and pagination of ActiveRecord models. Includes a model class and a view helper.}
|
17
|
+
gem.email = "jarrettcolby@gmail.com"
|
18
|
+
gem.homepage = "http://github.com/jarrett/buscar"
|
19
|
+
gem.authors = ["jarrett"]
|
20
|
+
gem.add_dependency "activesupport", ">= 3.0.2"
|
21
|
+
gem.add_dependency "activerecord", ">= 3.0.2"
|
22
|
+
# Strictly speaking, you don't need ActionView to use the helpers. They rely on some methods ActionView provides,
|
23
|
+
# but in principle, those methods could be defined by something other than ActionView. So, we make ActionView
|
24
|
+
# (part of ActionPack) a development dependency for the sake of the specs.
|
25
|
+
gem.add_development_dependency "actionpack", ">= 3.0.2"
|
26
|
+
gem.add_development_dependency "rspec", ">= 2.1.0"
|
27
|
+
gem.add_development_dependency "sqlite3-ruby", ">= 1.2.5"
|
28
|
+
gem.add_development_dependency "machinist", ">= 1.0.6"
|
29
|
+
gem.add_development_dependency "faker", ">= 0.3.1"
|
30
|
+
gem.add_development_dependency "webrat", ">= 0.7.2"
|
31
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settingsend
|
32
|
+
end
|
33
|
+
Jeweler::RubygemsDotOrgTasks.new
|
34
|
+
|
35
|
+
require 'rake/testtask'
|
36
|
+
Rake::TestTask.new(:test) do |test|
|
37
|
+
test.libs << 'lib' << 'test'
|
38
|
+
test.pattern = 'test/**/test_*.rb'
|
39
|
+
test.verbose = true
|
40
|
+
end
|
41
|
+
|
42
|
+
require 'rcov/rcovtask'
|
43
|
+
Rcov::RcovTask.new do |test|
|
44
|
+
test.libs << 'test'
|
45
|
+
test.pattern = 'test/**/test_*.rb'
|
46
|
+
test.verbose = true
|
47
|
+
end
|
48
|
+
|
49
|
+
task :default => :test
|
50
|
+
|
51
|
+
require 'rake/rdoctask'
|
52
|
+
Rake::RDocTask.new do |rdoc|
|
53
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
54
|
+
|
55
|
+
rdoc.rdoc_dir = 'rdoc'
|
56
|
+
rdoc.title = "Buscar #{version}"
|
57
|
+
rdoc.rdoc_files.include('README*')
|
58
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
59
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/buscar.gemspec
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
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{buscar}
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["jarrett"]
|
12
|
+
s.date = %q{2010-12-07}
|
13
|
+
s.description = %q{Simplifies searching, sorting, and pagination of ActiveRecord models. Includes a model class and a view helper.}
|
14
|
+
s.email = %q{jarrettcolby@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.markdown"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rvmrc",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE",
|
25
|
+
"README.markdown",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"buscar.gemspec",
|
29
|
+
"lib/buscar.rb",
|
30
|
+
"lib/buscar/helpers.rb",
|
31
|
+
"lib/buscar/index.rb",
|
32
|
+
"lib/buscar/railtie.rb",
|
33
|
+
"spec/blueprint.rb",
|
34
|
+
"spec/helpers_spec.rb",
|
35
|
+
"spec/index_spec.rb",
|
36
|
+
"spec/matchers.rb",
|
37
|
+
"spec/spec_helper.rb",
|
38
|
+
"spec/test_db.rb"
|
39
|
+
]
|
40
|
+
s.homepage = %q{http://github.com/jarrett/buscar}
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
s.rubygems_version = %q{1.3.7}
|
43
|
+
s.summary = %q{Searching, sorting, and pagination for Rails}
|
44
|
+
s.test_files = [
|
45
|
+
"spec/blueprint.rb",
|
46
|
+
"spec/helpers_spec.rb",
|
47
|
+
"spec/index_spec.rb",
|
48
|
+
"spec/matchers.rb",
|
49
|
+
"spec/spec_helper.rb",
|
50
|
+
"spec/test_db.rb"
|
51
|
+
]
|
52
|
+
|
53
|
+
if s.respond_to? :specification_version then
|
54
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
55
|
+
s.specification_version = 3
|
56
|
+
|
57
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
58
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
59
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
|
60
|
+
s.add_development_dependency(%q<rspec>, [">= 2.2.0"])
|
61
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<machinist>, [">= 1.0.6"])
|
63
|
+
s.add_development_dependency(%q<faker>, [">= 0.3.1"])
|
64
|
+
s.add_development_dependency(%q<activesupport>, [">= 3.0.3"])
|
65
|
+
s.add_development_dependency(%q<actionpack>, [">= 3.0.3"])
|
66
|
+
s.add_development_dependency(%q<activerecord>, [">= 3.0.3"])
|
67
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 3.0.2"])
|
68
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 3.0.2"])
|
69
|
+
s.add_development_dependency(%q<actionpack>, [">= 3.0.2"])
|
70
|
+
s.add_development_dependency(%q<rspec>, [">= 2.1.0"])
|
71
|
+
s.add_development_dependency(%q<sqlite3-ruby>, [">= 1.2.5"])
|
72
|
+
s.add_development_dependency(%q<machinist>, [">= 1.0.6"])
|
73
|
+
s.add_development_dependency(%q<faker>, [">= 0.3.1"])
|
74
|
+
s.add_development_dependency(%q<webrat>, [">= 0.7.2"])
|
75
|
+
else
|
76
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
77
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
|
78
|
+
s.add_dependency(%q<rspec>, [">= 2.2.0"])
|
79
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
80
|
+
s.add_dependency(%q<machinist>, [">= 1.0.6"])
|
81
|
+
s.add_dependency(%q<faker>, [">= 0.3.1"])
|
82
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.3"])
|
83
|
+
s.add_dependency(%q<actionpack>, [">= 3.0.3"])
|
84
|
+
s.add_dependency(%q<activerecord>, [">= 3.0.3"])
|
85
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.2"])
|
86
|
+
s.add_dependency(%q<activerecord>, [">= 3.0.2"])
|
87
|
+
s.add_dependency(%q<actionpack>, [">= 3.0.2"])
|
88
|
+
s.add_dependency(%q<rspec>, [">= 2.1.0"])
|
89
|
+
s.add_dependency(%q<sqlite3-ruby>, [">= 1.2.5"])
|
90
|
+
s.add_dependency(%q<machinist>, [">= 1.0.6"])
|
91
|
+
s.add_dependency(%q<faker>, [">= 0.3.1"])
|
92
|
+
s.add_dependency(%q<webrat>, [">= 0.7.2"])
|
93
|
+
end
|
94
|
+
else
|
95
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
96
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
|
97
|
+
s.add_dependency(%q<rspec>, [">= 2.2.0"])
|
98
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
99
|
+
s.add_dependency(%q<machinist>, [">= 1.0.6"])
|
100
|
+
s.add_dependency(%q<faker>, [">= 0.3.1"])
|
101
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.3"])
|
102
|
+
s.add_dependency(%q<actionpack>, [">= 3.0.3"])
|
103
|
+
s.add_dependency(%q<activerecord>, [">= 3.0.3"])
|
104
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.2"])
|
105
|
+
s.add_dependency(%q<activerecord>, [">= 3.0.2"])
|
106
|
+
s.add_dependency(%q<actionpack>, [">= 3.0.2"])
|
107
|
+
s.add_dependency(%q<rspec>, [">= 2.1.0"])
|
108
|
+
s.add_dependency(%q<sqlite3-ruby>, [">= 1.2.5"])
|
109
|
+
s.add_dependency(%q<machinist>, [">= 1.0.6"])
|
110
|
+
s.add_dependency(%q<faker>, [">= 0.3.1"])
|
111
|
+
s.add_dependency(%q<webrat>, [">= 0.7.2"])
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
data/lib/buscar.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
module Buscar
|
2
|
+
module Helpers
|
3
|
+
# Accepts an instance of Buscar::Index, which will tell it the total number of pages, the current page, and the number of records per page.
|
4
|
+
# Accepts a block, which is yielded each page number and must return a URL to that page.
|
5
|
+
def page_links(index)
|
6
|
+
unless block_given?
|
7
|
+
raise ArgumentError, 'page_links requires a block.'
|
8
|
+
end
|
9
|
+
total_pages = index.page_count
|
10
|
+
if total_pages > 1
|
11
|
+
current_page = index.page + 1
|
12
|
+
content_tag('ul', 'class' => 'pagination') do
|
13
|
+
lis = ''
|
14
|
+
min_page = current_page - 2
|
15
|
+
min_page = 1 if min_page < 1
|
16
|
+
max_page = current_page + 2
|
17
|
+
max_page = total_pages if max_page > total_pages
|
18
|
+
if current_page > 1
|
19
|
+
lis << content_tag('li', link_to('« Back'.html_safe, yield(current_page - 1)))
|
20
|
+
end
|
21
|
+
if min_page > 1
|
22
|
+
lis << content_tag('li', link_to('1', yield(1)))
|
23
|
+
end
|
24
|
+
if min_page > 2
|
25
|
+
lis << content_tag('li', '...')
|
26
|
+
end
|
27
|
+
(min_page..max_page).each do |page|
|
28
|
+
lis << content_tag('li') do
|
29
|
+
if current_page.to_i == page
|
30
|
+
page.to_s
|
31
|
+
else
|
32
|
+
link_to page, yield(page)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
if max_page < total_pages - 1
|
37
|
+
lis << content_tag('li', '...')
|
38
|
+
end
|
39
|
+
if max_page < total_pages
|
40
|
+
lis << content_tag('li', link_to(total_pages, yield(total_pages)))
|
41
|
+
|
42
|
+
end
|
43
|
+
if current_page < total_pages
|
44
|
+
lis << content_tag('li', link_to('Next »'.html_safe, yield(current_page + 1)))
|
45
|
+
end
|
46
|
+
lis.html_safe
|
47
|
+
end.html_safe
|
48
|
+
else
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def buscar_index_menu(index, type, options)
|
54
|
+
options.reverse_merge! :link_to_current => true
|
55
|
+
content_tag('ul', :class => "#{type}_menu") do
|
56
|
+
choices = ''
|
57
|
+
index.send("#{type}_param_options").each do |param, label_str|
|
58
|
+
choices << content_tag('li') do
|
59
|
+
label_str = param.to_s.humanize if label_str.nil?
|
60
|
+
if !options[:link_to_current] and index.filter_param.to_s == param.to_s
|
61
|
+
('<span class="selected">' + label_str + '</span>').html_safe
|
62
|
+
else
|
63
|
+
link_to label_str, yield(param)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
choices.html_safe
|
68
|
+
end.html_safe
|
69
|
+
end
|
70
|
+
|
71
|
+
def filter_menu(index, options = {}, &block)
|
72
|
+
buscar_index_menu(index, 'filter', options, &block)
|
73
|
+
end
|
74
|
+
|
75
|
+
def sort_menu(index, options = {}, &block)
|
76
|
+
buscar_index_menu(index, 'sort', options, &block)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/buscar/index.rb
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
module Buscar
|
2
|
+
class Index
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
# Views can call this method to iterate over the current page's records. Uses
|
6
|
+
# the value returned by #page, which in turn uses the value of @params[:page]
|
7
|
+
def each
|
8
|
+
records_on_page(page).each do |record|
|
9
|
+
yield record
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def empty?
|
14
|
+
@records.empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns one of the following in descending order of preference:
|
18
|
+
# - params[:filter]
|
19
|
+
# - default_filter_option
|
20
|
+
# - 'none'
|
21
|
+
def filter_param
|
22
|
+
@params[:filter] || (respond_to?(:default_filter_option, true) ? default_filter_option : 'none')
|
23
|
+
end
|
24
|
+
|
25
|
+
def filter_param_options
|
26
|
+
filter_options.collect do |opt|
|
27
|
+
arr = [opt[0]]
|
28
|
+
arr << opt[2] if opt.length == 3
|
29
|
+
arr
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.generate(*args)
|
34
|
+
index = new(*args)
|
35
|
+
index.generate!
|
36
|
+
index
|
37
|
+
end
|
38
|
+
|
39
|
+
def generate!
|
40
|
+
unless respond_to?(:finder, true)
|
41
|
+
raise 'Subclasses of Index must define #finder, which must return something that responds to #find. For example, #finder may return an ActiveRecord::Base subclass.'
|
42
|
+
end
|
43
|
+
|
44
|
+
raise "Buscar::Index#conditions is deprecated. Name your method where_clause instead." if respond_to?(:conditions, true)
|
45
|
+
raise "Buscar::Index#order is deprecated. Name your method order_clause instead." if respond_to?(:order, true)
|
46
|
+
raise "Buscar::Index#include_clause is deprecated. Name your method includes_clause instead." if respond_to?(:include, true)
|
47
|
+
|
48
|
+
@records = finder.scoped # Get the bare relation object in case none of the modifiers are used
|
49
|
+
# Use each AR query modifier other than #where and #order if applicable
|
50
|
+
%w(having select group limit offset joins includes lock readonly from).each do |meth|
|
51
|
+
@records = @records.send(meth, send("#{meth}_clause".to_sym)) if respond_to?("#{meth}_clause".to_sym, true)
|
52
|
+
end
|
53
|
+
|
54
|
+
chained_filters = filter
|
55
|
+
chained_filters = chain(chained_filters) unless chained_filters.is_a?(Chain) # filter might return a Chain or a single filtering rule. If it's a single one, we'll put it in an array.
|
56
|
+
@filter_procs = chained_filters.select { |f| f.is_a?(Proc) } # Procs will be triggered by the first call to #records so as not to defeat lazy loading
|
57
|
+
where_filters = chained_filters.select { |f| !f.is_a?(Proc) } # SQL filters can be used right away
|
58
|
+
where_filters.each do |filt|
|
59
|
+
@records = @records.where(filt)
|
60
|
+
end
|
61
|
+
|
62
|
+
sort_rule = sort
|
63
|
+
# If sorting by a proc, do it on the first call to #records so as not to defeat lazy loading
|
64
|
+
if sort_rule.is_a?(Proc)
|
65
|
+
@sort_proc = sort_rule
|
66
|
+
else
|
67
|
+
@records = @records.order(sort_rule)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def initialize(params = {})
|
72
|
+
@params = params
|
73
|
+
end
|
74
|
+
|
75
|
+
def length
|
76
|
+
@records.length
|
77
|
+
end
|
78
|
+
|
79
|
+
def optional_params(*keys)
|
80
|
+
keys.inject({}) do |hash, key|
|
81
|
+
unless @params[key].blank?
|
82
|
+
hash[key] = @params[key]
|
83
|
+
end
|
84
|
+
hash
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# The current page, as determined by @params[:page]. Since we want the user to see the page array as one-based,
|
89
|
+
# but we use zero-based indices internally, we subtract one from the page number.
|
90
|
+
#
|
91
|
+
# Thus, the return value is a zero-based offset.
|
92
|
+
def page
|
93
|
+
(@params[:page] || @params['page'] || 1).to_i - 1
|
94
|
+
end
|
95
|
+
|
96
|
+
def page_count
|
97
|
+
(@records.length.to_f / records_per_page).ceil
|
98
|
+
end
|
99
|
+
|
100
|
+
attr_reader :params
|
101
|
+
|
102
|
+
# page_num is zero-based for this method.
|
103
|
+
def records_on_page(page_num)
|
104
|
+
if paginate?
|
105
|
+
@records.slice((page_num) * records_per_page, records_per_page)
|
106
|
+
else
|
107
|
+
@records
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns one of the following in descending order of preference:
|
112
|
+
# - params[:sort]
|
113
|
+
# - default_sort_option
|
114
|
+
# - 'none'
|
115
|
+
def sort_param
|
116
|
+
@params[:sort] || (respond_to?(:default_sort_option, true) ? default_sort_option : 'none')
|
117
|
+
end
|
118
|
+
|
119
|
+
def sort_param_options
|
120
|
+
sort_options.collect do |opt|
|
121
|
+
arr = [opt[0]]
|
122
|
+
arr << opt[2] if opt.length == 3
|
123
|
+
arr
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def records
|
128
|
+
unless @procs_called
|
129
|
+
@filter_procs.each do |proc|
|
130
|
+
@records = @records.select(&proc)
|
131
|
+
end
|
132
|
+
@records = @records.sort_by(&@sort_proc) if @sort_proc
|
133
|
+
@procs_called = true
|
134
|
+
end
|
135
|
+
@records
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
class Chain < Array; end
|
141
|
+
|
142
|
+
def chain(*elements)
|
143
|
+
Chain.new(elements)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Return something for #where, a Proc, or an array
|
147
|
+
def filter
|
148
|
+
if respond_to?(:filter_options, true) and (option = filter_options.assoc(filter_param))
|
149
|
+
option[1]
|
150
|
+
else
|
151
|
+
nil
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def paginate?
|
156
|
+
true
|
157
|
+
end
|
158
|
+
|
159
|
+
def records_per_page
|
160
|
+
@params[:records_per_page] || 50
|
161
|
+
end
|
162
|
+
|
163
|
+
def sort
|
164
|
+
if respond_to?(:sort_options, true) and (option = sort_options.assoc(sort_param))
|
165
|
+
option[1]
|
166
|
+
else
|
167
|
+
nil
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|