mholling-paged_scopes 0.0.4 → 0.0.5

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/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 4
2
+ :patch: 5
3
3
  :major: 0
4
4
  :minor: 0
@@ -76,7 +76,7 @@ module PagedScopes
76
76
  end
77
77
 
78
78
  def each(&block)
79
- (1..count).each { |number| yield find(number) }
79
+ 1.upto(count) { |number| yield find(number) }
80
80
  end
81
81
 
82
82
  def all
@@ -21,19 +21,47 @@ module PagedScopes
21
21
  def next
22
22
  path.call(@page.next) unless @page.last?
23
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
24
32
 
25
33
  def window(options)
26
- size = options[:size]
27
- extras = [ options[:extras] ].flatten.compact
28
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) }
29
51
  returning [] do |results|
30
- results << yield(:first, @page.first? ? nil : @path.call(@page.class.first)) if extras.include?(:first)
31
- results << yield(:previous, @page.first? ? nil : @path.call(@page.previous)) if extras.include?(:previous)
32
- (-size..size).map { |offset| @page.offset(offset) }.compact.each do |page|
33
- results << yield(page, @path.call(page))
34
- end
35
- results << yield(:next, @page.last? ? nil : @path.call(@page.next)) if extras.include?(:next)
36
- results << yield(:last, @page.last? ? nil : @path.call(@page.class.last)) if extras.include?(:last)
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)
37
65
  end.join("\n")
38
66
  end
39
67
  end
data/spec/page_spec.rb CHANGED
@@ -44,7 +44,7 @@ describe "Pages" do
44
44
  end
45
45
 
46
46
  it "should find pages with valid numbers" do
47
- (1..@pages.count).each do |number|
47
+ 1.upto(@pages.count) do |number|
48
48
  lambda { @pages.find(number) }.should_not raise_error
49
49
  end
50
50
  end
@@ -107,6 +107,14 @@ describe "Pages" do
107
107
  lambda { @pages.find_by_article!(article) }.should raise_error(PagedScopes::PageNotFound)
108
108
  end
109
109
  end
110
+
111
+ it "should not find the page of an object if the object is a new record" do
112
+ lambda { @pages.find_by_article!(Article.new) }.should raise_error(PagedScopes::PageNotFound)
113
+ end
114
+
115
+ it "should not find the page of an object if the object is not an ActiveRecord::Base instance" do
116
+ lambda { @pages.find_by_article!(Object.new) }.should raise_error(PagedScopes::PageNotFound)
117
+ end
110
118
 
111
119
  it "should find a page from a params hash with a pages name as an id in the key" do
112
120
  @pages.stub!(:name).and_return("Page")
@@ -3,17 +3,32 @@ require 'spec_helper'
3
3
  describe "Paginator" do
4
4
  before(:each) do
5
5
  @articles = Article.scoped({})
6
- @articles.per_page = 3
6
+ @articles.per_page = 2
7
7
  @pages = @articles.pages
8
- @size = 2 # window size
9
- @page_count = @pages.count
10
- (@page_count >= 14).should be_true # window specs won't work otherwise
11
8
  @path = lambda { |page| "/path/to/page/#{page.to_param}" }
12
9
  end
13
10
 
14
11
  it "should raise an error if the paginator path is not set" do
15
12
  lambda { @pages.first.paginator.next }.should raise_error(RuntimeError)
16
13
  end
14
+
15
+ it "should call the path proc with the last page when #last is called" do
16
+ @pages.each do |page|
17
+ path = lambda { |page| }
18
+ page.paginator.set_path(&path)
19
+ path.should_receive(:call).with(@pages.last).and_return("/path")
20
+ page.paginator.last.should == "/path"
21
+ end
22
+ end
23
+
24
+ it "should call the path proc with the first page when #first is called" do
25
+ @pages.each do |page|
26
+ path = lambda { |page| }
27
+ page.paginator.set_path(&path)
28
+ path.should_receive(:call).with(@pages.first).and_return "/path"
29
+ page.paginator.first.should == "/path"
30
+ end
31
+ end
17
32
 
18
33
  context "for the first page" do
19
34
  before(:each) do
@@ -23,8 +38,8 @@ describe "Paginator" do
23
38
  end
24
39
 
25
40
  it "should call the path proc with the next page when #next is called" do
26
- @path.should_receive(:call).with(@page.next)
27
- @paginator.next
41
+ @path.should_receive(:call).with(@page.next).and_return("/path")
42
+ @paginator.next.should == "/path"
28
43
  end
29
44
 
30
45
  it "should not call the path proc when #previous is called" do
@@ -35,19 +50,19 @@ describe "Paginator" do
35
50
 
36
51
  context "for the last page" do
37
52
  before(:each) do
38
- @page = @pages.first
53
+ @page = @pages.last
39
54
  @paginator = @page.paginator
40
55
  @paginator.set_path(&@path)
41
56
  end
42
57
 
43
- it "should call the path proc with the next page when #next is called" do
44
- @path.should_receive(:call).with(@page.next)
45
- @paginator.next
58
+ it "should call the path proc with the previous page when #previous is called" do
59
+ @path.should_receive(:call).with(@page.previous).and_return("/path")
60
+ @paginator.previous.should == "/path"
46
61
  end
47
62
 
48
- it "should not call the path proc when #previous is called" do
63
+ it "should not call the path proc when #next is called" do
49
64
  @path.should_not_receive(:call)
50
- @paginator.previous.should be_nil
65
+ @paginator.next.should be_nil
51
66
  end
52
67
  end
53
68
 
@@ -59,44 +74,89 @@ describe "Paginator" do
59
74
  end
60
75
 
61
76
  it "should call the path proc with the next page when #next is called" do
62
- @path.should_receive(:call).with(@page.next)
63
- @paginator.next
77
+ @path.should_receive(:call).with(@page.next).and_return("/path")
78
+ @paginator.next.should == "/path"
64
79
  end
65
80
 
66
81
  it "should call the path proc with the previous page when #previous is called" do
67
- @path.should_receive(:call).with(@page.previous)
68
- @paginator.previous
82
+ @path.should_receive(:call).with(@page.previous).and_return("/path")
83
+ @paginator.previous.should == "/path"
69
84
  end
70
85
  end
71
86
 
72
87
  describe "window generator" do
73
88
  before(:each) do
74
- @args = []
89
+ @pages.stub!(:count).and_return(14)
90
+ @count = @pages.count
75
91
  end
76
92
 
77
93
  it "should raise an error if no block is provided" do
78
- lambda { @pages.first.paginator.window({}) }.should raise_error(ArgumentError)
94
+ lambda { @pages.first.paginator.window(:inner => 2) }.should raise_error(ArgumentError)
95
+ end
96
+
97
+ it "should raise an error if no inner size is provided" do
98
+ lambda { @pages.first.paginator.window({}) { |page, path| } }.should raise_error(ArgumentError)
79
99
  end
80
100
 
81
101
  it "should concatenate all the block return values into a string" do
82
102
  page = @pages.find(6)
83
103
  page.paginator.set_path { |page| }
84
- links = (1..5).map { |n| "<li><a href='/path/to/page/#{n}'>1</a></li>" }
85
- links.join("\n").should == page.paginator.window(:size => 2) { |page, path| links.shift }
104
+ links = (4..8).map { |n| "<li><a href='/path/to/page/#{6+n}'>#{6+n}</a></li>" }
105
+ links.join("\n").should == page.paginator.window(:inner => 2) { |page, path| links.shift }
86
106
  end
87
-
107
+
88
108
  it "should call the block with the page and the path for each page in a window surrounding the page" do
