paged_scopes 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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