buxfer 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1 +1,3 @@
1
+ v0.2 Added a proper class structure and documentation.
2
+
1
3
  v0.1 First version of Buxfer API.
data/Manifest CHANGED
@@ -1,6 +1,14 @@
1
1
  CHANGELOG
2
2
  LICENSE
3
3
  Manifest
4
- README
4
+ README.rdoc
5
5
  Rakefile
6
+ buxfer.gemspec
6
7
  lib/buxfer.rb
8
+ lib/buxfer/account.rb
9
+ lib/buxfer/base.rb
10
+ lib/buxfer/report.rb
11
+ lib/buxfer/tag.rb
12
+ spec/lib/base_spec.rb
13
+ spec/spec.opts
14
+ spec/spec_helper.rb
@@ -0,0 +1,43 @@
1
+ Buxfer is a library for accessing the buxfer API (http://www.buxfer.com).
2
+
3
+ == Requirements:
4
+
5
+ * HTTParty 0.5.0
6
+ * ActiveSupport 2.3.4
7
+
8
+ == Install:
9
+
10
+ * sudo gem install buxfer
11
+
12
+ == Usage:
13
+
14
+ require 'buxfer'
15
+
16
+ Buxfer.auth('username', 'password')
17
+ accounts = Buxfer.accounts.all => [Buxfer::Account(name => 'Savings')]
18
+ accounts.first.transactions => [amount => 30.0, description => 'Petrol']
19
+
20
+ == License
21
+
22
+ Copyright (c) 2009 Jeremy Wells
23
+
24
+ Permission is hereby granted, free of charge, to any person
25
+ obtaining a copy of this software and associated documentation
26
+ files (the "Software"), to deal in the Software without
27
+ restriction, including without limitation the rights to use,
28
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
29
+ copies of the Software, and to permit persons to whom the
30
+ Software is furnished to do so, subject to the following
31
+ conditions:
32
+
33
+ The above copyright notice and this permission notice shall be
34
+ included in all copies or substantial portions of the Software.
35
+
36
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
37
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
38
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
39
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
40
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
41
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
42
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
43
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -2,17 +2,17 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{buxfer}
5
- s.version = "0.1"
5
+ s.version = "0.2"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Jeremy Wells"]
9
- s.date = %q{2009-12-28}
9
+ s.date = %q{2009-12-29}
10
10
  s.description = %q{A library providing access to buxfer (www.buxfer.com) API based on HTTParty.}
11
11
  s.email = %q{}
12
- s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README", "lib/buxfer.rb"]
13
- s.files = ["CHANGELOG", "LICENSE", "Manifest", "README", "Rakefile", "lib/buxfer.rb", "buxfer.gemspec"]
12
+ s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.rdoc", "lib/buxfer.rb", "lib/buxfer/account.rb", "lib/buxfer/base.rb", "lib/buxfer/report.rb", "lib/buxfer/tag.rb"]
13
+ s.files = ["CHANGELOG", "LICENSE", "Manifest", "README.rdoc", "Rakefile", "buxfer.gemspec", "lib/buxfer.rb", "lib/buxfer/account.rb", "lib/buxfer/base.rb", "lib/buxfer/report.rb", "lib/buxfer/tag.rb", "spec/lib/base_spec.rb", "spec/spec.opts", "spec/spec_helper.rb"]
14
14
  s.homepage = %q{}
15
- s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Buxfer", "--main", "README"]
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Buxfer", "--main", "README.rdoc"]
16
16
  s.require_paths = ["lib"]
17
17
  s.rubyforge_project = %q{buxfer}
18
18
  s.rubygems_version = %q{1.3.5}
@@ -1,76 +1,17 @@
1
1
  require 'httparty'
2
2
  require 'active_support'
3
+ require 'ostruct'
3
4
 
4
- class Buxfer
5
- include HTTParty
6
- base_uri 'https://www.buxfer.com/api'
7
- format :xml
8
-
9
- def initialize(username, password)
10
- @username = username
11
- @password = password
12
- end
13
-
14
- def add_transaction(amount, description, account = nil, status = nil, tags = [])
15
- amount = (amount < 0 ? amount.to_s : '+' + amount.to_s)
16
- tags = tags.join(',')
17
- attrs = {}
18
- text = [description, amount]
19
-
20
- {:acct => account, :status => status, :tags => tags}.each do |k, v|
21
- text << '%s:%s' % [k, v] unless v.blank?
22
- end
23
-
24
- self.class.post('/add_transaction.xml', auth_query({:text => text, :format => 'sms'}, :body))
25
- end
26
-
27
- def upload_statement(accountId, statement, dateFormat = 'DD/MM/YYYY')
28
- unless statement.is_a?(String)
29
- if statement.respond_to?(:read)
30
- statement = statement.read
31
- else chec
32
- statement = statement.to_s
33
- end
34
- end
35
-
36
- options = {:accountId => accountId, :statement => statement, :dateFormat => dateFormat}
37
-
38
- self.class.post('/upload_statement.xml', auth_query(options, :body))
5
+ module Buxfer
6
+ class Response < OpenStruct
7
+ undef_method :id
8
+ undef_method :type
39
9
  end
10
+ end
40
11
 
41
- def transactions(options = {})
42
- self.class.get('/transactions.xml', auth_query(options))['response']['transactions']['transaction']
43
- end
44
-
45
- def reports(options = {})
46
- self.class.get('/reports.xml', auth_query(options))
47
- end
12
+ $: << File.dirname(__FILE__)
48
13
 
49
- def accounts
50
- self.class.get('/accounts.xml', auth_query)['response']['accounts']['account']
51
- end
52
-
53
- def loans
54
- self.class.get('/loans.xml', auth_query)
55
- end
56
-
57
- def tags
58
- self.class.get('/tags.xml', auth_query)['response']['tags']['tag']
59
- end
60
-
61
- private
62
-
63
- def auth
64
- @auth ||= begin
65
- self.class.get('/login.xml', :query => {:userid => @username, :password => @password})
66
- end
67
- end
68
-
69
- def token
70
- auth['response']['token']
71
- end
72
-
73
- def auth_query(options = {}, container = :query)
74
- {container => options.merge(:token => token)}
75
- end
76
- end
14
+ require 'buxfer/base'
15
+ require 'buxfer/account'
16
+ require 'buxfer/tag'
17
+ require 'buxfer/report'
@@ -0,0 +1,49 @@
1
+ module Buxfer
2
+ class Account < OpenStruct
3
+ # Returns an array of accounts. See Buxfer::Base#accounts
4
+ #
5
+ # Example:
6
+ # Buxfer::Account.all
7
+ #
8
+ def self.all
9
+ Buxfer.accounts
10
+ end
11
+
12
+ # Return an array of the last 25 transactions for this account.
13
+ # See Buxfer::Base#transactions for valid options.
14
+ def transactions(options = {})
15
+ Buxfer.transactions(options.merge(:account => self))
16
+ end
17
+
18
+ # Add a transaction to Buxfer. The amount and description must be
19
+ # specified.
20
+ #
21
+ # An array of tag names can be specified.
22
+ #
23
+ # Examples:
24
+ #
25
+ # account.add_transaction(1000, 'Salary')
26
+ #
27
+ # See: http://www.buxfer.com/help.php?topic=API#add_transaction
28
+ def add_transaction(amount, description, status = nil, tags = [])
29
+ Buxfer.add_transaction(amount, description, self.name, status, tags)
30
+ end
31
+
32
+ # Upload a file containing a transaction statement to Buxfer account.
33
+ #
34
+ # The statement can be a String or an IO object.
35
+ #
36
+ # An optional date format can be passed indicating if the dates used in the statement
37
+ # are in the format 'DD/MM/YYYY' (default) or 'MM/DD/YYYY'
38
+ #
39
+ # Example:
40
+ #
41
+ # account = Buxfer.accounts.first
42
+ # account.upload_statement(open('/path/to/file')) => true
43
+ #
44
+ # http://www.buxfer.com/help.php?topic=API#upload_statement
45
+ def upload_statement(statement, date_format = 'DD/MM/YYYY')
46
+ Buxfer.upload_statement(self.id, statement, date_format)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,214 @@
1
+ # The Buxfer API methods can be called on the Buxfer module after calling
2
+ # the #auth method. See Buxfer::Base for more information on each API call.
3
+ module Buxfer
4
+ # Specify the authentication details for connecting to Buxfer
5
+ def self.auth(username, password)
6
+ @username = username
7
+ @password = password
8
+ @connection = nil
9
+ end
10
+
11
+ def self.method_missing(method, *args) #:nodoc:
12
+ if connection.respond_to?(method)
13
+ connection.send(method, *args)
14
+ else
15
+ super
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ # The username that will be used
22
+ def self.username
23
+ @username
24
+ end
25
+
26
+ # The password that will be used
27
+ def self.password
28
+ @password
29
+ end
30
+
31
+ def self.connection #:nodoc:
32
+ @connection ||= Buxfer::Base.new(username, password)
33
+ end
34
+
35
+ public
36
+
37
+ # The Buxfer::Base class provides the API methods. It can be instansiated directly
38
+ # or a default instance is used with the Buxfer module.
39
+ #
40
+ # Default usage:
41
+ #
42
+ # Buxfer.auth('username', 'password')
43
+ # Buxfer.accounts
44
+ #
45
+ # Instance usage:
46
+ #
47
+ # client = Buxfer::Base.new('username', 'password')
48
+ # client.accounts
49
+ #
50
+ class Base
51
+ include HTTParty
52
+ base_uri 'https://www.buxfer.com/api'
53
+ format :xml
54
+
55
+ # Create a new Buxfer::Base object with a username and password.
56
+ def initialize(username, password)
57
+ @username = username
58
+ @password = password
59
+ end
60
+
61
+ # Add a transaction to Buxfer. The amount and description must be
62
+ # specified.
63
+ #
64
+ # An account name or Buxfer::Account object can be specified
65
+ # if the transaction is to be associated with a particular account.
66
+ #
67
+ # An array of tag names can be specified.
68
+ #
69
+ # Examples:
70
+ #
71
+ # Buxfer.add_transaction(-10.0, 'Internet bill', 'Current account', nil, %w(bill payment))
72
+ # Buxfer.add_transaction(1000, 'Salary', 'Current account', 'pending', %w(salary))
73
+ #
74
+ # See: http://www.buxfer.com/help.php?topic=API#add_transaction
75
+ def add_transaction(amount, description, account = nil, status = nil, tags = [])
76
+ amount = (amount < 0 ? amount.to_s : '+' + amount.to_s)
77
+ tags = tags.join(',')
78
+ attrs = {}
79
+ text = [description, amount]
80
+ account.respond_to?(:name) ? account.name : account.to_s
81
+
82
+ {:acct => account, :status => status, :tags => tags}.each do |k, v|
83
+ text << '%s:%s' % [k, v] unless v.blank?
84
+ end
85
+
86
+ self.class.post('/add_transaction.xml', auth_query({:text => text, :format => 'sms'}, :body))
87
+ end
88
+
89
+ # Upload a file containing a transaction statement to Buxfer account. The account
90
+ # specified can be a Buxfer::Account (see #accounts) or an account id string.
91
+ #
92
+ # The statement can be a String or an IO object.
93
+ #
94
+ # An optional date format can be passed indicating if the dates used in the statement
95
+ # are in the format 'DD/MM/YYYY' (default) or 'MM/DD/YYYY'
96
+ #
97
+ # Example:
98
+ #
99
+ # account = Buxfer.accounts.first
100
+ # Buxfer.upload_statement(account, open('/path/to/file')) => true
101
+ #
102
+ # http://www.buxfer.com/help.php?topic=API#upload_statement
103
+ def upload_statement(account, statement, date_format = 'DD/MM/YYYY')
104
+ account_id = account.is_a?(Buxfer::Account) ? account.id : account
105
+
106
+ unless statement.is_a?(String)
107
+ if statement.respond_to?(:read)
108
+ statement = statement.read
109
+ else
110
+ statement = statement.to_s
111
+ end
112
+ end
113
+
114
+ options = {:accountId => account_id, :statement => statement, :dateFormat => date_format}
115
+
116
+ self.class.post('/upload_statement.xml', auth_query(options, :body))
117
+ end
118
+
119
+ # Return an array of the last 25 transactions.
120
+ #
121
+ # Valid options:
122
+ #
123
+ # * <tt>:account</tt> - A Buxfer::Account or account name
124
+ # * <tt>:tag</tt> - A Buxfer::Tag object or tag name
125
+ # * <tt>:startDate</tt> - Date in format '10 feb 2008' or '2008-02-10'
126
+ # * <tt>:endDate</tt> - Date in format '10 feb 2008' or '2008-02-10'
127
+ # * <tt>:month</tt> - Month name and year in short format 'feb 08'
128
+ #
129
+ # http://www.buxfer.com/help.php?topic=API#transactions
130
+ def transactions(options = {})
131
+ options.symbolize_keys!
132
+
133
+ if account = options.delete(:account)
134
+ options[:accountName] = account.is_a?(Buxfer::Account) ? account.name : account
135
+ end
136
+
137
+ if tag = options.delete(:tag)
138
+ options[:tagName] = tag.is_a?(Buxfer::Tag) ? tag.name : tag
139
+ end
140
+
141
+ get_collection('transactions', options)
142
+ end
143
+
144
+ # Return a Buxfer::Report object.
145
+ #
146
+ # http://www.buxfer.com/help.php?topic=API#reports
147
+ def reports(options = {})
148
+ Buxfer::Report.new(get('/reports.xml', auth_query(options))['response'])
149
+ end
150
+
151
+ # Returns an array of Buxfer::Account objects
152
+ #
153
+ # http://www.buxfer.com/help.php?topic=API#accounts
154
+ def accounts
155
+ get_collection 'accounts', :class => Buxfer::Account
156
+ end
157
+
158
+ # Returns an array of Buxfer::Tag objects
159
+ #
160
+ # http://www.buxfer.com/help.php?topic=API#tags
161
+ def tags
162
+ get_collection 'tags', :class => Buxfer::Tag
163
+ end
164
+
165
+ # http://www.buxfer.com/help.php?topic=API#loans
166
+ def loans; get_collection('loans'); end
167
+ # http://www.buxfer.com/help.php?topic=API#budgets
168
+ def budgets; get_collection('budgets'); end
169
+ # http://www.buxfer.com/help.php?topic=API#reminders
170
+ def reminders; get_collection('reminders'); end
171
+ # http://www.buxfer.com/help.php?topic=API#groups
172
+ def groups; get_collection('groups'); end
173
+ # http://www.buxfer.com/help.php?topic=API#contacts
174
+ def contacts; get_collection('contacts'); end
175
+
176
+ private
177
+
178
+ def get(*args)
179
+ self.class.get(*args)
180
+ end
181
+
182
+ def get_collection(plural, options = {})
183
+ options.symbolize_keys!
184
+ path = options.delete(:path) || '/%s.xml' % plural
185
+ singular = options.delete(:singular) || plural.singularize
186
+ klass = options.delete(:class) || Buxfer::Response
187
+
188
+ response = get(path, auth_query(options))['response']
189
+ load_collection(response[plural][singular], klass)
190
+ end
191
+
192
+ def load_collection(collection, klass = Buxfer::Response)
193
+ if collection.respond_to?(:map)
194
+ collection.map{|item| klass.new(item) }
195
+ else
196
+ [klass.new(collection)]
197
+ end
198
+ end
199
+
200
+ def auth
201
+ @auth ||= begin
202
+ self.class.get('/login.xml', :query => {:userid => @username, :password => @password})
203
+ end
204
+ end
205
+
206
+ def token
207
+ auth['response']['token']
208
+ end
209
+
210
+ def auth_query(options = {}, container = :query)
211
+ {container => options.merge(:token => token)}
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,30 @@
1
+ module Buxfer
2
+ class Report
3
+ def initialize(data)
4
+ @data = data['analysis']
5
+ @tags = [@data['rawData']['item']].flatten.map do |item|
6
+ Buxfer::Tag.new(:name => item['tag'], :amount => item['amount'].to_f)
7
+ end
8
+ end
9
+
10
+ def image_url
11
+ @data['imageURL']
12
+ end
13
+
14
+ def [](value)
15
+ tags.detect{|tag| tag.name == value }
16
+ end
17
+
18
+ def tags
19
+ @tags
20
+ end
21
+
22
+ def tag_names
23
+ tags.map(&:name)
24
+ end
25
+
26
+ def to_s
27
+ tags.collect{|t| [t.name, t.amount].join(': ') }.join("\n")
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ module Buxfer
2
+ class Tag < OpenStruct
3
+ # Return an array of the last 25 transactions for this tag.
4
+ # See Buxfer::Base#transactions for valid options.
5
+ def transactions(options = {})
6
+ Buxfer.transactions(options.merge(:tag => self))
7
+ end
8
+
9
+ def report(options = {})
10
+ Buxfer.reports(options.merge(:tagName => self.name))
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe Buxfer do
4
+ describe '::auth' do
5
+ it 'should reset the connection'
6
+ end
7
+
8
+ describe '::connection' do
9
+ it 'should create a new connection'
10
+ end
11
+ end
12
+
13
+ describe Buxfer::Base do
14
+ before do
15
+ @base = Buxfer::Base.new('username', 'password')
16
+ @base.stub!(:token).and_return('token')
17
+ @base.class.stub!(:get)
18
+ end
19
+
20
+ describe '#tags' do
21
+ it 'should return a collection of Buxfer::Tag objects'
22
+ end
23
+
24
+ describe '#accounts' do
25
+ it 'should return a collection of Buxfer::Account objects'
26
+ end
27
+
28
+ %w(loans budgets reminders groups contacts).each do |type|
29
+ describe "##{type}" do
30
+ before do
31
+ @response = {
32
+ 'response' => {
33
+ type => {
34
+ type.singularize => [
35
+ {'id' => 1},
36
+ {'id' => 2}
37
+ ]
38
+ }
39
+ }
40
+ }
41
+ @base.class.stub!(:get).and_return(@response)
42
+ end
43
+
44
+ it "should call get on the path /#{type}.xml" do
45
+ @base.class.should_receive(:get).with("/#{type}.xml", anything).and_return(@response)
46
+ @base.send(type)
47
+ end
48
+
49
+ it "should pass the auth token with the get request" do
50
+ @base.class.should_receive(:get).with(anything, :query => {:token => 'token'}).and_return(@response)
51
+ @base.send(type)
52
+ end
53
+
54
+ it "should load the collection from response[#{type}][#{type.singularize}]" do
55
+ @base.send(type).first.should be_a(Buxfer::Response)
56
+ @base.send(type).first.id.should == 1
57
+ @base.send(type).last.id.should == 2
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,3 @@
1
+ --color
2
+ --reverse
3
+ --debugger
@@ -0,0 +1,2 @@
1
+ require 'rubygems'
2
+ load 'lib/buxfer.rb'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: buxfer
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.1"
4
+ version: "0.2"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Wells
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-28 00:00:00 +13:00
12
+ date: 2009-12-29 00:00:00 +13:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -47,16 +47,27 @@ extensions: []
47
47
  extra_rdoc_files:
48
48
  - CHANGELOG
49
49
  - LICENSE
50
- - README
50
+ - README.rdoc
51
51
  - lib/buxfer.rb
52
+ - lib/buxfer/account.rb
53
+ - lib/buxfer/base.rb
54
+ - lib/buxfer/report.rb
55
+ - lib/buxfer/tag.rb
52
56
  files:
53
57
  - CHANGELOG
54
58
  - LICENSE
55
59
  - Manifest
56
- - README
60
+ - README.rdoc
57
61
  - Rakefile
58
- - lib/buxfer.rb
59
62
  - buxfer.gemspec
63
+ - lib/buxfer.rb
64
+ - lib/buxfer/account.rb
65
+ - lib/buxfer/base.rb
66
+ - lib/buxfer/report.rb
67
+ - lib/buxfer/tag.rb
68
+ - spec/lib/base_spec.rb
69
+ - spec/spec.opts
70
+ - spec/spec_helper.rb
60
71
  has_rdoc: true
61
72
  homepage: ""
62
73
  licenses: []
@@ -68,7 +79,7 @@ rdoc_options:
68
79
  - --title
69
80
  - Buxfer
70
81
  - --main
71
- - README
82
+ - README.rdoc
72
83
  require_paths:
73
84
  - lib
74
85
  required_ruby_version: !ruby/object:Gem::Requirement
data/README DELETED
File without changes