89
- [ [ 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|
109
+ [
110
+ [ 6, 4..8 ],
111
+ [ 3, 1..5 ],
112
+ [ 1, 1..5 ],
113
+ [ @count-2, @count-4..@count ],
114
+ [ @count, @count-4..@count ]
115
+ ].each do |number, range|
90
116
  page = @pages.find(number)
91
117
  page.paginator.set_path(&@path)
92
- expected_args = []
93
- range.map { |nearby_number| @pages.find(nearby_number) }.each do |nearby_page|
94
- expected_args << [ nearby_page, @path.call(nearby_page) ]
118
+ pages, paths = [], []
119
+ range.each do |n|
120
+ pages << @pages.find(n)
121
+ paths << (n == page.number ? nil : @path.call(@pages.find(n)))
95
122
  end
96
- page.paginator.window(:size => @size) do |*args|
97
- expected_args.shift.should == args
123
+ page.paginator.window(:inner => 2) do |pg, path, classes|
124
+ pg.should == pages.shift
125
+ path.should == paths.shift
126
+ pg == page ? classes.should(include(:selected)) : classes.should_not(include(:selected))
127
+ end
128
+ end
129
+ end
130
+
131
+ context "with an outer window" do
132
+ it "should also call the block for each page in a window from the first and last pages, and include separators between the windws if necessary" do
133
+ [
134
+ [ 6, 1..2, 6-2..6+2, @count-1..@count ],
135
+ [ 5, 1..5+2, @count-1..@count ],
136
+ [ 3, 1..5, @count-1..@count ],
137
+ [ 1, 1..5, @count-1..@count ],
138
+ [ @count-4, 1..2, @count-4-2..@count ],
139
+ [ @count-2, 1..2, @count-4..@count ],
140
+ [ @count, 1..2, @count-4..@count ]
141
+ ].each do |number, *ranges|
142
+ page = @pages.find(number)
143
+ page.paginator.set_path(&@path)
144
+ pages, paths, gaps_before, gaps_after = [], [], [], []
145
+ ranges.each do |range|
146
+ range.each do |n|
147
+ pages << @pages.find(n)
148
+ paths << (n == page.number ? nil : @path.call(@pages.find(n)))
149
+ end
150
+ end
151
+ pages.each_with_index { |pg, n| gaps_before << (pages[n-1] ? pages[n-1].number < pg.number - 1 : false) }
152
+ pages.each_with_index { |pg, n| gaps_after << (pages[n+1] ? pages[n+1].number > pg.number + 1 : false) }
153
+ page.paginator.window(:inner => 2, :outer => 2) do |pg, path, classes|
154
+ pg.should == pages.shift
155
+ path.should == paths.shift
156
+ gaps_before.shift ? classes.should(include(:gap_before)) : classes.should_not(include(:gap_before))
157
+ gaps_after.shift ? classes.should(include(:gap_after)) : classes.should_not(include(:gap_after))
158
+ end
98
159
  end
99
- expected_args.should be_empty
100
160
  end
101
161
  end
102
162
 
@@ -104,21 +164,23 @@ describe "Paginator" do
104
164
  it "should call the block with #{extra.inspect} and the path for the #{extra} page if #{extra.inspect} is specified as an extra" do
105
165
  page = @pages.find(number)
106
166
  page.paginator.set_path(&@path)
107
- page.paginator.window(:size => @size, :extras => [ extra ]) do |*args|
108
- @args << args
167
+ pages_paths = []
168
+ page.paginator.window(:inner => 2, :extras => [ extra ]) do |page, path, classes|
169
+ pages_paths << [ page, path ]
109
170
  end
110
- @args.should include([ extra, @path.call(@pages.find(new_number)) ])
171
+ pages_paths.should include([ extra, @path.call(@pages.find(new_number)) ])
111
172
  end
112
173
  end
113
174
 
114
- [ [ :previous, "1" ], [ :next, "@page_count" ] ].each do |extra, number|
175
+ [ [ :previous, "1" ], [ :next, "@count" ] ].each do |extra, number|
115
176
  it "should call the block with #{extra.inspect} and a nil path if #{extra.inspect} is specified as an extra but there is no #{extra} page" do
116
177
  page = @pages.find(eval(number))
117
178
  page.paginator.set_path(&@path)
118
- page.paginator.window(:size => @size, :extras => [ extra ]) do |*args|
119
- @args << args
179
+ pages_paths = []
180
+ page.paginator.window(:inner => 2, :extras => [ extra ]) do |page, path, classes|
181
+ pages_paths << [ page, path ]
120
182
  end
121
- @args.should include([ extra, nil ])
183
+ pages_paths.should include([ extra, nil ])
122
184
  end
123
185
  end
124
186
 
@@ -126,33 +188,69 @@ describe "Paginator" do
126
188
  it "should call the block with #{extra.inspect} and the path for the #{extra} page if #{extra.inspect} is specified as an extra" do
127
189
  page = @pages.find(6)
128
190
  page.paginator.set_path(&@path)
129
- page.paginator.window(:size => @size, :extras => [ extra ]) do |*args|
130
- @args << args
191
+ pages_paths = []
192
+ page.paginator.window(:inner => 2, :extras => [ extra ]) do |page, path, classes|
193
+ pages_paths << [ page, path ]
131
194
  end
132
- @args.should include([ extra, @path.call(@pages.send(extra)) ])
195
+ pages_paths.should include([ extra, @path.call(@pages.send(extra)) ])
133
196
  end
134
197
  end
135
198
 
136
- [ [ :first, "1" ], [ :last, "@page_count" ] ].each do |extra, number|
199
+ [ [ :first, "1" ], [ :last, "@count" ] ].each do |extra, number|
137
200
  it "should call the block with #{extra.inspect} and a nil path if #{extra.inspect} is specified as an extra but the current page is the #{extra} page" do
138
201
  page = @pages.find(eval(number))
139
202
  page.paginator.set_path(&@path)
140
- page.paginator.window(:size => @size, :extras => [ extra ]) do |*args|
141
- @args << args
203
+ pages_paths = []
204
+ page.paginator.window(:inner => 2, :extras => [ extra ]) do |page, path, classes|
205
+ pages_paths << [ page, path ]
142
206
  end
143
- @args.should include([ extra, nil ])
207
+ pages_paths.should include([ extra, nil ])
144
208
  end
145
209
  end
146
210
 
147
211
  it "should call the block with :first, :previous, pages, :next, :last in that order" do
148
212
  page = @pages.find(6)
149
213
  page.paginator.set_path(&@path)
150
- pages_in_order = []
151
- page.paginator.window(:size => 1, :extras => [ :first, :previous, :next, :last ]) do |page, path|
152
- pages_in_order << page
214
+ pages = []
215
+ page.paginator.window(:inner => 1, :extras => [ :first, :previous, :next, :last ]) do |page, path, classes|
216
+ pages << page
153
217
  end
154
- pages_in_order.should == [ :first, :previous, @pages.find(5), @pages.find(6), @pages.find(7), :next, :last ]
218
+ pages.should == [ :first, :previous, @pages.find(5), @pages.find(6), @pages.find(7), :next, :last ]
155
219
  end
156
220
  end
157
221
 
158
- end
222
+ describe "window generator for a collection with fewer pages than the window size" do
223
+ it "should list all the page" do
224
+ @pages.stub!(:count).and_return(5)
225
+ @pages.each do |page|
226
+ page.paginator.set_path(&@path)
227
+ pages = @pages.all
228
+ page.paginator.window(:inner => 2) do |pg, path, classes|
229
+ pg.should == pages.shift
230
+ if pg == page
231
+ path.should be_nil
232
+ classes.should include(:selected)
233
+ else
234
+ path.should_not be_nil
235
+ classes.should_not include(:selected)
236
+ end
237
+ classes.should_not include(:gap_before, :gap_after)
238
+ end
239
+ pages.should be_empty
240
+ end
241
+ end
242
+ end
243
+
244
+ describe "window generator for a collection with only one page" do
245
+ it "should not generate any links" do
246
+ @pages.stub!(:count).and_return(1)
247
+ page = @pages.first
248
+ page.paginator.set_path(&@path)
249
+ @path.should_not_receive(:call)
250
+ page.paginator.window(:inner => 2) do |pg, path, classes|
251
+ fail "expected block not to be called"
252
+ "<a>some link</a>"
253
+ end.should be_blank
254
+ end
255
+ end
256
+ 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.4
4
+ version: 0.0.5
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-22 00:00:00 -07:00
12
+ date: 2009-06-26 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency