simplificator-ruby-kiva 0.1.0 → 0.2.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.
data/README.rdoc CHANGED
@@ -4,7 +4,23 @@
4
4
  This is a ruby wrapper for the kiva.org api.
5
5
  See build.kiva.org for details.
6
6
 
7
- == Howtow
7
+ == Status
8
+
9
+ Still work in progress, some tests are missing, documentation is quite weak.
10
+ Most API calls from Kiva are reflected.
11
+ http://build.kiva.org/api offers a list of API methods, not implemented: Methods, Release, Templates
12
+
13
+ == Attention
14
+
15
+ Whenever the unknown elements are encountered in the JSON documents, then an exception is raised.
16
+ Let me know if this happens and i will add the missing attributes. Or set Kiva::Api.friendly = true AND let me know.
17
+
18
+
19
+ == History
20
+ 0.2.0 Updated API to include more functionality, added some tests
21
+ 0.1.0 Initial Release
22
+
23
+ == Howto
8
24
 
9
25
  include Kiva
10
26
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
data/lib/ruby-kiva/api.rb CHANGED
@@ -1,15 +1,49 @@
1
1
  module Kiva
2
2
  module Api
3
+ # set the API id
3
4
  def self.app_id=(value)
4
5
  @@app_id = value
5
6
  end
7
+ # retrieve the API id
6
8
  def self.app_id
7
- @@app_id
9
+ @@app_id if defined? @@app_id
10
+ end
11
+
12
+ def self.friendly=(value)
13
+ @@friendly = value
14
+ end
15
+ def self.friendly
16
+ @@friendly if defined? @@friendly
8
17
  end
9
18
 
10
19
 
11
20
  module ClassMethods
12
21
  private
22
+ # map options to a new hash
23
+ # options
24
+ # mappings, an Array with up to 4 elements: [:source_key, :destination_key, sanitize (true, false), valid items]
25
+ # if mapping size is 1, then items are just copied over to new hash using same key
26
+ # if mapping size is 2, then items are copied over using :destination_key as the new key
27
+ # if mapping size is 3, then value is sanitized if mappind[2]
28
+ # if mapping size is 4, then value is verified to be included in mapping[3]
29
+ #
30
+ # if the value is nil in the options hash, then it is not copied
31
+ def map_options(options, mappings)
32
+ result = {}
33
+ mappings.each do |mapping|
34
+ if options.has_key?(mapping[0])
35
+ value = options[mapping[0]]
36
+ raise "Invalid value for #{mapping[0]}: #{value}" if mapping[3] && !mapping[3].include?(value)
37
+ result[mapping[1] || mapping[0]] = mapping[2] ? sanitize_id_parameter(value) : value
38
+ end
39
+ end
40
+ result
41
+ end
42
+
43
+ # converts JSON to a PagedArray or a single Instance of a ruby-kiva Api class
44
+ # data: the JSON Date
45
+ # data_key, the key under which the date for a item is stored
46
+ # many if true: create an array / if false: create a single instance
13
47
  def json_to_paged_array(data, data_key, many)
14
48
  if many
15
49
  if data[data_key]
@@ -26,20 +60,23 @@ module Kiva
26
60
  end
27
61
  end
28
62
 
63
+ # basic options
64
+ # includes App ID if set and adds pagination options if required
29
65
  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
66
+ result = pagination_options(options)
67
+ result[:app_id] = Api.app_id if Api.app_id
68
+ result
35
69
  end
36
70
 
71
+ # builds a new hash with pagination options
37
72
  def pagination_options(options = {})
38
- { :page => options[:page] || 1}
73
+ options[:page] ? {:page => options[:page]} : {}
39
74
  end
40
75
 
76
+ # sanitizes ID parameters for use in path of service
77
+ # pass a String or an Array with IDs, returns a comman separated String of IDs, whitespaces removed
41
78
  def sanitize_id_parameter(id)
42
- Array(id).join(',').gsub(/\s/, '')
79
+ Array(id).flatten.join(',').gsub(/\s/, '')
43
80
  end
44
81
  end
45
82
 
@@ -2,22 +2,26 @@ module Kiva
2
2
  module DynamicInitializer
3
3
  module ClassMethods
4
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)
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)
12
10
  end
13
- attr_reader name
11
+ instance_variable_set("@#{name}", typed)
12
+ end
13
+ attr_reader name
14
14
  end
15
15
  end
16
16
 
17
17
  module InstanceMethods
18
18
  def initialize(options = {})
19
19
  options.each do |key, value|
