simplificator-ruby-kiva 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 pascalbetz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,59 @@
1
+ = simplificator-ruby-kiva
2
+
3
+ == What
4
+ This is a ruby wrapper for the kiva.org api.
5
+ See build.kiva.org for details.
6
+
7
+ == Howtow
8
+
9
+ include Kiva
10
+
11
+ Api.app_id = 'your api id here, not required but recommended, see build.kiva.org'
12
+ ### Loans
13
+ ## finding Loans by ID
14
+
15
+ #puts Loan.find(:id => 180009).terms
16
+ #puts Loan.find(:id => '180009, 180008').size
17
+ #puts Loan.find(:id => [180009, '180008']).size
18
+
19
+ ## finding Loans by lender_id
20
+ #puts Loan.find(:lender_id => 'matt', :page => 1).inspect
21
+
22
+ ## finding Loans
23
+ #puts Loan.find(:query => 'Chinchilla', :page => 1).inspect
24
+ #puts Loan.find(:partner_id => [150, 156, 188, 190, 120], :query => 'Vegetable', :status => :in_repayment, :page => 2).inspect
25
+
26
+ ### Partners
27
+
28
+ #puts Partner.find().first.inspect
29
+ #puts Partner.find(:id => 1).inspect
30
+
31
+
32
+ ### Lending Actions
33
+ #LendingAction.recent.first
34
+
35
+
36
+ ### Journal Entries
37
+ #puts JournalEntry.find(:loan_id => 180009, :include_bulk => true, :page => 10).inspect
38
+ #puts Loan.find(:id => 180009).journal_entries(:include_bulk => true).first.comments
39
+ #puts Comment.find(:journal_entry_id => 28342).inspect
40
+
41
+
42
+ ### Team
43
+ #puts Team.find(:id => 2).team_since
44
+ #puts Team.find(:shortname => 'buildkiva')
45
+ #puts Team.find(:id => 6341).lenders(:page => 1).inspect
46
+
47
+ == Note on Patches/Pull Requests
48
+
49
+ * Fork the project.
50
+ * Make your feature addition or bug fix.
51
+ * Add tests for it. This is important so I don't break it in a
52
+ future version unintentionally.
53
+ * Commit, do not mess with rakefile, version, or history.
54
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
55
+ * Send me a pull request. Bonus points for topic branches.
56
+
57
+ == Copyright
58
+
59
+ Copyright (c) 2010 pascalbetz. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "simplificator-ruby-kiva"
8
+ gem.summary = %Q{a ruby wrapper for the kiva.org api}
9
+ gem.description = %Q{a ruby wrapper for the kiva.org api}
10
+ gem.email = "info@simplificator.com"
11
+ gem.homepage = "http://github.com/simplificator/simplificator-ruby-kiva"
12
+ gem.authors = ["simplificator"]
13
+ gem.add_development_dependency "shoulda", ">= 0"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/test_*.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "simplificator-ruby-kiva #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,55 @@
1
+ module Kiva
2
+ module Api
3
+ def self.app_id=(value)
4
+ @@app_id = value
5
+ end
6
+ def self.app_id
7
+ @@app_id
8
+ end
9
+
10
+
11
+ module ClassMethods
12
+ private
13
+ def json_to_paged_array(data, data_key, many)
14
+ if many
15
+ if data[data_key]
16
+ PagedArray.new(data[data_key].map { |item| self.new(item)}, data['paging'] || {})
17
+ else
18
+ PagedArray.new([], {})
19
+ end
20
+ else
21
+ if data[data_key]
22
+ self.new(data[data_key].first)
23
+ else
24
+ nil
25
+ end
26
+ end
27
+ end
28
+
29
+ def base_options(options = {})
30
+ if Api.app_id
31
+ {:app_id => Api.app_id}.merge(pagination_options(options))
32
+ else
33
+ pagination_options(options)
34
+ end
35
+ end
36
+
37
+ def pagination_options(options = {})
38
+ { :page => options[:page] || 1}
39
+ end
40
+
41
+ def sanitize_id_parameter(id)
42
+ Array(id).join(',').gsub(/\s/, '')
43
+ end
44
+ end
45
+
46
+
47
+ def self.included(receiver)
48
+ receiver.instance_eval do
49
+ extend ClassMethods
50
+ include HTTParty
51
+ base_uri 'http://api.kivaws.org/v1'
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,21 @@
1
+ module Kiva
2
+ class Borrower
3
+ include DynamicInitializer
4
+ attr_accessor :gender, :pictured, :first_name, :last_name
5
+
6
+ def male?
7
+ self.gender == 'M'
8
+ end
9
+ def female?
10
+ self.gender == 'F'
11
+ end
12
+
13
+ def pictured?
14
+ self.pictured
15
+ end
16
+
17
+ def to_s
18
+ "<#{self.first_name} #{self.last_name} #{self.gender} #{self.pictured?}>"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ module Kiva
2
+ class Comment
3
+ include DynamicInitializer
4
+ include Api
5
+ attr_accessor :id, :author, :whereabouts, :body, :date
6
+ typed_attr_accessor :date, Time, :parse
7
+
8
+ def self.find(params)
9
+ if params[:journal_entry_id]
10
+ json_to_paged_array(get("/journal_entries/#{params[:journal_entry_id]}/comments.json", :query => base_options(params)), 'comments', true)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ module Kiva
2
+ class Country
3
+ include DynamicInitializer
4
+ attr_accessor :country_code, :name, :region, :iso_code
5
+
6
+ typed_attr_accessor :location, Kiva::Location
7
+ end
8
+ end
@@ -0,0 +1,30 @@
1
+ module Kiva
2
+ module DynamicInitializer
3
+ module ClassMethods
4
+ def typed_attr_accessor(name, klass, factory_method = :new, array = false)
5
+ define_method("#{name}=") do |value|
6
+ if array
7
+ typed = value.map() {|item| klass.send(factory_method, item)}
8
+ else
9
+ typed = klass.send(factory_method, value)
10
+ end
11
+ instance_variable_set("@#{name}", typed)
12
+ end
13
+ attr_reader name
14
+ end
15
+ end
16
+
17
+ module InstanceMethods
18
+ def initialize(options = {})
19
+ options.each do |key, value|
20
+ self.send("#{key}=", value)
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.included(receiver)
26
+ receiver.extend ClassMethods
27
+ receiver.send :include, InstanceMethods
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,20 @@
1
+ module Kiva
2
+ class Image
3
+ include DynamicInitializer
4
+ attr_accessor :template_id, :id
5
+
6
+ SIZES = %w(w80h80 w200h200 w325h250 w450h360 fullsize)
7
+
8
+ def to_s
9
+ "<Image #{self.template_id} / #{self.id}>"
10
+ end
11
+
12
+ # Build the URL for this Image.
13
+ # See SIZES for valid sizes, defaults to w80h80
14
+ def url(size = 'w80h80')
15
+ raise 'Must have a template id and an id' unless self.template_id && self.id
16
+ raise "Unknown size #{size}" unless SIZES.include?(size.to_s)
17
+ "http://www.kiva.org/img/#{size}/#{self.id}.jpg"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,32 @@
1
+ module Kiva
2
+ class JournalEntry
3
+ include DynamicInitializer
4
+ include Api
5
+ attr_accessor :id, :body, :subject, :author, :bulk, :comment_count, :recommendation_count
6
+
7
+ typed_attr_accessor :date, Time, :parse
8
+
9
+ def bulk?
10
+ self.bulk
11
+ end
12
+
13
+ def self.find(params)
14
+ if params[:loan_id]
15
+ json_to_paged_array(get("/loans/#{params[:loan_id]}/journal_entries.json",
16
+ :query => base_options.merge(find_options(params))), 'journal_entries', true)
17
+ end
18
+ end
19
+
20
+ def comments(params = {})
21
+ @comments ||= Comment.find(params.merge({:journal_entry_id => self.id}))
22
+ end
23
+
24
+ private
25
+
26
+ def self.find_options(options)
27
+ result = {}
28
+ result['include_bulk'] = options[:include_bulk] ? 1 : 0
29
+ result
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,66 @@
1
+ module Kiva
2
+ class Lender
3
+ include Api
4
+ include DynamicInitializer
5
+ attr_accessor :loan_count, :occupation, :country_code, :name, :loan_because,
6
+ :lender_id, :uid, :whereabouts, :invitee_count,
7
+ :occupational_info, :personal_url
8
+
9
+ typed_attr_accessor :member_since, Time, :parse
10
+ typed_attr_accessor :team_join_date, Time, :parse
11
+ typed_attr_accessor :image, Kiva::Image
12
+
13
+ def lender_page_url
14
+ "http://www.kiva.org/lender/#{self.lender_id}"
15
+ end
16
+
17
+ # find the loans for this lender
18
+ # loans are cached to avoid roundtrips, refetch the lender if you need to refresh loans
19
+ def loans(params = {})
20
+ @loans ||= Loan.find(params.merge({:lender_id => self.lender_id}))
21
+ end
22
+
23
+ def to_s()
24
+ "<Lender #{self.lender_id}>"
25
+ end
26
+
27
+ # Find Lenders
28
+ # __either__
29
+ # :id : A single ID or a comma Separated String of IDs or an Array of IDs
30
+ # __or__
31
+ # :loan_id : A single loan ID
32
+ # __or__
33
+ # :team_id : a single team ID
34
+ #
35
+ # If searching for a single ID, then this method can return nil (no item with this ID found).
36
+ # Otherwise this method will always return a PagedArray instance (which might be empty though)
37
+ # If querying for multiple items, then :page is supported
38
+ def self.find(params)
39
+ if params[:id] # find one or many by lender ID
40
+ data = get("/lenders/#{sanitize_id_parameter(params[:id])}.json", :query => base_options(params))
41
+ many = sanitize_id_parameter(params[:id]).include?(',')
42
+ elsif params[:loan_id] # find lenders for a loan
43
+ data = get("/loans/#{params[:loan_id]}/lenders.json", :query => base_options(params))
44
+ many = true
45
+ elsif params[:team_id]
46
+ data = get("/teams/#{params[:team_id]}/lenders.json", :query => base_options(params))
47
+ many = true
48
+ end
49
+ json_to_paged_array(data, 'lenders', many)
50
+ end
51
+ end
52
+ end
53
+
54
+ # {"loan_count"=>93,
55
+ # "occupation"=>"Entrepreneur",
56
+ # "country_code"=>"US",
57
+ # "name"=>"Matt",
58
+ # "loan_because"=>"I love the stories. ",
59
+ # "lender_id"=>"matt",
60
+ # "invitee_count"=>23,
61
+ # "occupational_info"=>"I co-founded a startup nonprofit (this one!) and I work with an amazing group of people dreaming up ways to alleviate poverty through personal lending. ",
62
+ # "personal_url"=>"www.socialedge.org/blogs/kiva-chronicles",
63
+ # "uid"=>"matt",
64
+ # "whereabouts"=>"San Francisco CA",
65
+ # "image"=>{"template_id"=>1, "id"=>12829},
66
+ # "member_since"=>"2006-01-01T09:01:01Z"}
@@ -0,0 +1,25 @@
1
+ module Kiva
2
+ class LendingAction
3
+ include Api
4
+ include DynamicInitializer
5
+
6
+ attr_accessor :id, :sector, :basket_amount
7
+
8
+ typed_attr_accessor :date, Time, :parse
9
+ typed_attr_accessor :lender, Kiva::Lender
10
+ typed_attr_accessor :loan, Kiva::Loan
11
+
12
+
13
+ def self.recent(options = {})
14
+ json_to_paged_array(get('/lending_actions/recent.json'), 'lending_actions', true)
15
+ end
16
+
17
+ end
18
+ end
19
+
20
+ #{"date"=>"2010-04-09T22:00:27Z",
21
+ #{}"id"=>33614166,
22
+ #{}"lender"=>{"country_code"=>"US", "name"=>"Eisakouo Partnership", "lender_id"=>"eisakouopartnership3932", "uid"=>"eisakouopartnership3932", "whereabouts"=>"Columbia PA", "image"=>{"template_id"=>1, "id"=>482823}},
23
+ #{}"loan"=>{"borrower_count"=>1, "status"=>"fundraising", "name"=>"Laoy Lok", "posted_date"=>"2010-04-07T06:40:02Z", "activity"=>"Cattle", "id"=>191433, "description"=>{"languages"=>["en"]}, "partner_id"=>109, "use"=>"To buy a cow for breeding", "loan_amount"=>300, "funded_amount"=>0, "image"=>{"template_id"=>1, "id"=>520519}, "location"=>{"country_code"=>"KH", "country"=>"Cambodia", "geo"=>{"type"=>"point", "level"=>"country", "pairs"=>"13 105"}, "town"=>"Rung Village"},
24
+ #{}"sector"=>"Agriculture",
25
+ #{}"basket_amount"=>0}}
@@ -0,0 +1,109 @@
1
+ module Kiva
2
+ class Loan
3
+ include Api
4
+ include DynamicInitializer
5
+
6
+ attr_accessor :name, :activity, :use, :borrower_count,
7
+ :status, :id, :partner_id, :description,
8
+ :loan_amount, :funded_amount, :paid_amount, :basket_amount, :sector,
9
+ :delinquent, :journal_totals, :terms, :funded_date
10
+
11
+ typed_attr_accessor :location, Kiva::Location
12
+ typed_attr_accessor :image, Kiva::Image
13
+ typed_attr_accessor :video, Kiva::Video
14
+ typed_attr_accessor :terms, Kiva::Terms
15
+ typed_attr_accessor :posted_date, Date, :parse
16
+ typed_attr_accessor :borrowers, Kiva::Borrower, :new, true
17
+
18
+ # description consists of available languages and texts in different languages
19
+ # texts are only available when doing a find by id
20
+ def description=(value)
21
+ @description = value
22
+ @description_languages = value['languages']
23
+ @description_texts = value['texts']
24
+ end
25
+
26
+ def description_languages
27
+ @description_languages
28
+ end
29
+
30
+ def description_texts
31
+ @description_texts
32
+ end
33
+
34
+ def description_text(language)
35
+ description_texts[language]
36
+ end
37
+
38
+ # Get the lenders for this loan
39
+ # Caches the lenders, reload loan if you need to refresh the lenders
40
+ def lenders(params = {})
41
+ @lenders ||= Lender.find(params.merge({:loan_id => self.id}))
42
+ end
43
+
44
+
45
+ def journal_entries(params = {})
46
+ @journal_entries ||= JournalEntry.find(params.merge({:loan_id => self.id}))
47
+ end
48
+
49
+
50
+ # Supported options:
51
+ # * :page: The requested page number
52
+ def self.recent(options = {})
53
+ data = get('/loans/newest.json', :query => base_options(options))
54
+ json_to_paged_array(data, 'loans', true)
55
+ end
56
+
57
+ # Search for Loans.
58
+ # Different searches are possible
59
+ # __either:__
60
+ # :id: An ID, a comma delimited String of IDs or an Array of IDs
61
+ # __or__
62
+ # :lender_id: A single ID of a lender
63
+ # __or__
64
+ # :team_id : a single ID of a team
65
+ # __or a combination of__
66
+ # * :partner_id: A (comma delimited) String of Partner IDs or an Array of partner IDs
67
+ # * :status: A String or symbol with the Loan status
68
+ # * :query: A String
69
+ #
70
+ # If searching for a single ID, then this method can return nil (no item with this ID found).
71
+ # Otherwise this method will always return a PagedArray instance (which might be empty though)
72
+ #
73
+ # When searching for multiple Loans, then the :page parameter is supported to specify the desired page.
74
+ def self.find(params)
75
+ if params[:id] # find one or many by ID
76
+ data = get("/loans/#{sanitize_id_parameter(params[:id])}.json", :query => base_options(params))
77
+ many = sanitize_id_parameter(params[:id]).include?(',')
78
+ elsif params[:lender_id] # find all loans for a lender
79
+ data = get("/lenders/#{params[:lender_id]}/loans.json", :query => base_options(params))
80
+ many = true
81
+ elsif params[:team_id] # find all loans for a team
82
+ data = get("/teams/#{params[:team_id]}/loans.json", :query => base_options(params))
83
+ many = true
84
+ else # search
85
+ data = get('/loans/search.json', :query => base_options(params).merge(find_options(params)))
86
+ many = true
87
+ end
88
+ json_to_paged_array(data, 'loans', many)
89
+ end
90
+
91
+ def to_s()
92
+ "<Loan '%s' (%s) in %s with status %s, from %s>" % [self.name, self.id, self.location, self.status, self.borrowers]
93
+ end
94
+
95
+ private
96
+
97
+ def self.find_options(options = {})
98
+ result = {}
99
+ if options[:partner_id]
100
+ result[:partner] = sanitize_id_parameter(options[:partner_id])
101
+ end
102
+ result[:status] = options[:status] if options[:status]
103
+ result[:q] = options[:query] if options[:query]
104
+ result
105
+ end
106
+
107
+
108
+ end
109
+ end
@@ -0,0 +1,67 @@
1
+ module Kiva
2
+ class Location
3
+ include DynamicInitializer
4
+ attr_accessor :country, :geo, :town, :type, :level
5
+ attr_accessor :country_code
6
+
7
+ def geo=(value)
8
+ self.type = value['type']
9
+ self.level = value['level']
10
+ self.pairs = value['pairs']
11
+ end
12
+
13
+ def pairs=(value)
14
+ @pairs = value.split(' ').map() {|item| item.to_f}
15
+ end
16
+
17
+ def pairs
18
+ @pairs.join(' ')
19
+ end
20
+
21
+
22
+ # if this is a point?, then returns lat else nil
23
+ def lat
24
+ @pairs[0] if self.point?
25
+ end
26
+
27
+ # if this is a point?, then returns lat else nil
28
+ def lng
29
+ @pairs[1] if self.point?
30
+ end
31
+
32
+ # true if level is 'town'
33
+ def town?
34
+ 'town' == self.level
35
+ end
36
+ # true if level is 'country'
37
+ def country?
38
+ 'country' == self.level
39
+ end
40
+ # true if level 'exact'
41
+ def exact?
42
+ 'exact' == self.level
43
+ end
44
+
45
+ # true if type 'point'
46
+ def point?
47
+ 'point' == self.type
48
+ end
49
+ # true if type 'line'
50
+ def line?
51
+ 'line' == self.type
52
+ end
53
+ # true if type 'box'
54
+ def box?
55
+ 'box' == self.type
56
+ end
57
+ # true if type 'polygon'
58
+ def polygon?
59
+ 'polygon' == self.type
60
+ end
61
+
62
+ def to_s
63
+ "<Location '%s' '%s' %f/%f>" % [self.country, self.town, self.lat || 0, self.lng || 0]
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,66 @@
1
+ module Kiva
2
+ # Adds pagination information to array
3
+ # Pagination attributes/methods have the same names as the wonderful will_paginate
4
+ class PagedArray < Array
5
+ attr_reader :current_page, :per_page, :total_entries, :total_pages
6
+
7
+ def initialize(data, options = {})
8
+ super data
9
+ @current_page = options['page'] || 1
10
+ @per_page = options['page_size'] || data.size
11
+ @total_pages = options['pages'] || 1
12
+ @total_entries = options['total'] || data.size
13
+ end
14
+
15
+ def offset
16
+ (self.current_page - 1) * self.per_page
17
+ end
18
+
19
+ def next_page?()
20
+ self.current_page < self.total_pages
21
+ end
22
+
23
+ def next_page
24
+ self.current_page + 1 if next_page?
25
+ end
26
+
27
+ def previous_page
28
+ self.current_page - 1 if previous_page?
29
+ end
30
+
31
+ def previous_page?()
32
+ self.current_page > 1
33
+ end
34
+
35
+ def from_entry
36
+ self.offset + 1 unless self.out_of_bounds?
37
+ end
38
+
39
+ def out_of_bounds?
40
+ self.current_page < 1 || self.current_page > self.total_pages
41
+ end
42
+
43
+ def to_entry
44
+ [self.offset + self.per_page, self.total_entries].min unless self.out_of_bounds?
45
+ end
46
+
47
+ def inspect
48
+ "#{pagination_info}\n#{super}"
49
+ end
50
+
51
+ def to_s
52
+ self.inspect
53
+ end
54
+
55
+ private
56
+
57
+
58
+ def pagination_info
59
+ if out_of_bounds?
60
+ "<Page is out of bounds>"
61
+ else
62
+ "<Page #{self.current_page} of #{self.total_pages} (entries #{self.from_entry} - #{self.to_entry} of #{self.total_entries}). Previous? #{self.previous_page?}, Next? #{self.next_page?}>"
63
+ end
64
+ end
65
+ end
66
+ end