buxfer 0.1 → 0.2

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/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