paged_scopes 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,68 @@
1
+ module PagedScopes
2
+ class Paginator
3
+ attr_reader :page
4
+
5
+ def initialize(page)
6
+ @page = page
7
+ end
8
+
9
+ def set_path(&block)
10
+ @path = block
11
+ end
12
+
13
+ def path
14
+ @path || raise(RuntimeError, "No path proc supplied.")
15
+ end
16
+
17
+ def previous
18
+ path.call(@page.previous) unless @page.first?
19
+ end
20
+
21
+ def next
22
+ path.call(@page.next) unless @page.last?
23
+ end
24
+
25
+ def first
26
+ path.call(@page.class.first)
27
+ end
28
+
29
+ def last
30
+ path.call(@page.class.last)
31
+ end
32
+
33
+ def window(options)
34
+ raise ArgumentError, "No window block supplied." unless block_given?
35
+ raise ArgumentError, "please specify a :inner option" unless inner = options[:inner]
36
+ return if @page.page_count < 2
37
+ outer = options[:outer] || 0
38
+ extras = [ options[:extras] ].flatten.compact
39
+ numbers = case
40
+ when @page.number <= inner + 1
41
+ 1 .. 1 + 2 * inner
42
+ when @page.number >= @page.page_count - inner
43
+ @page.page_count - 2 * inner .. @page.page_count
44
+ else
45
+ @page.number - inner .. @page.number + inner
46
+ end.to_a
47
+ 1.upto(outer) { |n| numbers << n << @page.page_count-n+1 }
48
+ numbers.uniq!
49
+ numbers.sort!
50
+ numbers.reject! { |number| !number.between?(1, @page.page_count) }
51
+ returning [] do |results|
52
+ results << yield(:first, @page.first? ? nil : first, []) if extras.include?(:first)
53
+ results << yield(:previous, previous, []) if extras.include?(:previous)
54
+ numbers.zip([nil]+numbers, numbers[1..-1]) do |number, prev_number, next_number|
55
+ page = @page.class.find(number)
56
+ path = page == @page ? nil : @path.call(page)
57
+ classes = []
58
+ classes << :selected if page == @page
59
+ classes << :gap_before if prev_number && prev_number < number - 1
60
+ classes << :gap_after if next_number && next_number > number + 1
61
+ results << yield(page, path, classes)
62
+ end
63
+ results << yield(:next, self.next, []) if extras.include?(:next)
64
+ results << yield(:last, @page.last? ? nil : last, []) if extras.include?(:last)
65
+ end.join("\n")
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,32 @@
1
+ module PagedScopes
2
+ module Resources
3
+ def resources_with_paged(*entities, &block)
4
+ options = entities.extract_options!
5
+ if page_options = options.delete(:paged)
6
+ resources_without_paged(*(entities.dup << options), &block)
7
+ page_options = {} unless page_options.is_a? Hash
8
+ page_name = page_options.delete(:name)
9
+ page_options.slice!(:as, :name)
10
+ page_options.merge!(:only => :none)
11
+ preserved_options = ActionController::Resources::INHERITABLE_OPTIONS + [ :name_prefix, :path_prefix ]
12
+ with_options(options.slice(*preserved_options)) do |map|
13
+ map.resources_without_paged(page_name || :pages, page_options) do |page|
14
+ page.resources(*(entities.dup << { :only => :index }))
15
+ end
16
+ end
17
+ else
18
+ resources_without_paged(*(entities << options), &block)
19
+ end
20
+ end
21
+
22
+ def self.included(base)
23
+ base.class_eval do
24
+ alias_method_chain :resources, :paged
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ if defined? ActionController::Resources
31
+ ActionController::Resources.send :include, PagedScopes::Resources
32
+ end
@@ -0,0 +1,72 @@
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{paged_scopes}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Matthew Hollingworth"]
12
+ s.date = %q{2009-10-10}
13
+ s.email = %q{mdholling@gmail.com}
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.textile"
17
+ ]
18
+ s.files = [
19
+ ".document",
20
+ ".gitignore",
21
+ "LICENSE",
22
+ "README.textile",
23
+ "Rakefile",
24
+ "VERSION.yml",
25
+ "lib/paged_scopes.rb",
26
+ "lib/paged_scopes/collection.rb",
27
+ "lib/paged_scopes/context.rb",
28
+ "lib/paged_scopes/controller.rb",
29
+ "lib/paged_scopes/index.rb",
30
+ "lib/paged_scopes/pages.rb",
31
+ "lib/paged_scopes/paginator.rb",
32
+ "lib/paged_scopes/resources.rb",
33
+ "paged_scopes.gemspec",
34
+ "rails/init.rb",
35
+ "spec/collection_spec.rb",
36
+ "spec/context_spec.rb",
37
+ "spec/controller_spec.rb",
38
+ "spec/index_spec.rb",
39
+ "spec/page_spec.rb",
40
+ "spec/paginator_spec.rb",
41
+ "spec/resources_spec.rb",
42
+ "spec/spec_helper.rb"
43
+ ]
44
+ s.homepage = %q{http://github.com/mholling/paged_scopes}
45
+ s.rdoc_options = ["--charset=UTF-8"]
46
+ s.require_paths = ["lib"]
47
+ s.rubygems_version = %q{1.3.5}
48
+ s.summary = %q{PagedScopes is an ActiveRecord pagination gem. It lets you easily paginate collection associations and named scopes. It also paginates collections which already have :limit and :offset scopes in place. You can also find the page containing a given object in a collection, and find the next and previous objects for each object in the collection.}
49
+ s.test_files = [
50
+ "spec/collection_spec.rb",
51
+ "spec/context_spec.rb",
52
+ "spec/controller_spec.rb",
53
+ "spec/index_spec.rb",
54
+ "spec/page_spec.rb",
55
+ "spec/paginator_spec.rb",
56
+ "spec/resources_spec.rb",
57
+ "spec/spec_helper.rb"
58
+ ]
59
+
60
+ if s.respond_to? :specification_version then
61
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
62
+ s.specification_version = 3
63
+
64
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
65
+ s.add_runtime_dependency(%q<activerecord>, [">= 2.2.1"])
66
+ else
67
+ s.add_dependency(%q<activerecord>, [">= 2.2.1"])
68
+ end
69
+ else
70
+ s.add_dependency(%q<activerecord>, [">= 2.2.1"])
71
+ end
72
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'paged_scopes'
@@ -0,0 +1,66 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Collection" do
4
+ describe "(ActiveRecord::Base class)" do
5
+ it "should have a per_page setter and getter" do
6
+ Article.per_page = 5
7
+ Article.per_page.should == 5
8
+ end
9
+
10
+ it "should have a default page name of 'Page'" do
11
+ Article.page_name.should == "Page"
12
+ end
13
+
14
+ it "should have a page_name setter and getter" do
15
+ Article.page_name = "Group"
16
+ Article.page_name.should == "Group"
17
+ end
18
+
19
+ it "should not have a #pages method" do
20
+ Article.respond_to?(:pages).should be_false
21
+ end
22
+ end
23
+
24
+ [ [ "association", "User.first.articles" ],
25
+ [ "named scope", "Article.scoped(:conditions => 'title IS NULL')" ] ].each do |collection_type, collection|
26
+ describe "(#{collection_type})" do
27
+ before(:each) do
28
+ @collection = eval(collection)
29
+ Article.stub!(:per_page).and_return(10)
30
+ Article.page_name = "Page"
31
+ end
32
+
33
+ it "should have a default per_page of the ActiveRecord::Base class" do
34
+ @collection.per_page.should == 10
35
+ end
36
+
37
+ it "should have a per_page setter and getter" do
38
+ @collection.per_page = 20
39
+ @collection.per_page.should == 20
40
+ end
41
+
42
+ it "should not overwrite the ActiveRecord::Base per_page value when per_page is set" do
43
+ @collection.per_page = 20
44
+ Article.per_page.should == 10
45
+ end
46
+
47
+ it "should have a default page_name of the ActiveRecord::Base class" do
48
+ @collection.page_name.should == "Page"
49
+ end
50
+
51
+ it "should have a page_name setter and getter" do
52
+ @collection.page_name = "Group"
53
+ @collection.page_name.should == "Group"
54
+ end
55
+
56
+ it "should not overwrite the ActiveRecord::Base page_name value when page_name is set" do
57
+ @collection.page_name = "Group"
58
+ Article.page_name.should == "Page"
59
+ end
60
+
61
+ it "should have a #pages method" do
62
+ @collection.respond_to?(:pages).should be_true
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Context" do
4
+ in_contexts do
5
+ it "should know the object after an object obtained with #all" do
6
+ articles = @articles.all
7
+ until articles.empty? do
8
+ articles.shift.next.should == articles.first
9
+ end
10
+ end
11
+
12
+ it "should know the object before an object obtained with #all" do
13
+ articles = @articles.all
14
+ until articles.empty? do
15
+ articles.pop.previous.should == articles.last
16
+ end
17
+ end
18
+
19
+ it "should be known for an object obtained with #first" do
20
+ @articles.all.should have_at_least(2).articles
21
+ @articles.first.next.should_not be_nil
22
+ @articles.first.previous.should be_nil
23
+ end
24
+ # (For a collection including :limit , this won't work with #last unless the collection
25
+ # has been evaluated already!)
26
+ end
27
+ end
@@ -0,0 +1,234 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Controller" do
4
+
5
+ context "class" do
6
+ it "should raise 404 on PagedScopes::PageNotFound" do
7
+ Class.new(ActionController::Base).rescue_responses['PagedScopes::PageNotFound'].should == :not_found
8
+ end
9
+
10
+ it "should get the default collection name from the controller name" do
11
+ [ "ArticlesController", "ArticleController", "Admin::ArticlesController" ].each do |controller_name|
12
+ @class = Class.new(ActionController::Base)
13
+ @class.stub!(:name).and_return(controller_name)
14
+ @class.send(:default_collection_name).should == "articles"
15
+ end
16
+ end
17
+
18
+ it "should not get the default collection name from a misnamed controller" do
19
+ [ "", "SomeClassName" ].each do |controller_name|
20
+ @class = Class.new(ActionController::Base)
21
+ @class.stub!(:name).and_return(controller_name)
22
+ lambda { @class.send(:default_collection_name) }.should raise_error(RuntimeError)
23
+ end
24
+ end
25
+ end
26
+
27
+ context "instance using #page_for" do
28
+ before(:each) do
29
+ @class = Class.new(ActionController::Base)
30
+ @controller = @class.new
31
+ @articles = User.first.articles
32
+ @articles.per_page = 3
33
+ end
34
+
35
+ it "should default to the first page of the collection" do
36
+ in_controller @controller do
37
+ page_for(@articles).should == @articles.pages.first
38
+ end
39
+ end
40
+
41
+ it "should find the page from a page id in the params" do
42
+ in_controller @controller do
43
+ @articles.pages.each do |page|
44
+ stub!(:params).and_return(:page_id => page.id)
45
+ page_for(@articles).should == page
46
+ end
47
+ end
48
+ end
49
+
50
+ it "should find the page containing an object if specified" do
51
+ in_controller @controller do
52
+ @articles.each do |article|
53
+ page_for(@articles, article).should == @articles.pages.find_by_article(article)
54
+ # page_for(@articles, article).articles.all.should include(article)
55
+ end
56
+ end
57
+ end
58
+
59
+ it "should set per_page on the collection if specified" do
60
+ in_controller @controller do
61
+ page_for(@articles, :per_page => 4)
62
+ @articles.per_page.should == 4
63
+ end
64
+ end
65
+
66
+ it "should set the page model name on the collection if specified" do
67
+ in_controller @controller do
68
+ page_for(@articles, :name => "Group")
69
+ @articles.page_name.should == "Group"
70
+ end
71
+ end
72
+
73
+ it "should set the pagination path proc if specified using a :path option" do
74
+ in_controller @controller do
75
+ @page = page_for(@articles, :path => :page_articles_path)
76
+ self.should_receive(:page_articles_path).with(@page.next)
77
+ @page.paginator.next
78
+ end
79
+ end
80
+
81
+ it "should set the pagination path proc if specified with a block" do
82
+ in_controller @controller do
83
+ @page = page_for(@articles) { |page| "path/to/page/#{page.to_param}" }
84
+ @page.paginator.next.should == "path/to/page/#{@page.next.to_param}"
85
+ end
86
+ end
87
+ end
88
+
89
+ context "class using #paginate" do
90
+ before(:each) do
91
+ @class = Class.new(ActionController::Base)
92
+ end
93
+
94
+ it "should add a protected paginate callback as a before filter when paginate is called" do
95
+ @class.paginate :articles
96
+ @class.before_filters.map(&:to_s).should include("paginate_articles")
97
+ @class.protected_instance_methods.map(&:to_s).should include("paginate_articles")
98
+ end
99
+
100
+ it "should use the default collection name if no collection name is specified" do
101
+ @class.stub!(:name).and_return("ArticlesController")
102
+ @class.paginate
103
+ @class.before_filters.map(&:to_s).should include("paginate_articles")
104
+ end
105
+
106
+ it "should pass filter options except for :per_page, :name and :path on to the before filter" do
107
+ @options = { :per_page => 3, :name => "Group", :path => :page_articles_path, :only => [ :index, :show ], :if => :test }
108
+ @class.paginate :articles, @options
109
+ @filter = @class.filter_chain.detect { |filter| filter.method.to_s == "paginate_articles" }
110
+ @filter.options.keys.should_not include(:per_page, :name, :path)
111
+ @filter.options.keys.should include(:only, :if)
112
+ end
113
+ end
114
+
115
+ context "instance using paginate with options in the controller class" do
116
+ before(:each) do
117
+ @options = { :per_page => 3, :name => "Group", :path => :page_articles_path, :only => [ :index, :show ], :if => :test }
118
+ @class = Class.new(ActionController::Base)
119
+ @class.paginate :articles, @options
120
+ @controller = @class.new
121
+ end
122
+
123
+ it "should set :per_page on the collection" do
124
+ in_controller @controller do
125
+ @articles = User.first.articles
126
+ paginate_articles
127
+ @articles.per_page.should == @options[:per_page]
128
+ end
129
+ end
130
+
131
+ it "should set :page_name on the collection" do
132
+ in_controller @controller do
133
+ @articles = User.first.articles
134
+ paginate_articles
135
+ @articles.page_name.should == @options[:name]
136
+ end
137
+ end
138
+
139
+ it "should set a page instance variable named accordingly" do
140
+ in_controller @controller do
141
+ @articles = User.first.articles
142
+ paginate_articles
143
+ @group.should_not be_nil
144
+ end
145
+ end
146
+
147
+ it "should set :path on the page's paginator" do
148
+ in_controller @controller do
149
+ @articles = User.first.articles
150
+ paginate_articles
151
+ self.should_receive(@options[:path])
152
+ @group.paginator.first
153
+ end
154
+ end
155
+ end
156
+
157
+ context "instance using #paginate in the controller class" do
158
+ before(:each) do
159
+ @class = Class.new(ActionController::Base)
160
+ @class.paginate :articles, :per_page => 3
161
+ @controller = @class.new
162
+ end
163
+
164
+ it "should raise an error if no collection is set" do
165
+ in_controller @controller do
166
+ lambda { paginate_articles }.should raise_error(RuntimeError)
167
+ end
168
+ end
169
+
170
+ it "should pass :per_page, :name and :path options on to the call to #page_for" do
171
+ @options = { :per_page => 3, :name => "Group", :path => :page_articles_path }
172
+ @class.paginate :articles, @options
173
+ @controller = @class.new
174
+ in_controller @controller do
175
+ @articles = User.first.articles
176
+ self.should_receive(:page_for).with(@articles, nil, @options)
177
+ paginate_articles
178
+ end
179
+ end
180
+
181
+ context "when the collection is set before the filter is run" do
182
+ before(:each) do
183
+ in_controller @controller do
184
+ @articles = User.first.articles
185
+ end
186
+ end
187
+
188
+ it "should get the page from the collection object if set as an instance variable" do
189
+ in_controller @controller do
190
+ @articles.each do |article|
191
+ @article = article
192
+ paginate_articles
193
+ @page.articles.all.should include(@article)
194
+ end
195
+ end
196
+ end
197
+
198
+ it "should get the page from the params if a collection object is not set or is a new record" do
199
+ in_controller @controller do
200
+ @articles.per_page = 3
201
+ stub!(:params).and_return(:page_id => @articles.pages.last.id)
202
+ [ nil, Article.new ].each do |article|
203
+ @article = article
204
+ paginate_articles
205
+ @page.should == @articles.pages.last
206
+ end
207
+ end
208
+ end
209
+
210
+ it "should raise PageNotFound if the page id in the params is not in range" do
211
+ in_controller @controller do
212
+ @articles.per_page = 3
213
+ stub!(:params).and_return(:page_id => @articles.pages.last.id + 1)
214
+ lambda { paginate_articles }.should raise_error(PagedScopes::PageNotFound)
215
+ end
216
+ end
217
+
218
+ it "should get the first page if the current object is a new record" do
219
+ in_controller @controller do
220
+ @article = @articles.new
221
+ paginate_articles
222
+ @page.should == @articles.pages.first
223
+ end
224
+ end
225
+
226
+ it "should otherwise default to the first page" do
227
+ in_controller @controller do
228
+ paginate_articles
229
+ @page.should == @articles.pages.first
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end