pageboy 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,39 @@
1
+ require 'pageboy/config'
2
+ require 'pageboy/collator'
3
+ require 'pageboy/page'
4
+ require 'pageboy/page_turner/array'
5
+ require 'pageboy/page_turner/link_header'
6
+
7
+ module Pageboy
8
+ def self.paginate(target, opts={})
9
+ Pageboy::Config.turners.each_pair do |page_turner_class, matchers|
10
+ matchers.each do |matcher|
11
+ return page_turner_class.new(target, opts) if matcher.call(target)
12
+ end
13
+ end
14
+ raise "could not find suitable PageTurner for #{target}"
15
+ end
16
+
17
+ def self.each(target, opts={}, &block)
18
+ page_turner = paginate(target, opts)
19
+ Pageboy::Collator(page_turner).each(&block)
20
+ end
21
+
22
+ def self.map(target, opts={}, &block)
23
+ page_turner = paginate(target, opts)
24
+ Pageboy::Collator(page_turner).map(&block)
25
+ end
26
+
27
+ def self.register_page_turner(klass, &block)
28
+ Pageboy::Config.turners[klass] ||= []
29
+ Pageboy::Config.turners[klass] << block
30
+ end
31
+ end
32
+
33
+ Pageboy.register_page_turner(Pageboy::PageTurner::Array) do |object|
34
+ object.respond_to?(:[]) && object.respond_to?(:size)
35
+ end
36
+
37
+ Pageboy.register_page_turner(Pageboy::PageTurner::LinkHeader) do |object|
38
+ object.is_a?(String) && object.include?('/api/')
39
+ end
@@ -0,0 +1,19 @@
1
+ module Pageboy
2
+ class Collator
3
+ include Enumerable
4
+
5
+ def initialize(page_turner)
6
+ @page_turner = page_turner
7
+ end
8
+
9
+ def each(&block)
10
+ page = @page_turner.first
11
+ while page
12
+ page.items.each(&block)
13
+ page = page.next
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ # Pageboy::Collator.new(page_turner).each { |item| }
@@ -0,0 +1,18 @@
1
+ require 'active_support/configurable'
2
+
3
+ module Pageboy
4
+ module Config
5
+ include ActiveSupport::Configurable
6
+ config_accessor :per_page
7
+ config_accessor :turners
8
+
9
+ def configure
10
+ yield self
11
+ end
12
+ end
13
+ end
14
+
15
+ Pageboy::Config.configure do |config|
16
+ config.per_page = 25
17
+ config.turners = {}
18
+ end
@@ -0,0 +1,28 @@
1
+ module Pageboy
2
+ class Page
3
+ attr_reader :items
4
+
5
+ def initialize(turner, items, prev_page_id, next_page_id)
6
+ @turner = turner
7
+ @items = items
8
+ @prev_page_id = prev_page_id
9
+ @next_page_id = next_page_id
10
+ end
11
+
12
+ def prev
13
+ @turner.page(@prev_page_id) if @prev_page_id
14
+ end
15
+
16
+ def next
17
+ @turner.page(@next_page_id) if @next_page_id
18
+ end
19
+
20
+ def to_a
21
+ @items
22
+ end
23
+
24
+ def each(&block)
25
+ @items.each(&block)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ require 'pageboy/page_turner/base'
2
+
3
+ module Pageboy
4
+ module PageTurner
5
+ class Array < Base
6
+
7
+ def page(page_id, opts={})
8
+ with_merged_options(opts) do |o|
9
+ per_page = Integer(o[:per_page])
10
+ i = Integer(page_id)
11
+ max_page = (resource.size.to_f / per_page).ceil
12
+ prev_page_id = i - 1 < 1 ? nil : i - 1
13
+ next_page_id = i + 1 > max_page ? nil : i + 1
14
+
15
+ first_item_index = (i-1) * per_page
16
+ last_item_index = i * per_page - 1
17
+ slice = resource[first_item_index..last_item_index]
18
+ Page.new(self, slice, prev_page_id, next_page_id)
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,36 @@
1
+ require 'pageboy/page'
2
+ require 'pageboy/config'
3
+
4
+ module Pageboy
5
+ module PageTurner
6
+ class Base
7
+ attr_accessor :resource
8
+
9
+ def initialize(resource, opts={})
10
+ @resource = resource
11
+ @opts = opts
12
+ end
13
+
14
+ # Can be overridden in subclasses
15
+ def first_page_id
16
+ # Some PageTurners use integers, others use GUIDs etc.,
17
+ # but page numbers are most common, so default to 1.
18
+ 1
19
+ end
20
+
21
+ # Should return a Pageboy::Page object
22
+ def page(page_id, opts={})
23
+ raise NotImplementedError,
24
+ "'page' should be implemented in subclasses"
25
+ end
26
+
27
+ def first(opts={})
28
+ page(first_page_id, opts)
29
+ end
30
+
31
+ def with_merged_options(opts={})
32
+ yield Pageboy::Config.config.merge(@opts).merge(opts)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,39 @@
1
+ require "link_header"
2
+ require "pageboy/page_turner/base"
3
+
4
+ module Pageboy
5
+ module PageTurner
6
+ class LinkHeader < Base
7
+
8
+ def initialize(url, opts={})
9
+ super
10
+ @url = url
11
+ @connection = opts.delete(:connection) or
12
+ fail("LinkHeader page turner requires :connection")
13
+ @first_page_id = url.dup
14
+ end
15
+
16
+ def first_page_id
17
+ @first_page_id
18
+ end
19
+
20
+ def find_link(header, rel)
21
+ link = ::LinkHeader.parse(header).links.find{ |link| link['rel'] == rel }
22
+ link.to_a.first if link
23
+ end
24
+
25
+ def page(url, opts={})
26
+ with_merged_options(opts) do |o|
27
+ response = @connection.get(url)
28
+ header = response.headers[:link]
29
+ # require 'debugger';debugger
30
+ next_page = find_link(header, 'next')
31
+ slice = response.body
32
+
33
+ Page.new(self, slice, nil, next_page)
34
+ end
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,3 @@
1
+ module Pageboy
2
+ VERSION = '0.1'
3
+ end
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pageboy/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.authors = ["Duane Johnson"]
8
+ gem.email = ["duane@instructure.com"]
9
+ gem.description = %q{Paginator}
10
+ gem.summary = %q{Paginator for collections of unknown size}
11
+
12
+ gem.files = %w[pageboy.gemspec]
13
+ gem.files += Dir.glob("lib/**/*.rb")
14
+ gem.files += Dir.glob("spec/**/*")
15
+ gem.test_files = Dir.glob("spec/**/*")
16
+ gem.name = "pageboy"
17
+ gem.require_paths = ["lib"]
18
+ gem.version = Pageboy::VERSION
19
+
20
+ gem.add_development_dependency "bundler", ">= 1.0.0"
21
+ gem.add_development_dependency "rspec", "~> 2.6"
22
+ gem.add_development_dependency "webmock"
23
+ gem.add_development_dependency "debugger"
24
+
25
+ # Used for configuration on modules and classes
26
+ gem.add_dependency "activesupport", ">= 3.0.0"
27
+
28
+ # HTTP library
29
+ gem.add_dependency "faraday", "~> 0.8.8"
30
+
31
+ # Parses Link headers formatted according to RFC 5988 draft spec
32
+ gem.add_dependency "link_header", ">= 0.0.7"
33
+ end
@@ -0,0 +1,21 @@
1
+ require 'pageboy/page_turner/array'
2
+ require 'pageboy/collator'
3
+
4
+ describe Pageboy::Collator do
5
+ let(:array) { %w(some words to paginate) }
6
+ let(:page_turner) { Pageboy::PageTurner::Array.new(array, :per_page => 3) }
7
+ let(:collator) { Pageboy::Collator.new(page_turner) }
8
+
9
+ it "collates with each" do
10
+ items = []
11
+ collator.each do |item|
12
+ items << item
13
+ end
14
+ items.should == %w(some words to paginate)
15
+ end
16
+
17
+ it "collates with map" do
18
+ collator.map { |item| item + '.' }.
19
+ should == %w(some. words. to. paginate.)
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ require 'webmock/rspec'
2
+ require 'faraday'
3
+ require 'pageboy/page_turner/link_header'
4
+
5
+ describe Pageboy do
6
+ context "for an API with link headers" do
7
+ let(:connection) { Faraday.new }
8
+ let(:url) { 'http://canvas.dev/api/v1/thing' }
9
+ let(:link_header) do
10
+ Pageboy::PageTurner::LinkHeader.new(url,
11
+ :connection => connection, :per_page => 3)
12
+ end
13
+
14
+ it "paginates a Link header based API" do
15
+ next_link = %{<http://canvas.dev/api/v1/thing?page=2>; rel="next"}
16
+
17
+ # First page
18
+ stub_request(:get, "http://canvas.dev/api/v1/thing").
19
+ to_return(
20
+ :status => 200,
21
+ :body => [{"name" => "test1"}],
22
+ :headers => {"Link" => next_link})
23
+ page = link_header.first
24
+ page.items.should == [{"name" => "test1"}]
25
+
26
+ # Second page
27
+ stub_request(:get, "http://canvas.dev/api/v1/thing?page=2").
28
+ to_return(
29
+ :status => 200,
30
+ :body => [{"name" => "test2"}],
31
+ :headers => {})
32
+ page = page.next
33
+ page.items.should == [{"name" => "test2"}]
34
+
35
+ # No more pages
36
+ page = page.next
37
+ page.should be_nil
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,27 @@
1
+ require_relative '../lib/pageboy'
2
+
3
+ describe Pageboy do
4
+ context "for an array" do
5
+ let(:array) { %w(this is an array with words in it) }
6
+ let(:turner) { Pageboy::PageTurner::Array.new(array, :per_page => 3) }
7
+
8
+ it "paginates an array" do
9
+ turner.first.prev.should be_nil
10
+ turner.first.to_a.should == %w(this is an)
11
+ turner.first.next.to_a.should == %w(array with words)
12
+ turner.first.next.next.to_a.should == %w(in it)
13
+ turner.first.next.next.next.should be_nil
14
+ end
15
+
16
+ it "can turn to any page" do
17
+ turner.page(2).to_a.should == %w(array with words)
18
+ end
19
+ end
20
+
21
+ context "matching for page turners" do
22
+ it "chooses PageTurner::Array for arrays" do
23
+ turner = Pageboy.paginate([])
24
+ turner.should be_a(Pageboy::PageTurner::Array)
25
+ end
26
+ end
27
+ end
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pageboy
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Duane Johnson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-31 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '2.6'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '2.6'
46
+ - !ruby/object:Gem::Dependency
47
+ name: webmock
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: debugger
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: activesupport
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: 3.0.0
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: 3.0.0
94
+ - !ruby/object:Gem::Dependency
95
+ name: faraday
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: 0.8.8
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: 0.8.8
110
+ - !ruby/object:Gem::Dependency
111
+ name: link_header
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: 0.0.7
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: 0.0.7
126
+ description: Paginator
127
+ email:
128
+ - duane@instructure.com
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - pageboy.gemspec
134
+ - lib/pageboy/collator.rb
135
+ - lib/pageboy/config.rb
136
+ - lib/pageboy/page.rb
137
+ - lib/pageboy/page_turner/array.rb
138
+ - lib/pageboy/page_turner/base.rb
139
+ - lib/pageboy/page_turner/link_header.rb
140
+ - lib/pageboy/version.rb
141
+ - lib/pageboy.rb
142
+ - spec/collator_spec.rb
143
+ - spec/link_header_spec.rb
144
+ - spec/pageboy_spec.rb
145
+ homepage:
146
+ licenses: []
147
+ post_install_message:
148
+ rdoc_options: []
149
+ require_paths:
150
+ - lib
151
+ required_ruby_version: !ruby/object:Gem::Requirement
152
+ none: false
153
+ requirements:
154
+ - - ! '>='
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ required_rubygems_version: !ruby/object:Gem::Requirement
158
+ none: false
159
+ requirements:
160
+ - - ! '>='
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ requirements: []
164
+ rubyforge_project:
165
+ rubygems_version: 1.8.23
166
+ signing_key:
167
+ specification_version: 3
168
+ summary: Paginator for collections of unknown size
169
+ test_files:
170
+ - spec/collator_spec.rb
171
+ - spec/link_header_spec.rb
172
+ - spec/pageboy_spec.rb
173
+ has_rdoc: