kelredd-wesabe 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +39 -0
- data/Rakefile +41 -0
- data/lib/wesabe/configuration.rb +5 -0
- data/lib/wesabe/exceptions.rb +8 -0
- data/lib/wesabe/extensions.rb +60 -0
- data/lib/wesabe/model/account.rb +50 -0
- data/lib/wesabe/model/base.rb +136 -0
- data/lib/wesabe/model/institution.rb +16 -0
- data/lib/wesabe/model/merchant.rb +16 -0
- data/lib/wesabe/model/txaction.rb +38 -0
- data/lib/wesabe/model.rb +3 -0
- data/lib/wesabe/version.rb +13 -0
- data/lib/wesabe.rb +4 -0
- data/test/test_helper.rb +10 -0
- data/test/unit/wesabe_test.rb +13 -0
- metadata +90 -0
data/README.rdoc
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
= Wesabe
|
2
|
+
|
3
|
+
== Description
|
4
|
+
|
5
|
+
A simplistic gem to assist in consuming the Wesabe API.
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
sudo gem install kelredd-wesabe --source http://gems.github.com
|
10
|
+
|
11
|
+
== Usage
|
12
|
+
|
13
|
+
require 'wesabe'
|
14
|
+
# then include any wesabe helpers into your own models or scripts
|
15
|
+
|
16
|
+
== License
|
17
|
+
|
18
|
+
Copyright (c) 2009 Kelly Redding
|
19
|
+
|
20
|
+
Permission is hereby granted, free of charge, to any person
|
21
|
+
obtaining a copy of this software and associated documentation
|
22
|
+
files (the "Software"), to deal in the Software without
|
23
|
+
restriction, including without limitation the rights to use,
|
24
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
25
|
+
copies of the Software, and to permit persons to whom the
|
26
|
+
Software is furnished to do so, subject to the following
|
27
|
+
conditions:
|
28
|
+
|
29
|
+
The above copyright notice and this permission notice shall be
|
30
|
+
included in all copies or substantial portions of the Software.
|
31
|
+
|
32
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
33
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
34
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
35
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
36
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
37
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
38
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
39
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
require 'lib/wesabe/version'
|
6
|
+
|
7
|
+
task :default => :test
|
8
|
+
|
9
|
+
spec = Gem::Specification.new do |s|
|
10
|
+
s.name = 'wesabe'
|
11
|
+
s.version = Wesabe::Version.to_s
|
12
|
+
s.has_rdoc = true
|
13
|
+
s.extra_rdoc_files = %w(README.rdoc)
|
14
|
+
s.rdoc_options = %w(--main README.rdoc)
|
15
|
+
s.summary = "A simplistic gem to assist in consuming the Wesabe API."
|
16
|
+
s.author = 'Kelly Redding'
|
17
|
+
s.email = 'kelly@kelredd.com'
|
18
|
+
s.homepage = ''
|
19
|
+
s.files = %w(README.rdoc Rakefile) + Dir.glob("{lib,test}/**/*")
|
20
|
+
# s.executables = ['wesabe']
|
21
|
+
|
22
|
+
s.add_dependency('nokogiri', '> 1.2.3')
|
23
|
+
s.add_dependency('rest-client', '> 0.9.2')
|
24
|
+
end
|
25
|
+
|
26
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
27
|
+
pkg.gem_spec = spec
|
28
|
+
end
|
29
|
+
|
30
|
+
Rake::TestTask.new do |t|
|
31
|
+
t.libs << 'test'
|
32
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
33
|
+
t.verbose = true
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'Generate the gemspec to serve this Gem from Github'
|
37
|
+
task :github do
|
38
|
+
file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
|
39
|
+
File.open(file, 'w') {|f| f << spec.to_ruby }
|
40
|
+
puts "Created gemspec: #{file}"
|
41
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module Wesabe
|
4
|
+
module Extensions
|
5
|
+
module String
|
6
|
+
|
7
|
+
module ClassMethods; end
|
8
|
+
def self.included(klass)
|
9
|
+
klass.extend(ClassMethods) if klass.kind_of?(Class)
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_datetime
|
16
|
+
DateTime.strptime(self) rescue nil
|
17
|
+
end
|
18
|
+
def to_date
|
19
|
+
Date.strptime(self) rescue nil
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module Wesabe
|
27
|
+
module Extensions
|
28
|
+
module Hash
|
29
|
+
|
30
|
+
module ClassMethods; end
|
31
|
+
def self.included(klass)
|
32
|
+
klass.extend(ClassMethods) if klass.kind_of?(Class)
|
33
|
+
end
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns string formatted for HTTP URL encoded name-value pairs.
|
39
|
+
# For example,
|
40
|
+
# {:id => 'thomas_hardy'}.to_http_str
|
41
|
+
# # => "id=thomas_hardy"
|
42
|
+
# {:id => 23423, :since => Time.now}.to_http_str
|
43
|
+
# # => "since=Thu,%2021%20Jun%202007%2012:10:05%20-0500&id=23423"
|
44
|
+
def to_http_query_str(opts = {})
|
45
|
+
opts[:prepend] ||= '?'
|
46
|
+
opts[:append] ||= ''
|
47
|
+
self.empty? ? '' : "#{opts[:prepend]}#{self.collect{|key, val| "#{key.to_s}=#{CGI.escape(val.to_s)}"}.join('&')}#{opts[:append]}"
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class String
|
55
|
+
include Wesabe::Extensions::String
|
56
|
+
end
|
57
|
+
|
58
|
+
class Hash
|
59
|
+
include Wesabe::Extensions::Hash
|
60
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
%w(base institution).each do |file|
|
2
|
+
require File.join(File.dirname(__FILE__), "#{file}.rb")
|
3
|
+
end
|
4
|
+
|
5
|
+
module Wesabe
|
6
|
+
module Model
|
7
|
+
class Account < Wesabe::Model::Base
|
8
|
+
|
9
|
+
def self.find(id)
|
10
|
+
case id
|
11
|
+
when :all
|
12
|
+
find_collection("/accounts", "//accounts/account")
|
13
|
+
else
|
14
|
+
super("/accounts/#{id}", "//account")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
attribute :id, :integer, :path => "id"
|
19
|
+
attribute :key, :string, :path => "guid"
|
20
|
+
attribute :number, :string, :path => "account-number"
|
21
|
+
attribute :name, :string, :path => "name"
|
22
|
+
attribute :account_type, :string, :path => "account-type"
|
23
|
+
attribute :balance, :float, :path => "current-balance"
|
24
|
+
attribute :last_uploaded_at, :datetime, :path => "last-uploaded-at"
|
25
|
+
attribute :oldest_transaction_on, :date, :path => "oldest-txaction"
|
26
|
+
attribute :newest_transaction_on, :date, :path => "newest-txaction"
|
27
|
+
|
28
|
+
def institution
|
29
|
+
@institution ||= Wesabe::Model::Institution.new(get_node('./financial-institution'))
|
30
|
+
end
|
31
|
+
|
32
|
+
def currency
|
33
|
+
node = get_node('./currency')
|
34
|
+
@currency ||= {
|
35
|
+
:symbol => node['symbol'],
|
36
|
+
:delimiter => node['delimiter'],
|
37
|
+
:separator => node['separator'],
|
38
|
+
:decimal_places => node['decimal_places'],
|
39
|
+
:type => node.content.to_s
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
self.id
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
%w(rubygems rest_client nokogiri).each do |lib|
|
2
|
+
require lib
|
3
|
+
end
|
4
|
+
%w(configuration exceptions extensions).each do |file|
|
5
|
+
require File.join(File.dirname(__FILE__), "..", "#{file}.rb")
|
6
|
+
end
|
7
|
+
|
8
|
+
module Wesabe
|
9
|
+
module Model
|
10
|
+
|
11
|
+
class Base
|
12
|
+
@@user = nil
|
13
|
+
@@password = nil
|
14
|
+
@@content_type = Wesabe::API_DEFAULT_CONTENT_TYPE
|
15
|
+
|
16
|
+
def self.resource
|
17
|
+
RestClient::Resource.new(Wesabe::API_SITE, :user => @@user, :password => @@password, :content_type => @@content_type)
|
18
|
+
end
|
19
|
+
@@api = resource
|
20
|
+
@@api_cache = {}
|
21
|
+
|
22
|
+
attr_reader :xml
|
23
|
+
|
24
|
+
def self.configure(opts={})
|
25
|
+
@@user = opts[:user]
|
26
|
+
@@password = opts[:password]
|
27
|
+
@@content_type = opts[:content_type] if opts[:content_type] && Wesabe::API_CONTENT_TYPES.include?(opts[:content_type])
|
28
|
+
@@api = resource
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.find(path, xpath)
|
32
|
+
new(get_xml(path).xpath(xpath))
|
33
|
+
end
|
34
|
+
def self.find_collection(path, xpath)
|
35
|
+
get_xml(path).xpath(xpath).collect{|item_xml| new(item_xml)}
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(xml)
|
39
|
+
@xml = xml
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def self.get_xml(path)
|
45
|
+
self.resource_xml(:get, path)
|
46
|
+
end
|
47
|
+
def self.resource_xml(verb, path, force=false)
|
48
|
+
summary = "#{verb.to_s.upcase} #{@@api.url}#{path}"
|
49
|
+
key = cache_key(verb, path)
|
50
|
+
begin
|
51
|
+
resp = @@api_cache[key]
|
52
|
+
if resp.nil? || force
|
53
|
+
base_config_check
|
54
|
+
p "** Wesabe API: #{summary}"
|
55
|
+
resp = case verb.to_sym
|
56
|
+
when :get
|
57
|
+
@@api["#{path}"].get
|
58
|
+
end
|
59
|
+
@@api_cache[key] = resp
|
60
|
+
else
|
61
|
+
p "** Wesabe API: [cache] #{summary}."
|
62
|
+
end
|
63
|
+
rescue Exception => err
|
64
|
+
raise Wesabe::ResourceAPIError, "error with API resource: #{summary}: #{err.message}"
|
65
|
+
end
|
66
|
+
begin
|
67
|
+
xml = Nokogiri::XML(resp.to_s)
|
68
|
+
rescue Exception => err
|
69
|
+
raise Wesabe::ResourceParseError, "error parsing API resource [#{@@content_type}]: #{summary}: #{err.message}: Response: #{resp.to_s}"
|
70
|
+
end
|
71
|
+
xml
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.attribute(name, type, config)
|
75
|
+
raise Wesabe::NotConfigured, "no path provided for selecting the attribute '#{name}'." unless config[:path]
|
76
|
+
content_method = case type.to_sym
|
77
|
+
when :string
|
78
|
+
'to_s'
|
79
|
+
when :integer
|
80
|
+
'to_i'
|
81
|
+
when :float
|
82
|
+
'to_f'
|
83
|
+
when :date
|
84
|
+
'to_date'
|
85
|
+
when :datetime
|
86
|
+
'to_datetime'
|
87
|
+
else
|
88
|
+
'to_s'
|
89
|
+
end
|
90
|
+
define_method(name) do
|
91
|
+
instance_variable_get("@#{name}") || instance_variable_set("@#{name}", get_node("./#{config[:path]}").content.send(content_method))
|
92
|
+
end
|
93
|
+
end
|
94
|
+
def self.get_node(xml, path)
|
95
|
+
xml.xpath(path.to_s).first
|
96
|
+
end
|
97
|
+
def get_node(path)
|
98
|
+
self.class.get_node(@xml, path)
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.xml_root_name(xml)
|
102
|
+
xml.root.name
|
103
|
+
end
|
104
|
+
def xml_root_name
|
105
|
+
self.class.xml_root_name(@xml)
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.cache_key(path, verb)
|
109
|
+
"#{verb}_#{path}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.clear_cache
|
113
|
+
@@api_cache = {}
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.base_config_check
|
117
|
+
raise Wesabe::NotConfigured, "No user provided to access the Wesabe API" if blank?(@@user)
|
118
|
+
raise Wesabe::NotConfigured, "No password provided to access the Wesabe API" if blank?(@@password)
|
119
|
+
raise Wesabe::NotConfigured, "Wesabe not configured to access the API." if blank?(@@api)
|
120
|
+
end
|
121
|
+
def base_config_check
|
122
|
+
self.class.base_config_check
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.blank?(value)
|
126
|
+
value.nil? || (value.respond_to?('empty?') ? value.empty? : false)
|
127
|
+
end
|
128
|
+
def blank?(value)
|
129
|
+
self.class.blank?(value)
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
%w(base).each do |file|
|
2
|
+
require File.join(File.dirname(__FILE__), "#{file}.rb")
|
3
|
+
end
|
4
|
+
|
5
|
+
module Wesabe
|
6
|
+
module Model
|
7
|
+
|
8
|
+
class Institution < Wesabe::Model::Base
|
9
|
+
|
10
|
+
attribute :key, :string, :path => "id"
|
11
|
+
attribute :name, :string, :path => "name"
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
%w(base).each do |file|
|
2
|
+
require File.join(File.dirname(__FILE__), "#{file}.rb")
|
3
|
+
end
|
4
|
+
|
5
|
+
module Wesabe
|
6
|
+
module Model
|
7
|
+
|
8
|
+
class Merchant < Wesabe::Model::Base
|
9
|
+
|
10
|
+
attribute :key, :string, :path => "id"
|
11
|
+
attribute :name, :string, :path => "name"
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
%w(base account merchant).each do |file|
|
2
|
+
require File.join(File.dirname(__FILE__), "#{file}.rb")
|
3
|
+
end
|
4
|
+
|
5
|
+
module Wesabe
|
6
|
+
module Model
|
7
|
+
class Txaction < Wesabe::Model::Base
|
8
|
+
|
9
|
+
def self.find(opts={})
|
10
|
+
acct = opts.delete(:account)
|
11
|
+
path = (acct ? "/accounts/#{acct.to_s}.xml" : "/transactions.xml")
|
12
|
+
path += opts.to_http_query_str if opts.respond_to?('to_http_query_str')
|
13
|
+
find_collection(path, "//txactions/txaction")
|
14
|
+
end
|
15
|
+
|
16
|
+
attribute :key, :string, :path => "guid"
|
17
|
+
attribute :account_id, :string, :path => "account-id"
|
18
|
+
attribute :date, :date, :path => "date"
|
19
|
+
attribute :original_date, :date, :path => "original-date"
|
20
|
+
attribute :amount, :float, :path => "amount"
|
21
|
+
attribute :display_name, :string, :path => "display-name"
|
22
|
+
attribute :raw_name, :string, :path => "raw-name"
|
23
|
+
attribute :raw_type, :string, :path => "raw-txntype"
|
24
|
+
attribute :memo, :string, :path => "memo"
|
25
|
+
attribute :transfer_key, :string, :path => "transfer/guid"
|
26
|
+
|
27
|
+
def account
|
28
|
+
@account ||= Wesabe::Model::Account.find(self.account_id)
|
29
|
+
end
|
30
|
+
|
31
|
+
def merchant
|
32
|
+
@merchant ||= (get_node('./merchant') ? Wesabe::Model::Merchant.new(get_node('./merchant')) : nil)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
data/lib/wesabe/model.rb
ADDED
data/lib/wesabe.rb
ADDED
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kelredd-wesabe
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kelly Redding
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-06-15 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: nokogiri
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.2.3
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rest-client
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.9.2
|
34
|
+
version:
|
35
|
+
description:
|
36
|
+
email: kelly@kelredd.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.rdoc
|
43
|
+
files:
|
44
|
+
- README.rdoc
|
45
|
+
- Rakefile
|
46
|
+
- lib/wesabe
|
47
|
+
- lib/wesabe/configuration.rb
|
48
|
+
- lib/wesabe/exceptions.rb
|
49
|
+
- lib/wesabe/extensions.rb
|
50
|
+
- lib/wesabe/model
|
51
|
+
- lib/wesabe/model/account.rb
|
52
|
+
- lib/wesabe/model/base.rb
|
53
|
+
- lib/wesabe/model/institution.rb
|
54
|
+
- lib/wesabe/model/merchant.rb
|
55
|
+
- lib/wesabe/model/txaction.rb
|
56
|
+
- lib/wesabe/model.rb
|
57
|
+
- lib/wesabe/version.rb
|
58
|
+
- lib/wesabe.rb
|
59
|
+
- test/test_helper.rb
|
60
|
+
- test/unit
|
61
|
+
- test/unit/wesabe_test.rb
|
62
|
+
has_rdoc: true
|
63
|
+
homepage: ""
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options:
|
66
|
+
- --main
|
67
|
+
- README.rdoc
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: "0"
|
81
|
+
version:
|
82
|
+
requirements: []
|
83
|
+
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 1.2.0
|
86
|
+
signing_key:
|
87
|
+
specification_version: 2
|
88
|
+
summary: A simplistic gem to assist in consuming the Wesabe API.
|
89
|
+
test_files: []
|
90
|
+
|