mholling-paged_scopes 0.0.1 → 0.0.3

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/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
+ require 'yaml'
3
4
 
4
5
  begin
5
6
  require 'jeweler'
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 1
2
+ :patch: 3
3
3
  :major: 0
4
4
  :minor: 0
@@ -11,11 +11,11 @@ module PagedScopes
11
11
 
12
12
  module ClassMethods
13
13
  def find_with_context(*args)
14
+ found_scope, found_options = (scope(:find) || {}).dup, args.dup.extract_options!.dup
14
15
  returning find_without_context(*args) do |results|
15
- found_scope, found_options = scope(:find), args.extract_options!
16
- [ results ].flatten.each do |result|
17
- result.instance_variable_set "@found_scope", found_scope
18
- result.instance_variable_set "@found_options", found_options
16
+ [ results ].flatten.compact.each do |result|
17
+ result.instance_variable_set "@found_scope", found_scope.dup
18
+ result.instance_variable_set "@found_options", found_options.dup
19
19
  end
20
20
  end
21
21
  end
@@ -7,16 +7,18 @@ module PagedScopes
7
7
  raise RuntimeError, "no @#{collection_name.to_s.pluralize} collection was set" unless collection
8
8
  object = instance_variable_get("@#{collection_name.to_s.singularize}")
9
9
  collection.per_page = options[:per_page] if options[:per_page]
10
- collection.name = options[:name] if options[:name]
11
- page = collection.pages.from_params(params) || (object && collection.pages.find_by_object(object)) || collection.pages.first
10
+ collection.page_name = options[:name] if options[:name]
11
+ page = collection.pages.from_params!(params) || (object && collection.pages.find_by_object(object)) || collection.pages.first
12
+ page.paginator.set_path { |pg| send(options[:path], pg) } if options[:path]
12
13
  instance_variable_set("@#{collection.pages.name.underscore}", page)
13
14
  end
14
15
  protected callback_method
15
- before_filter callback_method
16
+ before_filter callback_method, options.except(:per_page, :name, :path)
16
17
  end
17
18
  end
18
19
  end
19
20
 
20
21
  if defined? ActionController::Base
21
22
  ActionController::Base.extend PagedScopes::Controller
23
+ ActionController::Base.rescue_responses.update('PagedScopes::PageNotFound' => :not_found)
22
24
  end
@@ -64,10 +64,8 @@ module PagedScopes
64
64
 
65
65
  def after(object)
66
66
  after_index = index_of(object) + 1
67
- find_scope = scope(:find) || {}
68
- if find_scope[:limit]
69
- offset = (find_scope[:offset] || 0).to_i
70
- after_index >= find_scope[:limit] ? nil : first(:offset => after_index + offset)
67
+ if limit = scope(:find, :limit)
68
+ after_index >= limit ? nil : first(:offset => after_index + (scope(:find, :offset) || 0))
71
69
  else
72
70
  first(:offset => after_index)
73
71
  end
@@ -75,10 +73,8 @@ module PagedScopes
75
73
 
76
74
  def before(object)
77
75
  before_index = index_of(object) - 1
78
- find_scope = scope(:find) || {}
79
- if find_scope[:limit]
80
- offset = find_scope[:offset].to_i
81
- before_index < 0 ? nil : first(:offset => before_index + offset)
76
+ if scope(:find, :limit)
77
+ before_index < 0 ? nil : first(:offset => before_index + (scope(:find, :offset) || 0))
82
78
  else
83
79
  before_index < 0 ? nil : first(:offset => before_index)
84
80
  end
@@ -48,13 +48,8 @@ module PagedScopes
48
48
  end
49
49
 
50
50
  def from_params!(params)
51
- find(params[name.underscore.foreign_key.to_sym])
52
- end
53
-
54
- def from_params(params)
55
- from_params!(params)
56
- rescue PageNotFound
57
- nil
51
+ number = params[name.underscore.foreign_key.to_sym]
52
+ number ? find(number) : nil
58
53
  end
59
54
 
60
55
  def count
@@ -67,7 +62,7 @@ module PagedScopes
67
62
  else
68
63
  proxy.count("#{proxy.table_name}.#{proxy.primary_key}", :distinct => true)
69
64
  end
70
- (collection_count - 1)/per_page + 1
65
+ [ (collection_count - 1)/per_page + 1, 1].max
71
66
  end
72
67
 
73
68
  memoize :count
@@ -89,7 +84,12 @@ module PagedScopes
89
84
  end
90
85
 
91
86
  def closest_to(number)
92
- find([ [ 1, number ].max, count ].min)
87
+ closest_number = [ [ 1, number ].max, count ].min
88
+ closest_number > 0 ? find(closest_number) : nil ## TODO this is unneeded now!
89
+ end
90
+
91
+ def reload!
92
+ unmemoize_all
93
93
  end
94
94
 
95
95
  private
@@ -108,13 +108,19 @@ module PagedScopes
108
108
  private :proxy, :proxy_options, :proxy_scoped?
109
109
 
110
110
  def initialize(number)
111
- unless number > 0 && number <= self.class.count
111
+ unless number > 0 && number <= page_count
112
112
  raise PageNotFound.new("couldn't find page number #{number}", self.class.closest_to(number))
113
113
  end
114
114
  @number = number
115
115
  @paginator = PagedScopes::Paginator.new(self)
116
116
  end
117
117
 
118
+ def reload!
119
+ self.class.reload!
120
+ self.class.find(number)
121
+ unmemoize_all
122
+ end
123
+
118
124
  def page_count
119
125
  self.class.count
120
126
  end
@@ -11,7 +11,7 @@ module PagedScopes
11
11
  end
12
12
 
13
13
  def path
14
- @path || raise(ArgumentError, "No path proc supplied.")
14
+ @path || raise(RuntimeError, "No path proc supplied.")
15
15
  end
16
16
 
17
17
  def previous
@@ -23,25 +23,27 @@ module PagedScopes
23
23
  end
24
24
 
25
25
  def window(options)
26
+ results = []
26
27
  size = options[:size]
27
28
  extras = [ options[:extras] ].flatten.compact
28
29
  raise ArgumentError, "No window block supplied." unless block_given?
29
30
  return if @page.page_count < 2
30
31
  if @page.number - size > 1
31
- yield :first, @path.call(@page.class.first) if extras.include? :first
32
+ results << yield(:first, @path.call(@page.class.first)) if extras.include? :first
32
33
  if extras.include?(:previous) && offset_page = @page.offset(-2 * size - 1)
33
- yield :previous, @path.call(offset_page)
34
+ results << yield(:previous, @path.call(offset_page))
34
35
  end
35
36
  end
36
37
  (-size..size).map { |offset| @page.offset(offset) }.compact.each do |page|
37
- yield page, @path.call(page)
38
+ results << yield( page, @path.call(page))
38
39
  end
39
40
  if @page.number + size < @page.page_count
40
41
  if extras.include?(:next) && offset_page = @page.offset(2 * size + 1)
41
- yield :next, @path.call(offset_page)
42
+ results << yield(:next, @path.call(offset_page))
42
43
  end
43
- yield :last, @path.call(@page.class.last) if extras.include? :last
44
+ results << yield(:last, @path.call(@page.class.last)) if extras.include? :last
44
45
  end
46
+ results.join("\n")
45
47
  end
46
48
  end
47
49
  end
data/spec/context_spec.rb CHANGED
@@ -2,18 +2,26 @@ require 'spec_helper'
2
2
 
3
3
  describe "Context" do
4
4
  in_contexts do
5
- it "should know the object after an object in the collection" do
5
+ it "should know the object after an object obtained with #all" do
6
6
  articles = @articles.all
7
7
  until articles.empty? do
8
8
  articles.shift.next.should == articles.first
9
9
  end
10
10
  end
11
11
 
12
- it "should know the object before an object in the collection" do
12
+ it "should know the object before an object obtained with #all" do
13
13
  articles = @articles.all
14
14
  until articles.empty? do
15
15
  articles.pop.previous.should == articles.last
16
16
  end
17
17
  end
18
- 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
19
27
  end
@@ -1,40 +1,71 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "Controller" do
4
+
4
5
  context "class" do
6
+ before(:each) do
7
+ @class = Class.new(ActionController::Base)
8
+ end
9
+
10
+ it "should raise 404 on PagedScopes::PageNotFound" do
11
+ @class.rescue_responses['PagedScopes::PageNotFound'].should == :not_found
12
+ end
13
+
5
14
  it "should add a protected get_page_for callback as a before filter when get_page_for is called" do