20
- self.send("#{key}=", value)
20
+ if respond_to?("#{key}=")
21
+ self.send("#{key}=", value)
22
+ elsif !Api.friendly
23
+ raise "Attribute #{key} is not yet supported. Set Api.friendly = true"
24
+ end
21
25
  end
22
26
  end
23
27
  end
@@ -5,6 +5,8 @@ module Kiva
5
5
  attr_accessor :id, :body, :subject, :author, :bulk, :comment_count, :recommendation_count
6
6
 
7
7
  typed_attr_accessor :date, Time, :parse
8
+ typed_attr_accessor :image, Kiva::Image
9
+ typed_attr_accessor :video, Kiva::Video
8
10
 
9
11
  def bulk?
10
12
  self.bulk
@@ -13,7 +15,10 @@ module Kiva
13
15
  def self.find(params)
14
16
  if params[:loan_id]
15
17
  json_to_paged_array(get("/loans/#{params[:loan_id]}/journal_entries.json",
16
- :query => base_options.merge(find_options(params))), 'journal_entries', true)
18
+ :query => sanitize_options(params)), 'journal_entries', true)
19
+ else
20
+ json_to_paged_array(get("/journal_entries/search.json",
21
+ :query => sanitize_options(params)), 'journal_entries', true)
17
22
  end
18
23
  end
19
24
 
@@ -23,10 +28,17 @@ module Kiva
23
28
 
24
29
  private
25
30
 
26
- def self.find_options(options)
27
- result = {}
31
+ OPTION_MAPPINGS = [
32
+ [:media, :media, false, ['any', 'video', 'image']],
33
+ [:sort_by, :sort_by, false, ['newest', 'oldest', 'recommendation_count', 'comment_count']],
34
+ [:partner_id, :partner, true],
35
+ [:query, :q]
36
+ ]
37
+ def self.sanitize_options(options = {})
38
+ result = base_options(options).merge(map_options(options, OPTION_MAPPINGS))
28
39
  result['include_bulk'] = options[:include_bulk] ? 1 : 0
29
40
  result
30
41
  end
42
+
31
43
  end
32
44
  end
@@ -24,30 +24,48 @@ module Kiva
24
24
  "<Lender #{self.lender_id}>"
25
25
  end
26
26
 
27
+ def self.newest(params = {})
28
+ json_to_paged_array(get('/lenders/newest.json', :query => sanitize_options(params)), 'lenders', true)
29
+ end
30
+
27
31
  # Find Lenders
28
32
  # __either__
29
33
  # :id : A single ID or a comma Separated String of IDs or an Array of IDs
30
34
  # __or__
31
35
  # :loan_id : A single loan ID
32
36
  # __or__
33
- # :team_id : a single team ID
37
+ # :team_id : a single team ID, supports :sort_by
34
38
  #
35
39
  # If searching for a single ID, then this method can return nil (no item with this ID found).
36
40
  # Otherwise this method will always return a PagedArray instance (which might be empty though)
37
41
  # If querying for multiple items, then :page is supported
38
- def self.find(params)
42
+ def self.find(params = {})
39
43
  if params[:id] # find one or many by lender ID
40
- data = get("/lenders/#{sanitize_id_parameter(params[:id])}.json", :query => base_options(params))
44
+ data = get("/lenders/#{sanitize_id_parameter(params[:id])}.json", :query => sanitize_options(params))
41
45
  many = sanitize_id_parameter(params[:id]).include?(',')
42
46
  elsif params[:loan_id] # find lenders for a loan
43
- data = get("/loans/#{params[:loan_id]}/lenders.json", :query => base_options(params))
47
+ data = get("/loans/#{params[:loan_id]}/lenders.json", :query => sanitize_options(params))
44
48
  many = true
45
49
  elsif params[:team_id]
46
- data = get("/teams/#{params[:team_id]}/lenders.json", :query => base_options(params))
50
+ data = get("/teams/#{params[:team_id]}/lenders.json", :query => sanitize_options(params))
51
+ many = true
52
+ else
53
+ data = get("/lenders/search.json", :query => sanitize_options(params))
47
54
  many = true
48
55
  end
49
56
  json_to_paged_array(data, 'lenders', many)
50
57
  end
58
+
59
+ private
60
+ OPTION_MAPPINGS = [
61
+ [:sort_by, :sort_by, false, ['oldest', 'newest']],
62
+ [:occupation],
63
+ [:query, :q]
64
+ ]
65
+ def self.sanitize_options(options = {})
66
+ base_options(options).merge(map_options(options, OPTION_MAPPINGS))
67
+ end
68
+
51
69
  end
52
70
  end
53
71
 
@@ -10,9 +10,13 @@ module Kiva
10
10
  typed_attr_accessor :loan, Kiva::Loan
11
11
 
12
12
 
13
- def self.recent(options = {})
13
+ def self.newest(options = {})
14
14
  json_to_paged_array(get('/lending_actions/recent.json'), 'lending_actions', true)
15
15
  end
16
+ # renamed from recent to 'newest' to make it consistent with LendingAction and Lender
17
+ class <<self
18
+ alias_method :recent, :newest
19
+ end
16
20
 
17
21
  end
18
22
  end
@@ -13,7 +13,9 @@ module Kiva
13
13
  typed_attr_accessor :video, Kiva::Video
14
14
  typed_attr_accessor :terms, Kiva::Terms
15
15
  typed_attr_accessor :posted_date, Date, :parse
16
+ typed_attr_accessor :paid_date, Date, :parse
16
17
  typed_attr_accessor :borrowers, Kiva::Borrower, :new, true
18
+ typed_attr_accessor :payments, Kiva::Payment, :new, true
17
19
 
18
20
  # description consists of available languages and texts in different languages
19
21
  # texts are only available when doing a find by id
@@ -46,10 +48,14 @@ module Kiva
46
48
  @journal_entries ||= JournalEntry.find(params.merge({:loan_id => self.id}))
47
49
  end
48
50
 
51
+ def loan_updates
52
+ @loan_updates ||= find_loan_updates()
53
+ end
54
+
49
55
 
50
56
  # Supported options:
51
57
  # * :page: The requested page number
52
- def self.recent(options = {})
58
+ def self.newest(options = {})
53
59
  data = get('/loans/newest.json', :query => base_options(options))
54
60
  json_to_paged_array(data, 'loans', true)
55
61
  end
@@ -73,35 +79,52 @@ module Kiva
73
79
  # When searching for multiple Loans, then the :page parameter is supported to specify the desired page.
74
80
  def self.find(params)
75
81
  if params[:id] # find one or many by ID
76
- data = get("/loans/#{sanitize_id_parameter(params[:id])}.json", :query => base_options(params))
82
+ data = get("/loans/#{sanitize_id_parameter(params[:id])}.json", :query => sanitize_options(params))
77
83
  many = sanitize_id_parameter(params[:id]).include?(',')
78
84
  elsif params[:lender_id] # find all loans for a lender
79
- data = get("/lenders/#{params[:lender_id]}/loans.json", :query => base_options(params))
85
+ data = get("/lenders/#{params[:lender_id]}/loans.json", :query => sanitize_options(params))
80
86
  many = true
81
87
  elsif params[:team_id] # find all loans for a team
82
- data = get("/teams/#{params[:team_id]}/loans.json", :query => base_options(params))
88
+ data = get("/teams/#{params[:team_id]}/loans.json", :query => sanitize_options(params))
83
89
  many = true
84
90
  else # search
85
- data = get('/loans/search.json', :query => base_options(params).merge(find_options(params)))
91
+ puts "Searching with #{base_options(params).merge(find_options(params)).inspect}"
92
+ data = get('/loans/search.json', :query => sanitize_options(params))
86
93
  many = true
87
94
  end
88
95
  json_to_paged_array(data, 'loans', many)
89
96
  end
90
97
 
91
98
  def to_s()
92
- "<Loan '%s' (%s) in %s with status %s, from %s>" % [self.name, self.id, self.location, self.status, self.borrowers]
99
+ "<Loan '%s' (%s) in %s with status %s, from %s>" % [self.name, self.id, self.location, self.id, self.borrowers]
93
100
  end
94
101
 
95
102
  private
96
103
 
97
- def self.find_options(options = {})
98
- result = {}
99
- if options[:partner_id]
100
- result[:partner] = sanitize_id_parameter(options[:partner_id])
104
+ def find_loan_updates
105
+ data = Loan.get("/loans/#{self.id}/updates.json")
106
+ data['loan_updates'].map do |entry|
107
+ type = entry.delete('update_type')
108
+ case type
109
+ when 'journal_entry' then JournalEntry.new(entry['journal_entry'])
110
+ when 'payment' then Payment.new(entry['payment'])
111
+ else raise "unknown type #{type}"
112
+ end
101
113
  end
102
- result[:status] = options[:status] if options[:status]
103
- result[:q] = options[:query] if options[:query]
104
- result
114
+ end
115
+
116
+ OPTION_MAPPINGS = [
117
+ [:partner_id, :partner, true],
118
+ [:sort_by, :sort_by, false, ['oldest', 'newest']],
119
+ [:status, :status, false, ['fundraising', 'funded', 'in_repayment', 'paid', 'defaulted']],
120
+ [:query, :q],
121
+ [:gender, :gender, false, ['male', 'female']],
122
+ [:region, :region, false, ['na', 'ca', 'sa', 'af', 'as', 'me', 'ee']],
123
+ [:sector],
124
+ [:country_code],
125
+ ]
126
+ def self.sanitize_options(options = {})
127
+ base_options(options).merge(map_options(options, OPTION_MAPPINGS))
105
128
  end
106
129
 
107
130
 
@@ -9,8 +9,9 @@ module Kiva
9
9
 
10
10
  # Find a partner
11
11
  # either by :id or all of them
12
- # Since kiva does not offer pagination or search,
12
+ # Since kiva does not offer search,
13
13
  # finding by id is implemented in memory/ruby.
14
+ # Pagination is not supported, page size is 200 and currently there are 143 partners
14
15
  # Items are cached but can be reloaded by passing :reload => true (caching is suggested by kiva...)
15
16
  def self.find(params = {})
16
17
  if params[:id]
@@ -1,8 +1,10 @@
1
1
  module Kiva
2
2
  class Payment
3
3
  include DynamicInitializer
4
- attr_accessor :amount
4
+ attr_accessor :amount, :local_amount, :rounded_local_amount, :comment, :payment_id
5
5
  typed_attr_accessor :due_date, Time, :parse
6
+ typed_attr_accessor :settlement_date, Time, :parse
7
+ typed_attr_accessor :processed_date, Time, :parse
6
8
 
7
9
  def to_s
8
10
  "<Payment due at #{self.due_date} : #{self.amount}>"
@@ -1,5 +1,6 @@
1
1
  module Kiva
2
2
  class Team
3
+
3
4
  include DynamicInitializer
4
5
  include Api
5
6
 
@@ -19,10 +20,31 @@ module Kiva
19
20
  # find a team by :id or :shortname
20
21
  def self.find(params)
21
22
  if params[:id]
22
- json_to_paged_array(get("/teams/#{params[:id]}.json", :query => base_options(params)), 'teams', false)
23
+ data = get("/teams/#{sanitize_id_parameter(params[:id])}.json", :query => sanitize_options(params))
24
+ many = sanitize_id_parameter(params[:id]).include?(',')
23
25
  elsif params[:shortname]
24
- json_to_paged_array(get("/teams/using_shortname/#{params[:shortname]}.json", :query => base_options(params)), 'teams', false)
26
+ data = get("/teams/using_shortname/#{sanitize_id_parameter(params[:shortname])}.json", :query => sanitize_options(params))
27
+ many = sanitize_id_parameter(params[:shortnames]).include?(',')
28
+ else
29
+ data = get("/teams/search.json", :query => sanitize_options(params))
30
+ many = true
25
31
  end
32
+ json_to_paged_array(data, 'teams', many)
26
33
  end
34
+
35
+ private
36
+
37
+ OPTION_MAPPINGS = [
38
+ [:sort_by, :sort_by, false, ['oldest', 'newest']],
39
+ [:occupation],
40
+ [:query, :q],
41
+ [:country_code],
42
+ [:membership_type, :membership_type, false, ['open', 'closed']],
43
+ [:category, :category, false, ['Alumni Groups', 'Businesses', 'Businesses - Internal Groups', 'Clubs', 'Colleges/Universities', 'Common Interest', 'Events', 'Families', 'Field Partner Fans', 'Friends', 'Local Area', 'Memorials', 'Religious Congregations', 'Schools', 'Sports Groups', 'Youth Groups', 'Other']],
44
+ ]
45
+ def self.sanitize_options(options = {})
46
+ base_options(options).merge(map_options(options, OPTION_MAPPINGS))
47
+ end
48
+
27
49
  end
28
50
  end
@@ -4,4 +4,4 @@ require 'httparty'
4
4
  %w( dynamic_initializer api payment borrower location country image paged_array video terms loan
5
5
  lender partner lending_action journal_entry comment team).each do |name|
6
6
  require File.join(File.dirname(__FILE__), 'ruby-kiva', name)
7
- end
7
+ end
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{simplificator-ruby-kiva}
8
- s.version = "0.1.0"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["simplificator"]
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 1
7
+ - 2
8
8
  - 0
9
- version: 0.1.0
9
+ version: 0.2.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - simplificator