nordea-rb 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +1 -0
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/examples/atom_feed/.htaccess +9 -0
- data/examples/atom_feed/nordea.cgi +87 -0
- data/examples/transfer-to-salary +24 -0
- data/lib/nordea/resources/account.rb +28 -10
- data/lib/nordea/session.rb +18 -7
- data/lib/nordea/transaction.rb +5 -5
- data/lib/nordea/version.rb +2 -2
- data/lib/nordea.rb +22 -13
- data/nordea-rb.gemspec +8 -2
- data/test/test_account.rb +23 -2
- data/test/test_functional.rb +3 -10
- data/test/test_helper.rb +11 -0
- data/test/test_nordea.rb +34 -0
- data/test/test_session.rb +29 -0
- data/test/test_transaction.rb +12 -0
- metadata +16 -4
data/README.markdown
CHANGED
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
@@ -0,0 +1,9 @@
|
|
1
|
+
AddHandler cgi-script .cgi
|
2
|
+
|
3
|
+
RewriteEngine on
|
4
|
+
|
5
|
+
# foo.atom -> foo.cgi
|
6
|
+
RewriteRule ^(.+)\.atom$ $1.cgi
|
7
|
+
|
8
|
+
# Give Apache CGI access to HTTP auth, http://www.besthostratings.com/articles/http-auth-php-cgi.html
|
9
|
+
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Based on/stolen from http://github.com/henrik/atomica -- Thanks!
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'nordea'
|
7
|
+
require('builder') rescue require('active_support') # gem install builder
|
8
|
+
|
9
|
+
class NordeAtom
|
10
|
+
NAME = "NordeAtom"
|
11
|
+
VERSION = "1.0"
|
12
|
+
SCHEMA_DATE = "2009-11-02"
|
13
|
+
|
14
|
+
def initialize(pnr, pin)
|
15
|
+
@pnr, @pin = pnr, pin
|
16
|
+
end
|
17
|
+
|
18
|
+
def render
|
19
|
+
atomize(fetch)
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def fetch
|
25
|
+
transactions = nil
|
26
|
+
Nordea.new(@pnr, @pin) do |n|
|
27
|
+
transactions = n.accounts.map(&:transactions).flatten.sort_by(&:date).reverse
|
28
|
+
end
|
29
|
+
transactions
|
30
|
+
end
|
31
|
+
|
32
|
+
def atomize(items)
|
33
|
+
updated_at = (Time.parse(items.first.date.to_s) || Time.now).iso8601
|
34
|
+
|
35
|
+
xml = Builder::XmlMarkup.new(:indent => 2, :target => $stdout)
|
36
|
+
xml.instruct! :xml, :version => "1.0"
|
37
|
+
|
38
|
+
xml.feed(:xmlns => "http://www.w3.org/2005/Atom") do |feed|
|
39
|
+
feed.title "Kontohistorik för #{@pnr}"
|
40
|
+
feed.id "tag:nordea,#{SCHEMA_DATE}:#{@pnr}"
|
41
|
+
feed.link :href => 'http://www.nordea.se'
|
42
|
+
feed.updated updated_at
|
43
|
+
feed.author { |a| a.name 'Nordea' }
|
44
|
+
feed.generator NAME, :version => VERSION
|
45
|
+
|
46
|
+
items.each do |item|
|
47
|
+
item_time = Time.parse(item.date.to_s)
|
48
|
+
item_date = [%w[Sön Mån Tis Ons Tors Fre Lör][item_time.wday], item_time.strftime('%Y-%m-%d')].join(' ')
|
49
|
+
style = item.withdrawal? ? 'color: red' : 'color: green'
|
50
|
+
|
51
|
+
feed.entry do |entry|
|
52
|
+
entry.id "tag:nordea,#{SCHEMA_DATE}:#{@pnr}/#{item.account.index};" +
|
53
|
+
"#{item_time.strftime('%Y-%m-%d')};#{item.text.gsub(/\W/, '')};" +
|
54
|
+
amount = %Q{%.2f SEK} % item.amount
|
55
|
+
entry.title "#{item.text} #{amount}"
|
56
|
+
"#{item.amount};#{}".gsub(/\s+/, '')
|
57
|
+
entry.content %{<table>
|
58
|
+
<tr><th>Konto:</th> <td>#{item.account.name}</td></tr>
|
59
|
+
<tr><th>Datum:</th> <td>#{item_date}</td></tr>
|
60
|
+
<tr><th>Belopp:</th> <td style="#{style}">#{item.amount}</td></tr>
|
61
|
+
</table>}, :type => 'html'
|
62
|
+
entry.updated item_time.iso8601
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
if __FILE__ == $0
|
70
|
+
|
71
|
+
# HTTP Basic auth based on code from http://blogs.23.nu/c0re/2005/04/antville-7409/
|
72
|
+
require 'base64'
|
73
|
+
|
74
|
+
auth = ENV.has_key?('HTTP_AUTHORIZATION') && ENV['HTTP_AUTHORIZATION'].to_s.split
|
75
|
+
if auth && auth[0] == 'Basic'
|
76
|
+
pnr, pwd = Base64.decode64(auth[1]).split(':')[0..1]
|
77
|
+
puts "Content-Type: application/atom+xml"
|
78
|
+
puts
|
79
|
+
NordeAtom.new(pnr, pwd).render
|
80
|
+
else
|
81
|
+
puts "Status: 401 Authorization Required"
|
82
|
+
puts %{WWW-Authenticate: Basic realm="#{NordeAtom::NAME} pnr/PIN"}
|
83
|
+
puts "Content-Type: text/plain"
|
84
|
+
puts
|
85
|
+
puts "Please provide personnummer and PIN as HTTP auth username/password."
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This script will take an amount as argument (or default to 3000 SEK)
|
4
|
+
# and transfer that amount from the salary to the payments account.
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'nordea'
|
8
|
+
|
9
|
+
amount = (ARGV.first || 3000).to_f
|
10
|
+
|
11
|
+
Nordea.new(:keychain) do |n|
|
12
|
+
main, payments = n.accounts[/Salary/], n.accounts[/payments/i]
|
13
|
+
puts "Current balance:", main, payments
|
14
|
+
puts
|
15
|
+
print "Transfer #{amount} SEK to payments account? (yes/no) "
|
16
|
+
|
17
|
+
if STDIN.gets.chop =~ /^y/i
|
18
|
+
print "\nTransferring..."
|
19
|
+
main.withdraw(amount, 'SEK', :deposit_to => payments)
|
20
|
+
puts "Done"
|
21
|
+
puts
|
22
|
+
puts "Current balance:", main, payments
|
23
|
+
end
|
24
|
+
end
|
@@ -3,7 +3,8 @@ module Nordea
|
|
3
3
|
def initialize(fields = {}, command_params = {}, session = nil)
|
4
4
|
@name, @balance, @currency, @index = fields[:name], fields[:balance], fields[:currency], fields[:index]
|
5
5
|
@command_params = command_params
|
6
|
-
@session = session
|
6
|
+
@session = session if session
|
7
|
+
yield self if block_given?
|
7
8
|
end
|
8
9
|
|
9
10
|
attr_accessor :name, :balance, :currency, :index, :session
|
@@ -14,13 +15,21 @@ module Nordea
|
|
14
15
|
@command_params
|
15
16
|
end
|
16
17
|
|
17
|
-
# TODO move shared transaction fetching to Nordea::Resource and call super
|
18
18
|
def transactions(reload = false)
|
19
19
|
@transactions = nil if reload
|
20
20
|
@transactions ||= begin
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
# for some reason it needs this otherwise it would use the old transaction list
|
22
|
+
session.request(Nordea::Commands::RESOURCE)
|
23
|
+
|
24
|
+
nodes = session.request(Commands::TRANSACTIONS, to_command_params).parse_xml.search('go[@href="#trans"]')
|
25
|
+
idx = nodes.length
|
26
|
+
|
27
|
+
nodes.inject([]) do |all, node|
|
28
|
+
trans = Transaction.new_from_xml(node, self)
|
29
|
+
# since transactions don't have a time (just date) but are listed in chronological order
|
30
|
+
# we add one second to each item in the collection so they can be sorted by date
|
31
|
+
trans.date = Time.parse(trans.date.to_s) + (idx -= 1)
|
32
|
+
all << trans
|
24
33
|
end
|
25
34
|
end
|
26
35
|
end
|
@@ -33,10 +42,15 @@ module Nordea
|
|
33
42
|
%Q{%.#{decimals}f} % balance
|
34
43
|
end
|
35
44
|
|
36
|
-
def
|
45
|
+
def reload
|
46
|
+
@transactions = nil
|
47
|
+
session.accounts(true)
|
48
|
+
end
|
49
|
+
|
50
|
+
def reload_from_xml(xml_node)
|
37
51
|
xml_node = Hpricot(xml_node) unless xml_node.class.to_s =~ /^Hpricot::/
|
38
52
|
xml_node = xml_node.at("anchor") unless xml_node.name == 'anchor'
|
39
|
-
|
53
|
+
initialize(self.class._setup_fields(xml_node), self.class._setup_command_params(xml_node))
|
40
54
|
end
|
41
55
|
|
42
56
|
def withdraw(amount, currency = 'SEK', options = {})
|
@@ -68,7 +82,7 @@ module Nordea
|
|
68
82
|
:to_currency_code => currency
|
69
83
|
})
|
70
84
|
|
71
|
-
|
85
|
+
reload
|
72
86
|
end
|
73
87
|
|
74
88
|
def deposit(amount, currency = 'SEK', options = {})
|
@@ -80,8 +94,12 @@ module Nordea
|
|
80
94
|
from.withdraw(amount, currency, options.merge(:deposit_to => self))
|
81
95
|
end
|
82
96
|
|
97
|
+
def self.new_from_xml(xml_node, session)
|
98
|
+
new({}, {}, session) { |acc| acc.reload_from_xml(xml_node) }
|
99
|
+
end
|
100
|
+
|
83
101
|
private
|
84
|
-
|
102
|
+
|
85
103
|
def self._setup_fields(xml_node)
|
86
104
|
name = xml_node.at("postfield[@name='account_name']")['value']
|
87
105
|
currency = xml_node.at("postfield[@name='account_currency_code']")['value']
|
@@ -99,4 +117,4 @@ module Nordea
|
|
99
117
|
end
|
100
118
|
end
|
101
119
|
end
|
102
|
-
end
|
120
|
+
end
|
data/lib/nordea/session.rb
CHANGED
@@ -45,19 +45,30 @@ module Nordea
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def accounts(reload = false)
|
48
|
-
|
49
|
-
|
48
|
+
if reload && @accounts
|
49
|
+
reloaded_xml_nodes = reload_resources(Account)
|
50
|
+
@accounts.each_with_index { |account, idx| account.reload_from_xml(reloaded_xml_nodes[idx]) }
|
51
|
+
else
|
52
|
+
@accounts ||= setup_resources(Account)
|
53
|
+
end
|
50
54
|
end
|
51
55
|
|
52
56
|
private
|
53
57
|
|
54
|
-
def
|
58
|
+
def fetch_resources(klass)
|
55
59
|
type = klass.new.account_type_name
|
56
60
|
doc = request(Commands::RESOURCE, "account_type_name" => type).parse_xml
|
57
|
-
(doc/"go postfield[@name='OBJECT'][@value='#{Commands::TRANSACTIONS}']")
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
+
(doc/"go postfield[@name='OBJECT'][@value='#{Commands::TRANSACTIONS}']")
|
62
|
+
end
|
63
|
+
|
64
|
+
def reload_resources(klass)
|
65
|
+
fetch_resources(klass).map { |field| field.parent.parent }
|
66
|
+
end
|
67
|
+
|
68
|
+
def setup_resources(klass)
|
69
|
+
fetch_resources(klass).inject(ResourceCollection.new) do |all, field|
|
70
|
+
all << klass.new_from_xml(field.parent.parent, self)
|
71
|
+
end
|
61
72
|
end
|
62
73
|
end
|
63
74
|
end
|
data/lib/nordea/transaction.rb
CHANGED
@@ -2,11 +2,11 @@ require 'date'
|
|
2
2
|
|
3
3
|
module Nordea
|
4
4
|
class Transaction
|
5
|
-
def initialize(date, amount, text)
|
6
|
-
@date, @amount, @text = date, amount, text
|
5
|
+
def initialize(date, amount, text, account = nil)
|
6
|
+
@date, @amount, @text, @account = date, amount, text, account
|
7
7
|
end
|
8
8
|
|
9
|
-
attr_accessor :date, :amount, :text
|
9
|
+
attr_accessor :date, :amount, :text, :account
|
10
10
|
|
11
11
|
def withdrawal?
|
12
12
|
amount < 0
|
@@ -16,12 +16,12 @@ module Nordea
|
|
16
16
|
amount > 0
|
17
17
|
end
|
18
18
|
|
19
|
-
def self.new_from_xml(xml)
|
19
|
+
def self.new_from_xml(xml, account = nil)
|
20
20
|
node = xml.is_a?(String) ? Hpricot.XML(xml) : xml
|
21
21
|
date = Date.parse(node.at("setvar[@name='date']")['value'])
|
22
22
|
amount = Support.dirty_currency_string_to_f(node.at("setvar[@name='amount']")['value'])
|
23
23
|
text = node.at("setvar[@name='text']")['value']
|
24
|
-
new
|
24
|
+
new(date, amount, text, account)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
data/lib/nordea/version.rb
CHANGED
data/lib/nordea.rb
CHANGED
@@ -40,30 +40,39 @@ module Nordea
|
|
40
40
|
|
41
41
|
private
|
42
42
|
|
43
|
-
# TODO clean up
|
44
43
|
def self.extract_login_details(pnr_or_hash, pin_or_options)
|
45
44
|
if pnr_or_hash.is_a?(Hash)
|
46
|
-
pnr_or_hash
|
47
|
-
pnr = pnr_or_hash[:pnr]
|
48
|
-
pin = pnr_or_hash[:pin]
|
45
|
+
pnr, pin = _login_details_from_hash(pnr_or_hash)
|
49
46
|
elsif pnr_or_hash.is_a?(Symbol)
|
50
|
-
|
51
|
-
options = { :label => 'Nordea' }.merge(pin_or_options.symbolize_keys)
|
52
|
-
pnr, pin = account_details_from_keychain(options)
|
53
|
-
end
|
47
|
+
pnr, pin = _login_details_from_symbol(pnr_or_hash, pin_or_options)
|
54
48
|
elsif pnr_or_hash.is_a?(String) && pin_or_options.is_a?(String)
|
55
|
-
pnr = pnr_or_hash
|
56
|
-
pin = pin_or_options
|
49
|
+
pnr, pin = pnr_or_hash, pin_or_options
|
57
50
|
else
|
58
51
|
raise ArgumentError
|
59
52
|
end
|
60
53
|
[pnr.to_s.gsub(/\D+/, ''), pin.to_s.gsub(/\D+/, '')]
|
61
54
|
end
|
62
|
-
|
63
|
-
def Nordea.
|
55
|
+
|
56
|
+
def Nordea._login_details_from_hash(pnr_or_hash)
|
57
|
+
pnr_or_hash.symbolize_keys!
|
58
|
+
[pnr_or_hash[:pnr], pnr_or_hash[:pin]]
|
59
|
+
end
|
60
|
+
|
61
|
+
def Nordea._login_details_from_symbol(symbol, options)
|
62
|
+
if symbol == :keychain
|
63
|
+
_account_details_from_keychain({ :label => 'Nordea' }.merge(options.symbolize_keys))
|
64
|
+
else
|
65
|
+
raise ArgumentError
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def Nordea._account_details_from_keychain(options)
|
64
70
|
command = "security find-generic-password -l '#{options[:label]}' -g"
|
65
71
|
command << " #{options[:keychain_file].inspect}" if options[:keychain_file]
|
66
72
|
command << " 2>&1"
|
67
|
-
`#{command}
|
73
|
+
output = `#{command}`
|
74
|
+
pnr = output.match(/"acct"<blob>="(\d{10})"/)[1]
|
75
|
+
pin = output.match(/password: "(\d{4})"/)[1]
|
76
|
+
[pnr, pin]
|
68
77
|
end
|
69
78
|
end
|
data/nordea-rb.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{nordea-rb}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Martin Str\303\266m"]
|
12
|
-
s.date = %q{2009-10
|
12
|
+
s.date = %q{2009-11-10}
|
13
13
|
s.default_executable = %q{nordea}
|
14
14
|
s.description = %q{Ruby library for accessing your Nordea Bank account and transferring money between your own accounts.}
|
15
15
|
s.email = %q{name@my-domain.se}
|
@@ -25,6 +25,9 @@ Gem::Specification.new do |s|
|
|
25
25
|
"Rakefile",
|
26
26
|
"VERSION",
|
27
27
|
"bin/nordea",
|
28
|
+
"examples/atom_feed/.htaccess",
|
29
|
+
"examples/atom_feed/nordea.cgi",
|
30
|
+
"examples/transfer-to-salary",
|
28
31
|
"lib/nordea.rb",
|
29
32
|
"lib/nordea/request.rb",
|
30
33
|
"lib/nordea/resources.rb",
|
@@ -80,9 +83,12 @@ Gem::Specification.new do |s|
|
|
80
83
|
s.specification_version = 3
|
81
84
|
|
82
85
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
86
|
+
s.add_runtime_dependency(%q<hpricot>, [">= 0"])
|
83
87
|
else
|
88
|
+
s.add_dependency(%q<hpricot>, [">= 0"])
|
84
89
|
end
|
85
90
|
else
|
91
|
+
s.add_dependency(%q<hpricot>, [">= 0"])
|
86
92
|
end
|
87
93
|
end
|
88
94
|
|
data/test/test_account.rb
CHANGED
@@ -6,6 +6,7 @@ class TestAccount < Test::Unit::TestCase
|
|
6
6
|
@session = Nordea::Session.new('foo', 'bar')
|
7
7
|
@account = Nordea::Account.new(:name => "savings account", :balance => 1234.50, :currency => 'EUR', :index => '2')
|
8
8
|
@account.session = @session
|
9
|
+
@session.stubs(:request)
|
9
10
|
end
|
10
11
|
|
11
12
|
context "each Account instance" do
|
@@ -20,8 +21,14 @@ class TestAccount < Test::Unit::TestCase
|
|
20
21
|
should "request the transactions from the server" do
|
21
22
|
response = mock(:parse_xml => Hpricot.XML(fixture_content('account')))
|
22
23
|
@session.expects(:request).with('KF11TW', {}).returns(response)
|
24
|
+
@session.expects(:request).with('KF00TW') # it needs this command to reset the transaction list
|
23
25
|
@account.transactions
|
24
26
|
end
|
27
|
+
|
28
|
+
should "reload the account object from session" do
|
29
|
+
@session.expects(:accounts).with(true)
|
30
|
+
@account.reload
|
31
|
+
end
|
25
32
|
end
|
26
33
|
|
27
34
|
context "transfers" do
|
@@ -72,7 +79,7 @@ class TestAccount < Test::Unit::TestCase
|
|
72
79
|
end
|
73
80
|
|
74
81
|
should "reload the accounts" do
|
75
|
-
@
|
82
|
+
@account.expects(:reload)
|
76
83
|
@savings.withdraw(6.5, "EUR", :deposit_to => @checking)
|
77
84
|
end
|
78
85
|
end
|
@@ -105,7 +112,11 @@ class TestAccount < Test::Unit::TestCase
|
|
105
112
|
end
|
106
113
|
|
107
114
|
should "set transaction's date" do
|
108
|
-
assert_equal "2009-08-16", @transactions.first.date.
|
115
|
+
assert_equal "2009-08-16", @transactions.first.date.strftime("%Y-%m-%d")
|
116
|
+
end
|
117
|
+
|
118
|
+
should "set each transaction's time increased in the same order as listed" do
|
119
|
+
assert_equal (0...20).to_a.reverse, @transactions.map { |t| t.date.sec }
|
109
120
|
end
|
110
121
|
|
111
122
|
should "set transaction's amount" do
|
@@ -115,6 +126,10 @@ class TestAccount < Test::Unit::TestCase
|
|
115
126
|
should "set transaction's text" do
|
116
127
|
assert_equal "Res. köp", @transactions.first.text
|
117
128
|
end
|
129
|
+
|
130
|
+
should "set transaction's account" do
|
131
|
+
assert_equal @account, @transactions.first.account
|
132
|
+
end
|
118
133
|
end
|
119
134
|
|
120
135
|
context "creating a new Account object from XML" do
|
@@ -163,6 +178,12 @@ class TestAccount < Test::Unit::TestCase
|
|
163
178
|
should "have an index" do
|
164
179
|
assert_equal "1", @account.index
|
165
180
|
end
|
181
|
+
|
182
|
+
should "reload via xml" do
|
183
|
+
xml = @xml_node.sub('2.000,00', '1.234,00')
|
184
|
+
@account.reload_from_xml(xml)
|
185
|
+
assert_equal 1234.0, @account.balance
|
186
|
+
end
|
166
187
|
end
|
167
188
|
end
|
168
189
|
end
|
data/test/test_functional.rb
CHANGED
@@ -18,13 +18,6 @@ class FunctionalTest < Test::Unit::TestCase
|
|
18
18
|
end
|
19
19
|
|
20
20
|
should "login and display the number of accounts" do
|
21
|
-
def assert_requests_made(session, num_requests = 1, msg = nil, &blk)
|
22
|
-
before = session.num_requests
|
23
|
-
yield
|
24
|
-
after = session.num_requests
|
25
|
-
assert_equal before + num_requests, after, msg
|
26
|
-
end
|
27
|
-
|
28
21
|
Nordea.new('1234567890', '0987') do |n|
|
29
22
|
|
30
23
|
# logging in
|
@@ -39,7 +32,7 @@ class FunctionalTest < Test::Unit::TestCase
|
|
39
32
|
assert_requests_made(n, 1, "reloading the accounts cache") { n.accounts(true) }
|
40
33
|
|
41
34
|
# getting one of the account's transactions
|
42
|
-
assert_requests_made(n,
|
35
|
+
assert_requests_made(n, 2) do
|
43
36
|
assert_equal 5, n.accounts.first.transactions.size # we use the same ficture so all accounts have 20 transactions
|
44
37
|
end
|
45
38
|
|
@@ -48,12 +41,12 @@ class FunctionalTest < Test::Unit::TestCase
|
|
48
41
|
assert_equal 5, n.accounts['Sparkonto'].transactions.size
|
49
42
|
end
|
50
43
|
|
51
|
-
assert_requests_made(n,
|
44
|
+
assert_requests_made(n, 2, "reload the cached transactions list") {
|
52
45
|
n.accounts['Sparkonto'].transactions(true).size
|
53
46
|
}
|
54
47
|
|
55
48
|
# ask for the other accounts's transactions
|
56
|
-
assert_requests_made(n,
|
49
|
+
assert_requests_made(n, 4) do
|
57
50
|
assert_equal 10, n.accounts['Huvudkonto'].transactions.size
|
58
51
|
assert_equal 20, n.accounts['Betalkonto'].transactions.size
|
59
52
|
end
|
data/test/test_helper.rb
CHANGED
@@ -55,4 +55,15 @@ class Test::Unit::TestCase
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
58
|
+
|
59
|
+
def assert_requests_made(session, num_requests = 1, msg = nil, &blk)
|
60
|
+
before = session.num_requests
|
61
|
+
yield
|
62
|
+
after = session.num_requests
|
63
|
+
assert_equal before + num_requests, after, msg
|
64
|
+
end
|
65
|
+
|
66
|
+
def assert_no_requests_made(session, msg = nil, &blk)
|
67
|
+
assert_requests_made(session, 0, msg, &blk)
|
68
|
+
end
|
58
69
|
end
|
data/test/test_nordea.rb
CHANGED
@@ -45,5 +45,39 @@ class TestNordea < Test::Unit::TestCase
|
|
45
45
|
should "raise an error if there is no pin provided" do
|
46
46
|
assert_raise(ArgumentError) { Nordea.new("1234567890") }
|
47
47
|
end
|
48
|
+
|
49
|
+
should "raise an error when using a symbol other than :keychain" do
|
50
|
+
assert_raise(ArgumentError) { Nordea.new(:foo) }
|
51
|
+
end
|
52
|
+
|
53
|
+
should "raise call the secutiry commando on OS X to retreive the password" do
|
54
|
+
#Nordea.expects(:`).returns(<<-EOF)
|
55
|
+
return_value = <<-EOF
|
56
|
+
keychain: "/Users/me/Library/Keychains/login.keychain"
|
57
|
+
class: "genp"
|
58
|
+
attributes:
|
59
|
+
0x00000001 <blob>="Nordea"
|
60
|
+
0x00000002 <blob>=<NULL>
|
61
|
+
"acct"<blob>="1234567890"
|
62
|
+
"cdat"<timedate>=0x11111111111111111111111111111111 "111111111111111\000"
|
63
|
+
"crtr"<uint32>=<NULL>
|
64
|
+
"cusi"<sint32>=<NULL>
|
65
|
+
"desc"<blob>=<NULL>
|
66
|
+
"gena"<blob>=<NULL>
|
67
|
+
"icmt"<blob>=<NULL>
|
68
|
+
"invi"<sint32>=<NULL>
|
69
|
+
"mdat"<timedate>=0x22222222222222222222222222222222 "222222222222222\000"
|
70
|
+
"nega"<sint32>=<NULL>
|
71
|
+
"prot"<blob>=<NULL>
|
72
|
+
"scrp"<sint32>=<NULL>
|
73
|
+
"svce"<blob>="Nordea"
|
74
|
+
"type"<uint32>=<NULL>
|
75
|
+
password: "1337"
|
76
|
+
EOF
|
77
|
+
|
78
|
+
Nordea.expects(:`).with(anything).returns(return_value)
|
79
|
+
|
80
|
+
Nordea.new(:keychain)
|
81
|
+
end
|
48
82
|
end
|
49
83
|
end
|
data/test/test_session.rb
CHANGED
@@ -125,5 +125,34 @@ class TestSession < Test::Unit::TestCase
|
|
125
125
|
should "find the account's name by pattern when using #[] with a regexp" do
|
126
126
|
assert_equal @session.accounts[1], @session.accounts[/huvud/i]
|
127
127
|
end
|
128
|
+
|
129
|
+
should "cache the account objects" do
|
130
|
+
@session.accounts
|
131
|
+
assert_no_requests_made(@session) do
|
132
|
+
@session.accounts
|
133
|
+
@session.accounts
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
should "refetch the account objects when passing true as argument" do
|
138
|
+
@session.accounts
|
139
|
+
assert_requests_made(@session, 1) { @session.accounts(true) }
|
140
|
+
end
|
141
|
+
|
142
|
+
should "not create new account objects when reloading" do
|
143
|
+
assert_equal @session.accounts, @session.accounts(true)
|
144
|
+
end
|
145
|
+
|
146
|
+
should "setup the objects first time when passing reload flag" do
|
147
|
+
assert_requests_made(@session, 1) { @session.accounts(true) }
|
148
|
+
assert_equal 3, @session.accounts.length
|
149
|
+
end
|
150
|
+
|
151
|
+
should "set new balance when reloading" do
|
152
|
+
@session.accounts # create the account objects
|
153
|
+
xml = Hpricot.XML(fixture_content('accounts').sub('179,05', '1234,56'))
|
154
|
+
Nordea::Request.stubs(:new).returns(stub(:parse_xml => xml))
|
155
|
+
assert_equal 1234.56, @session.accounts(true)[/Betal/].balance
|
156
|
+
end
|
128
157
|
end
|
129
158
|
end
|
data/test/test_transaction.rb
CHANGED
@@ -11,6 +11,7 @@ class TestTransaction < Test::Unit::TestCase
|
|
11
11
|
</go>
|
12
12
|
</anchor>
|
13
13
|
END
|
14
|
+
@account = stub("Account", :name => 'fake account')
|
14
15
|
@transaction = Nordea::Transaction.new_from_xml(@xml_node)
|
15
16
|
end
|
16
17
|
|
@@ -55,4 +56,15 @@ class TestTransaction < Test::Unit::TestCase
|
|
55
56
|
assert t.deposit?
|
56
57
|
assert !t.withdrawal?
|
57
58
|
end
|
59
|
+
|
60
|
+
should "allow to pass an account to the contstructor" do
|
61
|
+
t = Nordea::Transaction.new('2009-01-01', 10.00, 'Salery', @account)
|
62
|
+
assert_equal @account, t.account
|
63
|
+
end
|
64
|
+
|
65
|
+
should "have a setter for the account property" do
|
66
|
+
t = Nordea::Transaction.new('2009-01-01', 10.00, 'Salery')
|
67
|
+
t.account = @account
|
68
|
+
assert_equal @account, t.account
|
69
|
+
end
|
58
70
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nordea-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- "Martin Str\xC3\xB6m"
|
@@ -9,10 +9,19 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-10
|
12
|
+
date: 2009-11-10 00:00:00 +01:00
|
13
13
|
default_executable: nordea
|
14
|
-
dependencies:
|
15
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hpricot
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
16
25
|
description: Ruby library for accessing your Nordea Bank account and transferring money between your own accounts.
|
17
26
|
email: name@my-domain.se
|
18
27
|
executables:
|
@@ -29,6 +38,9 @@ files:
|
|
29
38
|
- Rakefile
|
30
39
|
- VERSION
|
31
40
|
- bin/nordea
|
41
|
+
- examples/atom_feed/.htaccess
|
42
|
+
- examples/atom_feed/nordea.cgi
|
43
|
+
- examples/transfer-to-salary
|
32
44
|
- lib/nordea.rb
|
33
45
|
- lib/nordea/request.rb
|
34
46
|
- lib/nordea/resources.rb
|