6
- in_controller_class do
7
- get_page_for :articles
8
- before_filters.should include("get_page_for_articles")
9
- protected_instance_methods.should include("get_page_for_articles")
10
- end
15
+ @class.get_page_for :articles
16
+ @class.before_filters.map(&:to_s).should include("get_page_for_articles")
17
+ @class.protected_instance_methods.map(&:to_s).should include("get_page_for_articles")
11
18
  end
19
+
20
+ it "should pass filter options except for :per_page, :name and :path on to the before filter" do
21
+ @options = { :per_page => 3, :name => "Group", :path => :page_articles_path, :only => [ :index, :show ], :if => :test }
22
+ @class.get_page_for :articles, @options
23
+ @filter = @class.filter_chain.detect { |filter| filter.method.to_s == "get_page_for_articles" }
24
+ @filter.options.keys.should_not include(:per_page, :name, :path)
25
+ @filter.options.keys.should include(:only, :if)
26
+ end
27
+
12
28
  end
13
-
14
- describe "instance" do
29
+
30
+ context "instance" do
31
+ before(:each) do
32
+ @controller = Class.new(ActionController::Base) do
33
+ get_page_for :articles
34
+ end.new
35
+ end
36
+
15
37
  it "should raise an error if no collection is set" do
16
- in_controller_instance_with_paged(:articles) do
38
+ in_controller @controller do
17
39
  lambda { get_page_for_articles }.should raise_error(RuntimeError)
18
40
  end
19
41
  end
20
-
42
+
21
43
  context "when the collection is set" do
22
- before(:all) do
23
- @articles = User.first.articles
24
- @articles.per_page = 3
44
+ before(:each) do
45
+ in_controller @controller do
46
+ @articles = User.first.articles
47
+ @articles.per_page = 3
48
+ end
25
49
  end
26
50
 
27
- it "should get the page from a page id in the params" do
28
- in_controller_instance_with_paged(:articles) do
29
- stub!(:params).and_return({ :page_id => @articles.pages.last.id })
51
+ it "should get the page from a page id in the params" do
52
+ in_controller @controller do
53
+ stub!(:params).and_return(:page_id => @articles.pages.last.id)
30
54
  get_page_for_articles
31
- @page.articles.should include(@articles.last)
55
+ @page.should == @articles.pages.last
32
56
  end
33
57
  end
34
-
58
+
59
+ it "should raise PageNotFound if the page id in the params is not in range" do
60
+ in_controller @controller do
61
+ stub!(:params).and_return(:page_id => @articles.pages.last.id + 1)
62
+ lambda { get_page_for_articles }.should raise_error(PagedScopes::PageNotFound)
63
+ end
64
+ end
65
+
35
66
  it "should otherwise get the page from the current object if no page id is present in the params" do
36
- @article = @articles.last
37
- in_controller_instance_with_paged(:articles) do
67
+ in_controller @controller do
68
+ @article = @articles.last
38
69
  get_page_for_articles
39
70
  @page.should == @articles.pages.find_by_article(@article)
40
71
  @page.articles.should include(@article)
@@ -42,19 +73,63 @@ describe "Controller" do
42
73
  end
43
74
 
44
75
  it "should get the first page if the current object is a new record" do
45
- @article = @articles.new
46
- in_controller_instance_with_paged(:articles) do
76
+ in_controller @controller do
77
+ @article = @articles.new
47
78
  get_page_for_articles
48
79
  @page.should == @articles.pages.first
49
80
  end
50
81
  end
51
82
 
52
83
  it "should otherwise get the first page" do
53
- in_controller_instance_with_paged(:articles) do
84
+ in_controller @controller do
54
85
  get_page_for_articles
55
86
  @page.should == @articles.pages.first
56
87
  end
57
88
  end
58
89
  end
59
90
  end
