cehoffman-acts_as_ferret 0.4.4
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/LICENSE +20 -0
- data/README +68 -0
- data/bin/aaf_install +23 -0
- data/config/ferret_server.yml +24 -0
- data/doc/README.win32 +23 -0
- data/doc/demo/README +154 -0
- data/doc/demo/README_DEMO +23 -0
- data/doc/demo/Rakefile +10 -0
- data/doc/demo/app/controllers/admin/backend_controller.rb +14 -0
- data/doc/demo/app/controllers/admin_area_controller.rb +4 -0
- data/doc/demo/app/controllers/application.rb +5 -0
- data/doc/demo/app/controllers/contents_controller.rb +49 -0
- data/doc/demo/app/controllers/searches_controller.rb +8 -0
- data/doc/demo/app/helpers/admin/backend_helper.rb +2 -0
- data/doc/demo/app/helpers/application_helper.rb +3 -0
- data/doc/demo/app/helpers/content_helper.rb +2 -0
- data/doc/demo/app/helpers/search_helper.rb +2 -0
- data/doc/demo/app/models/comment.rb +48 -0
- data/doc/demo/app/models/content.rb +12 -0
- data/doc/demo/app/models/content_base.rb +28 -0
- data/doc/demo/app/models/search.rb +19 -0
- data/doc/demo/app/models/shared_index1.rb +3 -0
- data/doc/demo/app/models/shared_index2.rb +3 -0
- data/doc/demo/app/models/special_content.rb +3 -0
- data/doc/demo/app/models/stats.rb +20 -0
- data/doc/demo/app/views/admin/backend/search.rhtml +18 -0
- data/doc/demo/app/views/contents/_form.rhtml +10 -0
- data/doc/demo/app/views/contents/edit.rhtml +9 -0
- data/doc/demo/app/views/contents/index.rhtml +24 -0
- data/doc/demo/app/views/contents/new.rhtml +8 -0
- data/doc/demo/app/views/contents/show.rhtml +8 -0
- data/doc/demo/app/views/layouts/application.html.erb +17 -0
- data/doc/demo/app/views/searches/_content.html.erb +2 -0
- data/doc/demo/app/views/searches/search.html.erb +20 -0
- data/doc/demo/config/boot.rb +109 -0
- data/doc/demo/config/database.yml +38 -0
- data/doc/demo/config/environment.rb +69 -0
- data/doc/demo/config/environments/development.rb +16 -0
- data/doc/demo/config/environments/production.rb +19 -0
- data/doc/demo/config/environments/test.rb +21 -0
- data/doc/demo/config/ferret_server.yml +18 -0
- data/doc/demo/config/lighttpd.conf +40 -0
- data/doc/demo/config/routes.rb +9 -0
- data/doc/demo/db/development_structure.sql +15 -0
- data/doc/demo/db/migrate/001_initial_migration.rb +18 -0
- data/doc/demo/db/migrate/002_add_type_to_contents.rb +9 -0
- data/doc/demo/db/migrate/003_create_shared_index1s.rb +11 -0
- data/doc/demo/db/migrate/004_create_shared_index2s.rb +11 -0
- data/doc/demo/db/migrate/005_special_field.rb +9 -0
- data/doc/demo/db/migrate/006_create_stats.rb +15 -0
- data/doc/demo/db/schema.sql +18 -0
- data/doc/demo/doc/README_FOR_APP +2 -0
- data/doc/demo/doc/howto.txt +70 -0
- data/doc/demo/public/.htaccess +40 -0
- data/doc/demo/public/404.html +8 -0
- data/doc/demo/public/500.html +8 -0
- data/doc/demo/public/dispatch.cgi +10 -0
- data/doc/demo/public/dispatch.fcgi +24 -0
- data/doc/demo/public/dispatch.rb +10 -0
- data/doc/demo/public/favicon.ico +0 -0
- data/doc/demo/public/images/rails.png +0 -0
- data/doc/demo/public/index.html +277 -0
- data/doc/demo/public/robots.txt +1 -0
- data/doc/demo/public/stylesheets/scaffold.css +74 -0
- data/doc/demo/script/about +3 -0
- data/doc/demo/script/breakpointer +3 -0
- data/doc/demo/script/console +3 -0
- data/doc/demo/script/destroy +3 -0
- data/doc/demo/script/ferret_server +10 -0
- data/doc/demo/script/generate +3 -0
- data/doc/demo/script/performance/benchmarker +3 -0
- data/doc/demo/script/performance/profiler +3 -0
- data/doc/demo/script/plugin +3 -0
- data/doc/demo/script/process/inspector +3 -0
- data/doc/demo/script/process/reaper +3 -0
- data/doc/demo/script/process/spawner +3 -0
- data/doc/demo/script/process/spinner +3 -0
- data/doc/demo/script/runner +3 -0
- data/doc/demo/script/server +3 -0
- data/doc/demo/test/fixtures/comments.yml +12 -0
- data/doc/demo/test/fixtures/contents.yml +13 -0
- data/doc/demo/test/fixtures/remote_contents.yml +9 -0
- data/doc/demo/test/fixtures/shared_index1s.yml +7 -0
- data/doc/demo/test/fixtures/shared_index2s.yml +7 -0
- data/doc/demo/test/functional/admin/backend_controller_test.rb +35 -0
- data/doc/demo/test/functional/contents_controller_test.rb +81 -0
- data/doc/demo/test/functional/searches_controller_test.rb +71 -0
- data/doc/demo/test/smoke/drb_smoke_test.rb +321 -0
- data/doc/demo/test/smoke/process_stats.rb +21 -0
- data/doc/demo/test/test_helper.rb +30 -0
- data/doc/demo/test/unit/comment_test.rb +217 -0
- data/doc/demo/test/unit/content_test.rb +705 -0
- data/doc/demo/test/unit/ferret_result_test.rb +24 -0
- data/doc/demo/test/unit/multi_index_test.rb +329 -0
- data/doc/demo/test/unit/remote_index_test.rb +23 -0
- data/doc/demo/test/unit/shared_index1_test.rb +108 -0
- data/doc/demo/test/unit/shared_index2_test.rb +13 -0
- data/doc/demo/test/unit/sort_test.rb +21 -0
- data/doc/demo/test/unit/special_content_test.rb +25 -0
- data/doc/demo/vendor/plugins/will_paginate/LICENSE +18 -0
- data/doc/demo/vendor/plugins/will_paginate/README +108 -0
- data/doc/demo/vendor/plugins/will_paginate/Rakefile +23 -0
- data/doc/demo/vendor/plugins/will_paginate/init.rb +21 -0
- data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/collection.rb +45 -0
- data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb +44 -0
- data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/finder.rb +159 -0
- data/doc/demo/vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb +95 -0
- data/doc/demo/vendor/plugins/will_paginate/test/array_pagination_test.rb +23 -0
- data/doc/demo/vendor/plugins/will_paginate/test/boot.rb +27 -0
- data/doc/demo/vendor/plugins/will_paginate/test/console +10 -0
- data/doc/demo/vendor/plugins/will_paginate/test/finder_test.rb +219 -0
- data/doc/demo/vendor/plugins/will_paginate/test/fixtures/admin.rb +3 -0
- data/doc/demo/vendor/plugins/will_paginate/test/fixtures/companies.yml +24 -0
- data/doc/demo/vendor/plugins/will_paginate/test/fixtures/company.rb +23 -0
- data/doc/demo/vendor/plugins/will_paginate/test/fixtures/developer.rb +11 -0
- data/doc/demo/vendor/plugins/will_paginate/test/fixtures/developers_projects.yml +13 -0
- data/doc/demo/vendor/plugins/will_paginate/test/fixtures/project.rb +4 -0
- data/doc/demo/vendor/plugins/will_paginate/test/fixtures/projects.yml +7 -0
- data/doc/demo/vendor/plugins/will_paginate/test/fixtures/replies.yml +20 -0
- data/doc/demo/vendor/plugins/will_paginate/test/fixtures/reply.rb +5 -0
- data/doc/demo/vendor/plugins/will_paginate/test/fixtures/schema.sql +44 -0
- data/doc/demo/vendor/plugins/will_paginate/test/fixtures/topic.rb +19 -0
- data/doc/demo/vendor/plugins/will_paginate/test/fixtures/topics.yml +30 -0
- data/doc/demo/vendor/plugins/will_paginate/test/fixtures/user.rb +2 -0
- data/doc/demo/vendor/plugins/will_paginate/test/fixtures/users.yml +35 -0
- data/doc/demo/vendor/plugins/will_paginate/test/helper.rb +42 -0
- data/doc/demo/vendor/plugins/will_paginate/test/lib/activerecord_test_connector.rb +64 -0
- data/doc/demo/vendor/plugins/will_paginate/test/lib/load_fixtures.rb +10 -0
- data/doc/demo/vendor/plugins/will_paginate/test/pagination_test.rb +136 -0
- data/doc/monit-example +22 -0
- data/init.rb +24 -0
- data/install.rb +18 -0
- data/lib/act_methods.rb +147 -0
- data/lib/acts_as_ferret.rb +584 -0
- data/lib/ar_mysql_auto_reconnect_patch.rb +41 -0
- data/lib/blank_slate.rb +53 -0
- data/lib/bulk_indexer.rb +38 -0
- data/lib/class_methods.rb +270 -0
- data/lib/ferret_extensions.rb +188 -0
- data/lib/ferret_find_methods.rb +141 -0
- data/lib/ferret_result.rb +53 -0
- data/lib/ferret_server.rb +238 -0
- data/lib/index.rb +99 -0
- data/lib/instance_methods.rb +171 -0
- data/lib/local_index.rb +205 -0
- data/lib/more_like_this.rb +217 -0
- data/lib/multi_index.rb +126 -0
- data/lib/rdig_adapter.rb +148 -0
- data/lib/remote_functions.rb +23 -0
- data/lib/remote_index.rb +54 -0
- data/lib/remote_multi_index.rb +20 -0
- data/lib/search_results.rb +50 -0
- data/lib/server_manager.rb +58 -0
- data/lib/unix_daemon.rb +64 -0
- data/lib/without_ar.rb +52 -0
- data/rakefile +141 -0
- data/recipes/aaf_recipes.rb +114 -0
- data/script/ferret_daemon +94 -0
- data/script/ferret_server +10 -0
- data/script/ferret_service +178 -0
- data/tasks/ferret.rake +22 -0
- metadata +258 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
2
|
+
|
|
3
|
+
class SharedIndex2Test < Test::Unit::TestCase
|
|
4
|
+
fixtures :shared_index2s, :shared_index1s
|
|
5
|
+
|
|
6
|
+
def setup
|
|
7
|
+
SharedIndex1.rebuild_index
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_query_for_record
|
|
11
|
+
assert_match /SharedIndex2/, shared_index2s(:first).query_for_record.to_s
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
2
|
+
|
|
3
|
+
class SortTest < Test::Unit::TestCase
|
|
4
|
+
include Ferret::Search
|
|
5
|
+
|
|
6
|
+
def test_sort_marshalling
|
|
7
|
+
[ Sort.new,
|
|
8
|
+
Sort.new( [], :reverse => true) ,
|
|
9
|
+
Sort.new([ Ferret::Search::SortField.new(:id, :reverse => true),
|
|
10
|
+
Ferret::Search::SortField::SCORE,
|
|
11
|
+
Ferret::Search::SortField::DOC_ID ],
|
|
12
|
+
:reverse => true),
|
|
13
|
+
Sort.new([ Ferret::Search::SortField.new(:id),
|
|
14
|
+
Ferret::Search::SortField::SCORE_REV,
|
|
15
|
+
Ferret::Search::SortField::DOC_ID_REV ])
|
|
16
|
+
].each do |sort|
|
|
17
|
+
assert_equal sort.to_s, Sort._load(sort._dump(0)).to_s
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
2
|
+
|
|
3
|
+
class SpecialContentTest < Test::Unit::TestCase
|
|
4
|
+
include Ferret::Index
|
|
5
|
+
include Ferret::Search
|
|
6
|
+
fixtures :contents, :comments
|
|
7
|
+
|
|
8
|
+
def setup
|
|
9
|
+
ContentBase.rebuild_index
|
|
10
|
+
Comment.rebuild_index
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_class_index_dir
|
|
14
|
+
assert SpecialContent.aaf_configuration[:index_dir] =~ %r{^#{RAILS_ROOT}/index/test/content_base}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_find_with_ferret
|
|
18
|
+
contents_from_ferret = SpecialContent.find_with_ferret('single table')
|
|
19
|
+
assert_equal 1, contents_from_ferret.size
|
|
20
|
+
assert_equal ContentBase.find(3), contents_from_ferret.first
|
|
21
|
+
contents_from_ferret = SpecialContent.find_with_ferret('title')
|
|
22
|
+
assert contents_from_ferret.empty?
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Copyright (c) 2007 PJ Hyett and Mislav Marohnić
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
5
|
+
the Software without restriction, including without limitation the rights to
|
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
8
|
+
subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
15
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
16
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
= WillPaginate
|
|
2
|
+
|
|
3
|
+
Quick quiz: Where does pagination logic belong?
|
|
4
|
+
|
|
5
|
+
a) in the model;
|
|
6
|
+
b) in the controller;
|
|
7
|
+
c) in views;
|
|
8
|
+
d) all of the above.
|
|
9
|
+
|
|
10
|
+
We think you know the answer (if you think hard enough).
|
|
11
|
+
|
|
12
|
+
This plugin makes magic happen. You *will* paginate!
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
== Example usage:
|
|
16
|
+
|
|
17
|
+
Use a paginate finder in the controller:
|
|
18
|
+
|
|
19
|
+
@posts = Post.paginate_by_board_id @board.id, :page => params[:page]
|
|
20
|
+
|
|
21
|
+
Yeah, +paginate+ works just like +find+ -- it just doesn't fetch all the records.
|
|
22
|
+
Just don't forget to tell it which page you want!
|
|
23
|
+
|
|
24
|
+
Render the posts in your view like you would normally do. When you need to render
|
|
25
|
+
pagination, just stick this in:
|
|
26
|
+
|
|
27
|
+
<%= will_paginate @posts %>
|
|
28
|
+
|
|
29
|
+
You're done. (Copy and paste the example fancy CSS styles from the bottom.)
|
|
30
|
+
|
|
31
|
+
How does it know how much items to fetch per page? It asks your model by calling
|
|
32
|
+
+Post.per_page+. You can define it like this:
|
|
33
|
+
|
|
34
|
+
class Post < ActiveRecord::Base
|
|
35
|
+
cattr_reader :per_page
|
|
36
|
+
@@per_page = 50
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
... or like this:
|
|
40
|
+
|
|
41
|
+
class Post < ActiveRecord::Base
|
|
42
|
+
def self.per_page
|
|
43
|
+
50
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
... or don't worry about it at all. (WillPaginate defines it to be 30 if missing.)
|
|
48
|
+
You can also specify the count explicitly when calling +paginate+:
|
|
49
|
+
|
|
50
|
+
@posts = Post.paginate :page => params[:page], :per_page => 50
|
|
51
|
+
|
|
52
|
+
The +paginate+ finder wraps the original finder and returns your resultset that now has
|
|
53
|
+
some new properties. You can use the collection as you would with any ActiveRecord
|
|
54
|
+
resultset, but WillPaginate view helpers also need that object to be able to render pagination:
|
|
55
|
+
|
|
56
|
+
<ol>
|
|
57
|
+
<% for post in @posts -%>
|
|
58
|
+
<li>Render `post` in some nice way.</li>
|
|
59
|
+
<% end -%>
|
|
60
|
+
</ol>
|
|
61
|
+
|
|
62
|
+
<p>Now let's render us some pagination!</p>
|
|
63
|
+
<%= will_paginate @posts %>
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
== Authors, credits
|
|
67
|
+
|
|
68
|
+
Ruby port by: PJ Hyett, Mislav Marohnić (Sulien)
|
|
69
|
+
Contributors: K. Adam Christensen, Chris Wanstrath, Dr. Nic Williams
|
|
70
|
+
Original announcement: http://errtheblog.com/post/929
|
|
71
|
+
Original PHP source: http://www.strangerstudios.com/sandbox/pagination/diggstyle.php
|
|
72
|
+
|
|
73
|
+
REPORT BUGS on Lighthouse: http://err.lighthouseapp.com/projects/466-plugins/overview
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
== Want Digg style?
|
|
77
|
+
|
|
78
|
+
Copy the following css into your stylesheet for a good start:
|
|
79
|
+
|
|
80
|
+
.pagination {
|
|
81
|
+
padding: 3px;
|
|
82
|
+
margin: 3px;
|
|
83
|
+
}
|
|
84
|
+
.pagination a {
|
|
85
|
+
padding: 2px 5px 2px 5px;
|
|
86
|
+
margin: 2px;
|
|
87
|
+
border: 1px solid #aaaadd;
|
|
88
|
+
text-decoration: none;
|
|
89
|
+
color: #000099;
|
|
90
|
+
}
|
|
91
|
+
.pagination a:hover, .pagination a:active {
|
|
92
|
+
border: 1px solid #000099;
|
|
93
|
+
color: #000;
|
|
94
|
+
}
|
|
95
|
+
.pagination span.current {
|
|
96
|
+
padding: 2px 5px 2px 5px;
|
|
97
|
+
margin: 2px;
|
|
98
|
+
border: 1px solid #000099;
|
|
99
|
+
font-weight: bold;
|
|
100
|
+
background-color: #000099;
|
|
101
|
+
color: #FFF;
|
|
102
|
+
}
|
|
103
|
+
.pagination span.disabled {
|
|
104
|
+
padding: 2px 5px 2px 5px;
|
|
105
|
+
margin: 2px;
|
|
106
|
+
border: 1px solid #eee;
|
|
107
|
+
color: #ddd;
|
|
108
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'rake'
|
|
2
|
+
require 'rake/testtask'
|
|
3
|
+
require 'rake/rdoctask'
|
|
4
|
+
|
|
5
|
+
desc 'Default: run unit tests.'
|
|
6
|
+
task :default => :test
|
|
7
|
+
|
|
8
|
+
desc 'Test the will_paginate plugin.'
|
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
|
10
|
+
t.pattern = 'test/**/*_test.rb'
|
|
11
|
+
t.verbose = true
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
desc 'Generate RDoc documentation for the will_paginate plugin.'
|
|
15
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
16
|
+
files = ['README', 'LICENSE', 'lib/**/*.rb']
|
|
17
|
+
rdoc.rdoc_files.add(files)
|
|
18
|
+
rdoc.main = "README" # page to start on
|
|
19
|
+
rdoc.title = "will_paginate"
|
|
20
|
+
rdoc.template = File.exists?(t="/Users/chris/ruby/projects/err/rock/template.rb") ? t : "/var/www/rock/template.rb"
|
|
21
|
+
rdoc.rdoc_dir = 'doc' # rdoc output folder
|
|
22
|
+
rdoc.options << '--inline-source'
|
|
23
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'will_paginate/core_ext'
|
|
2
|
+
require 'will_paginate/collection'
|
|
3
|
+
require 'will_paginate/finder'
|
|
4
|
+
require 'will_paginate/view_helpers'
|
|
5
|
+
|
|
6
|
+
ActionView::Base.send :include, WillPaginate::ViewHelpers
|
|
7
|
+
ActiveRecord::Base.send :include, WillPaginate::Finder
|
|
8
|
+
|
|
9
|
+
module ActiveRecord::Associations
|
|
10
|
+
# to support paginating finders on associations, we have to mix in the
|
|
11
|
+
# method_missing magic from WillPaginate::Finder::ClassMethods to AssociationProxy
|
|
12
|
+
# subclasses, but in a different way for Rails 1.2.x and 2.0
|
|
13
|
+
(AssociationCollection.instance_methods.include?(:create!) ?
|
|
14
|
+
AssociationCollection : AssociationCollection.subclasses.map(&:constantize)
|
|
15
|
+
).push(HasManyThroughAssociation).each do |klass|
|
|
16
|
+
klass.class_eval do
|
|
17
|
+
include WillPaginate::Finder::ClassMethods
|
|
18
|
+
alias_method_chain :method_missing, :paginate
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module WillPaginate
|
|
2
|
+
# Arrays returned from paginating finds are, in fact, instances of this.
|
|
3
|
+
# You may think of WillPaginate::Collection as an ordinary array with some
|
|
4
|
+
# extra properties. Those properites are used by view helpers to generate
|
|
5
|
+
# correct page links.
|
|
6
|
+
#
|
|
7
|
+
class Collection < Array
|
|
8
|
+
attr_reader :current_page, :per_page
|
|
9
|
+
attr_accessor :total_entries
|
|
10
|
+
|
|
11
|
+
# These collection objects are instantiated by ActiveRecord paginating
|
|
12
|
+
# finders; there is no need to do it manually.
|
|
13
|
+
#
|
|
14
|
+
def initialize(page, per_page, total)
|
|
15
|
+
@current_page = page.to_i
|
|
16
|
+
@per_page = per_page.to_i
|
|
17
|
+
@total_entries = total.to_i
|
|
18
|
+
@total_pages = (@total_entries / @per_page.to_f).ceil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# The total number of pages.
|
|
22
|
+
def page_count
|
|
23
|
+
@total_pages
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Current offset of the paginated collection. If we're on the first page,
|
|
27
|
+
# it is always 0. If we're on the 2nd page and there are 30 entries per page,
|
|
28
|
+
# the offset is 30. This property is useful if you want to render ordinals
|
|
29
|
+
# besides your records: simply start with offset + 1.
|
|
30
|
+
#
|
|
31
|
+
def offset
|
|
32
|
+
(current_page - 1) * per_page
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# current_page - 1 or nil if there is no previous page
|
|
36
|
+
def previous_page
|
|
37
|
+
current_page > 1 ? (current_page - 1) : nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# current_page + 1 or nil if there is no next page
|
|
41
|
+
def next_page
|
|
42
|
+
current_page < page_count ? (current_page + 1) : nil
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
3
|
+
unless Hash.instance_methods.include? 'except'
|
|
4
|
+
Hash.class_eval do
|
|
5
|
+
# Returns a new hash without the given keys.
|
|
6
|
+
def except(*keys)
|
|
7
|
+
rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
|
|
8
|
+
reject { |key,| rejected.include?(key) }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Replaces the hash without only the given keys.
|
|
12
|
+
def except!(*keys)
|
|
13
|
+
replace(except(*keys))
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
unless Hash.instance_methods.include? 'slice'
|
|
19
|
+
Hash.class_eval do
|
|
20
|
+
# Returns a new hash with only the given keys.
|
|
21
|
+
def slice(*keys)
|
|
22
|
+
allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
|
|
23
|
+
reject { |key,| !allowed.include?(key) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Replaces the hash with only the given keys.
|
|
27
|
+
def slice!(*keys)
|
|
28
|
+
replace(slice(*keys))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
unless Array.instance_methods.include? 'paginate'
|
|
34
|
+
# http://www.desimcadam.com/archives/8
|
|
35
|
+
Array.class_eval do
|
|
36
|
+
def paginate(page = 1, per_page = 15)
|
|
37
|
+
pagination_array = WillPaginate::Collection.new(page, per_page, size)
|
|
38
|
+
start_index = pagination_array.offset
|
|
39
|
+
end_index = start_index + (per_page - 1)
|
|
40
|
+
array_to_concat = self[start_index..end_index]
|
|
41
|
+
array_to_concat.nil? ? [] : pagination_array.concat(array_to_concat)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
module WillPaginate
|
|
2
|
+
# A mixin for ActiveRecord::Base. Provides `per_page` class method
|
|
3
|
+
# and makes `paginate` finders possible with some method_missing magic.
|
|
4
|
+
#
|
|
5
|
+
# Find out more in WillPaginate::Finder::ClassMethods
|
|
6
|
+
#
|
|
7
|
+
module Finder
|
|
8
|
+
def self.included(base)
|
|
9
|
+
base.extend ClassMethods
|
|
10
|
+
class << base
|
|
11
|
+
alias_method_chain :method_missing, :paginate
|
|
12
|
+
define_method(:per_page) { 30 } unless respond_to?(:per_page)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# = Paginating finders for ActiveRecord models
|
|
17
|
+
#
|
|
18
|
+
# WillPaginate doesn't really add extra methods to your ActiveRecord models (except +per_page+
|
|
19
|
+
# unless it's already available). It simply intercepts
|
|
20
|
+
# the calls to paginating finders such as +paginate+, +paginate_by_user_id+ (and so on) and
|
|
21
|
+
# translates them to ordinary finders: +find+, +find_by_user_id+, etc. It does so with some
|
|
22
|
+
# method_missing magic, but you don't need to care for that. You simply use paginating finders
|
|
23
|
+
# same way you used ordinary ones. You only need to tell them what page you want in options.
|
|
24
|
+
#
|
|
25
|
+
# @topics = Topic.paginate :all, :page => params[:page]
|
|
26
|
+
#
|
|
27
|
+
# In paginating finders, "all" is implicit. No sense in paginating a single record, right? So:
|
|
28
|
+
#
|
|
29
|
+
# Post.paginate => Post.find :all
|
|
30
|
+
# Post.paginate_all_by_something => Post.find_all_by_something
|
|
31
|
+
# Post.paginate_by_something => Post.find_all_by_something
|
|
32
|
+
#
|
|
33
|
+
# Knowing that, the above example can be written simply as:
|
|
34
|
+
#
|
|
35
|
+
# @topics = Topic.paginate :page => params[:page]
|
|
36
|
+
#
|
|
37
|
+
# Don't forget to pass the +page+ parameter! Without it, paginating finders will raise an error.
|
|
38
|
+
#
|
|
39
|
+
# == Options
|
|
40
|
+
# Options for paginating finders are:
|
|
41
|
+
#
|
|
42
|
+
# page REQUIRED, but defaults to 1 if false or nil
|
|
43
|
+
# per_page (default is read from the model, which is 30 if not overriden)
|
|
44
|
+
# total entries not needed unless you want to count the records yourself somehow
|
|
45
|
+
# count hash of options that are used only for the call to count
|
|
46
|
+
#
|
|
47
|
+
module ClassMethods
|
|
48
|
+
# This methods wraps +find_by_sql+ by simply adding LIMIT and OFFSET to your SQL string
|
|
49
|
+
# based on the params otherwise used by paginating finds: +page+ and +per_page+.
|
|
50
|
+
#
|
|
51
|
+
# Example:
|
|
52
|
+
#
|
|
53
|
+
# @developers = Developer.paginate_by_sql ['select * from developers where salary > ?', 80000],
|
|
54
|
+
# :page => params[:page], :per_page => 3
|
|
55
|
+
#
|
|
56
|
+
def paginate_by_sql(sql, options)
|
|
57
|
+
options, page, per_page = wp_parse_options!(options)
|
|
58
|
+
sanitized_query = sanitize_sql(sql)
|
|
59
|
+
total_entries = options[:total_entries] || count_by_sql("SELECT COUNT(*) FROM (#{sanitized_query}) AS count_table")
|
|
60
|
+
|
|
61
|
+
returning WillPaginate::Collection.new(page, per_page, total_entries) do |pager|
|
|
62
|
+
options.update :offset => pager.offset, :limit => pager.per_page
|
|
63
|
+
add_limit! sanitized_query, options
|
|
64
|
+
pager.replace find_by_sql(sanitized_query)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def respond_to?(method, include_priv = false)
|
|
69
|
+
case method.to_sym
|
|
70
|
+
when :paginate, :paginate_by_sql
|
|
71
|
+
true
|
|
72
|
+
else
|
|
73
|
+
super(method.to_s.sub(/^paginate/, 'find'), include_priv)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
protected
|
|
78
|
+
|
|
79
|
+
def method_missing_with_paginate(method, *args, &block)
|
|
80
|
+
# did somebody tried to paginate? if not, let them be
|
|
81
|
+
unless method.to_s.index('paginate') == 0
|
|
82
|
+
return method_missing_without_paginate(method, *args, &block)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
options, page, per_page = wp_parse_options!(args.pop)
|
|
86
|
+
# paginate finders are really just find_* with limit and offset
|
|
87
|
+
finder = method.to_s.sub /^paginate/, 'find'
|
|
88
|
+
# magic counting for user convenience
|
|
89
|
+
total_entries = wp_count!(options, args, finder)
|
|
90
|
+
|
|
91
|
+
# :all is implicit
|
|
92
|
+
if finder == 'find'
|
|
93
|
+
args.unshift(:all) if args.empty?
|
|
94
|
+
elsif finder.index('find_by_') == 0
|
|
95
|
+
finder.sub! /^find/, 'find_all'
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
::Object.returning WillPaginate::Collection.new(page, per_page, total_entries) do |pager|
|
|
99
|
+
args << options.update(:offset => pager.offset, :limit => pager.per_page)
|
|
100
|
+
pager.replace send(finder, *args)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def wp_count!(options, args, finder)
|
|
105
|
+
# :total_entries and :count are mutually exclusive!
|
|
106
|
+
unless options[:total_entries]
|
|
107
|
+
unless args.first.is_a? Array
|
|
108
|
+
# count expects (almost) the same options as find
|
|
109
|
+
count_options = options.except :count, :order, :select
|
|
110
|
+
|
|
111
|
+
# merge the hash found in :count
|
|
112
|
+
# this allows you to specify :select, :order, or anything else just for the count query
|
|
113
|
+
count_options.update(options.delete(:count)) if options.key? :count
|
|
114
|
+
# extract the conditions from calls like "paginate_by_foo_and_bar"
|
|
115
|
+
conditions = wp_extract_finder_conditions(finder, args, count_options)
|
|
116
|
+
|
|
117
|
+
# scope_out adds a 'with_finder' method which acts like with_scope, if it's present
|
|
118
|
+
# then execute the count with the scoping provided by the with_finder
|
|
119
|
+
count = nil
|
|
120
|
+
counter = Proc.new { count = count(count_options) }
|
|
121
|
+
|
|
122
|
+
if respond_to?(scoper = finder.sub(/^find/, 'with'))
|
|
123
|
+
send(scoper, &counter)
|
|
124
|
+
else
|
|
125
|
+
with_scope(:find => { :conditions => conditions }, &counter)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
count.respond_to?(:length) ? count.length : count
|
|
129
|
+
else
|
|
130
|
+
# array of IDs was passed, so its size is the total number
|
|
131
|
+
args.first.size
|
|
132
|
+
end
|
|
133
|
+
else
|
|
134
|
+
options.delete(:total_entries)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def wp_parse_options!(options)
|
|
139
|
+
raise ArgumentError, 'hash parameters expected' unless options.respond_to? :symbolize_keys!
|
|
140
|
+
options.symbolize_keys!
|
|
141
|
+
raise ArgumentError, ':page parameter required' unless options.key? :page
|
|
142
|
+
page = options.delete(:page) || 1
|
|
143
|
+
per_page = options.delete(:per_page) || self.per_page
|
|
144
|
+
[options, page, per_page]
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
private
|
|
148
|
+
|
|
149
|
+
# thanks to active record for making us duplicate this code
|
|
150
|
+
def wp_extract_finder_conditions(finder, arguments, count_options)
|
|
151
|
+
return unless match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(finder.to_s)
|
|
152
|
+
|
|
153
|
+
attribute_names = extract_attribute_names_from_match(match)
|
|
154
|
+
raise "I can't make sense of #{finder}" unless all_attributes_exists?(attribute_names)
|
|
155
|
+
construct_attributes_from_arguments(attribute_names, arguments)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|