60
- end
91
+
92
+ context "instance when :per_page is specified in the call to #get_page_for" do
93
+ it "should set per_page on the collection" do
94
+ @controller = Class.new(ActionController::Base) do
95
+ get_page_for :articles, :per_page => 3
96
+ end.new
97
+ in_controller @controller do
98
+ @articles = User.first.articles
99
+ @articles.per_page = nil
100
+ get_page_for_articles
101
+ @articles.per_page.should == 3
102
+ @articles.pages.per_page.should == 3
103
+ end
104
+ end
105
+ end
106
+
107
+ context "instance when :name is specified in the call to #get_page_for" do
108
+ it "should set page_name on the collection" do
109
+ @controller = Class.new(ActionController::Base) do
110
+ get_page_for :articles, :per_page => 3, :name => "Group"
111
+ end.new
112
+ in_controller @controller do
113
+ @articles = User.first.articles
114
+ get_page_for_articles
115
+ @articles.page_name.should == "Group"
116
+ @articles.pages.name.should == "Group"
117
+ end
118
+ end
119
+ end
120
+
121
+ context "instance when :path is specified in the call to #get_page_for" do
122
+ it "should set page's pagination path to the specified controller method" do
123
+ @controller = Class.new(ActionController::Base) do
124
+ get_page_for :articles, :per_page => 3, :path => :page_articles_path
125
+ end.new
126
+ in_controller @controller do
127
+ @articles = User.first.articles
128
+ @article = @articles.first
129
+ get_page_for_articles
130
+ self.should_receive(:page_articles_path).with(@page.next)
131
+ @page.paginator.next
132
+ end
133
+ end
134
+ end
135
+ end
data/spec/page_spec.rb CHANGED
@@ -3,6 +3,7 @@ require 'spec_helper'
3
3
  describe "Pages" do
4
4
  in_contexts do
5
5
  before(:each) do
6
+ @pages = @articles.pages
6
7
  @per_page = 3
7
8
  @articles.stub!(:per_page).and_return(@per_page)
8
9
  @articles.stub!(:page_name).and_return("Page")
@@ -30,11 +31,35 @@ describe "Pages" do
30
31
  @pages.count.should == (@articles.all.length - 1)/@per_page + 1
31
32
  end
32
33
 
34
+ it "should cache the page count" do
35
+ @articles.should_receive(:count).and_return(1)
36
+ 2.times { @pages.count }
37
+ end
38
+
39
+ it "should clear the count cache when reloaded" do
40
+ @articles.should_receive(:count).twice.and_return(1)
41
+ @pages.count
42
+ @pages.reload!
43
+ @pages.count
44
+ end
45
+
33
46
  it "should find pages with valid numbers" do
34
47
  (1..@pages.count).each do |number|
35
48
  lambda { @pages.find(number) }.should_not raise_error
36
49
  end
37
50
  end
51
+
52
+ it "should cache the results of find" do
53
+ @pages.should_receive(:new).once
54
+ 2.times { @pages.find(1) }
55
+ end
56
+
57
+ it "should clear the find cache when reloaded" do
58
+ @pages.should_receive(:new).twice
59
+ @pages.find(1)
60
+ @pages.reload!
61
+ @pages.find(1)
62
+ end
38
63
 
39
64
  it "should raise an error containing the nearest substitute page for invalid page numbers" do
40
65
  [ [ -1, @pages.first], [ 0, @pages.first ], [ @pages.count + 1, @pages.last ] ].each do |number, substitute_page|
@@ -84,8 +109,38 @@ describe "Pages" do
84
109
  end
85
110
 
86
111
  it "should find a page from a params hash with a pages name as an id in the key" do
87
- @pages.stub!(:name).and_return("Group")
88
- @pages.from_params(:group_id => "1").should == @pages.first
112
+ @pages.stub!(:name).and_return("Page")
113
+ @pages.from_params!(:page_id => "1").should == @pages.first
114
+ end
115
+
116
+ it "should find a nil page from a params hash without a pages name as an id in the key" do
117
+ @pages.stub!(:name).and_return("Page")
118
+ @pages.from_params!({}).should be_nil
119
+ end
120
+
121
+ it "should raise an error from a params hash containing an out-of-range page id in the key" do
122
+ @pages.stub!(:name).and_return("Page")
123
+ lambda { @pages.from_params!(:page_id => @pages.count + 1) }.should raise_error(PagedScopes::PageNotFound)
124
+ end
125
+ end
126
+
127
+ context "for an empty collection" do
128
+ before(:each) do
129
+ @articles = Article.scoped(:conditions => { :title => "Supercalifragilisticexpialidocious" })
130
+ @articles.all.should be_empty
131
+ @articles.per_page = 3
132
+ end
133
+
134
+ it "should have one page" do
135
+ @articles.pages.count.should == 1
136
+ end
137
+
138
+ it "should have a page numbered one" do
139
+ @articles.pages.first.number.should == 1
140
+ end
141
+
142
+ it "should have an empty page" do
143
+ @articles.pages.first.articles.all.should be_empty
89
144
  end
90
145
  end
91
146
  end
@@ -93,6 +148,7 @@ end
93
148
  describe "Page instance" do
94
149
  in_contexts do
95
150
  before(:each) do
151
+ @pages = @articles.pages
96
152
  @per_page = 3
97
153
  @articles.stub!(:per_page).and_return(@per_page)
98
154
  end
@@ -113,6 +169,17 @@ describe "Page instance" do
113
169
  @pages.map(&:id).should == @pages.map(&:number)
114
170
  end
115
171
 
172
+ it "should clear the page class cache when reloaded" do
173
+ @pages.should_receive(:reload!)
174
+ @pages.first.reload!
175
+ end
176
+
177
+ it "should be found again by the page class when reloaded" do
178
+ @page = @pages.first
179
+ @pages.should_receive(:find).with(@page.number)
180
+ @page.reload!
181
+ end
182
+
116
183
  it "should know whether it's first" do
117
184
  pages = @pages.all
118
185
  pages.shift.should be_first
@@ -12,7 +12,7 @@ describe "Paginator" do
12
12
  end
13
13
 
14
14
  it "should raise an error if the paginator path is not set" do
15
- lambda { @pages.first.paginator.next }.should raise_error
15
+ lambda { @pages.first.paginator.next }.should raise_error(RuntimeError)
16
16
  end
17
17
 
18
18
  context "for the first page" do
@@ -74,6 +74,13 @@ describe "Paginator" do
74
74
  lambda { @pages.first.paginator.window({}) }.should raise_error(ArgumentError)
75
75
  end
76
76
 
77
+ it "should concatenate all the block return values into a string" do
78
+ page = @pages.find(6)
79
+ page.paginator.set_path { |page| }
80
+ links = (1..5).map { |n| "<li><a href='/path/to/page/#{n}'>1</a></li>" }
81
+ links.join("\n").should == page.paginator.window(:size => 2) { |page, path| links.shift }
82
+ end
83
+
77
84
  it "should call the block with the page and the path for each page in a window surrounding the page" do
78
85
  [ [ 6, 6-@size..6+@size ], [ 2, 1..2+@size ], [ 1, 1..1+@size ], [ @page_count-1, @page_count-1-@size..@page_count ], [ @page_count, @page_count-@size..@page_count ] ].each do |number, range|
79
86
  page = @pages.find(number)
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe "Resources" do
2
4
  before(:each) do
3
5
  ActionController::Routing::Routes.clear!
data/spec/spec_helper.rb CHANGED
@@ -48,20 +48,15 @@ end
48
48
  end
49
49
 
50
50
  module ControllerHelpers
51
- def in_controller_class(&block)
52
- Class.new(ActionController::Base) do
51
+ def in_instance(instance, &block)
52
+ instance.instance_eval do
53
53
  extend Spec::Matchers
54
54
  instance_eval(&block)
55
55
  end
56
56
  end
57
-
58
- def in_controller_instance_with_paged(collection, &block)
59
- controller = Class.new(ActionController::Base) do
60
- get_page_for collection
61
- end.new
62
- controller.copy_instance_variables_from(self)
63
- controller.instance_eval do
64
- extend Spec::Matchers
57
+
58
+ def in_controller(controller, &block)
59
+ in_instance controller do
65
60
  stub!(:params).and_return({})
66
61
  instance_eval(&block)
67
62
  end
@@ -115,7 +110,6 @@ module Contexts
115
110
  before(:each) do
116
111
  @articles = eval("#{base}#{scope}")
117
112
  @articles.all.should_not be_empty
118
- @pages = @articles.pages
119
113
  end
120
114
  instance_eval(&block)
121
115
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mholling-paged_scopes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Hollingworth
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-15 00:00:00 -07:00
12
+ date: 2009-06-17 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency