ofx_for_ruby 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.externalToolBuilders/Rebuild parsers.launch +10 -0
- data/.gitignore +9 -0
- data/.loadpath +5 -0
- data/.project +27 -0
- data/COPYING +674 -0
- data/Gemfile +2 -0
- data/README +47 -0
- data/RELEASE_NOTES +45 -0
- data/Rakefile +63 -0
- data/lib/ofx.rb +60 -0
- data/lib/ofx/1.0.2/banking_message_set.rb +261 -0
- data/lib/ofx/1.0.2/credit_card_statement_message_set.rb +269 -0
- data/lib/ofx/1.0.2/document.rb +34 -0
- data/lib/ofx/1.0.2/email_message_set.rb +34 -0
- data/lib/ofx/1.0.2/financial_institution_profile_message_set.rb +155 -0
- data/lib/ofx/1.0.2/header.rb +48 -0
- data/lib/ofx/1.0.2/interbank_funds_transfer_message_set.rb +30 -0
- data/lib/ofx/1.0.2/investment_security_list_message_set.rb +30 -0
- data/lib/ofx/1.0.2/investment_statement_message_set.rb +30 -0
- data/lib/ofx/1.0.2/lexer.rb +135 -0
- data/lib/ofx/1.0.2/message_set.rb +165 -0
- data/lib/ofx/1.0.2/ofx_102.racc +90 -0
- data/lib/ofx/1.0.2/ofx_102.rex +65 -0
- data/lib/ofx/1.0.2/parser.rb +215 -0
- data/lib/ofx/1.0.2/payment_message_set.rb +30 -0
- data/lib/ofx/1.0.2/serializer.rb +114 -0
- data/lib/ofx/1.0.2/signon_message_set.rb +121 -0
- data/lib/ofx/1.0.2/signup_message_set.rb +186 -0
- data/lib/ofx/1.0.2/statements.rb +54 -0
- data/lib/ofx/1.0.2/status.rb +30 -0
- data/lib/ofx/1.0.2/wire_funds_transfer_message_set.rb +30 -0
- data/lib/ofx/banking_message_set.rb +125 -0
- data/lib/ofx/credit_card_statement_message_set.rb +104 -0
- data/lib/ofx/document.rb +30 -0
- data/lib/ofx/email_message_set.rb +42 -0
- data/lib/ofx/file_unique_identifier.rb +28 -0
- data/lib/ofx/financial_client.rb +76 -0
- data/lib/ofx/financial_institution.rb +83 -0
- data/lib/ofx/financial_institution_profile_message_set.rb +67 -0
- data/lib/ofx/header.rb +115 -0
- data/lib/ofx/http/cacert.pem +3509 -0
- data/lib/ofx/http/ofx_http_client.rb +84 -0
- data/lib/ofx/interbank_funds_transfer_message_set.rb +33 -0
- data/lib/ofx/investment_security_list_message_set.rb +33 -0
- data/lib/ofx/investment_statement_message_set.rb +33 -0
- data/lib/ofx/message_set.rb +78 -0
- data/lib/ofx/payment_message_set.rb +33 -0
- data/lib/ofx/serializer.rb +39 -0
- data/lib/ofx/signon_message_set.rb +141 -0
- data/lib/ofx/signup_message_set.rb +84 -0
- data/lib/ofx/statements.rb +49 -0
- data/lib/ofx/status.rb +100 -0
- data/lib/ofx/transaction_unique_identifier.rb +28 -0
- data/lib/ofx/version.rb +82 -0
- data/lib/ofx/wire_funds_transfer_message_set.rb +33 -0
- data/ofx_for_ruby.gemspec +25 -0
- data/planning/OFX Specification completion.ods +0 -0
- data/test/capital_one/capital_one_helper.rb +77 -0
- data/test/capital_one/fixtures/README +10 -0
- data/test/capital_one/fixtures/fipid-5599.xml +98 -0
- data/test/capital_one/test_banking_statement.rb +108 -0
- data/test/capital_one/test_financial_institution_profile.rb +295 -0
- data/test/capital_one/test_ofx_http_client.rb +72 -0
- data/test/capital_one/test_signup_account_information.rb +100 -0
- data/test/citi/citi_helper.rb +77 -0
- data/test/citi/fixtures/README +9 -0
- data/test/citi/fixtures/fipid-6642.xml +98 -0
- data/test/citi/test_credit_card_statement.rb +159 -0
- data/test/citi/test_financial_institution_profile.rb +231 -0
- data/test/citi/test_ofx_http_client.rb +71 -0
- data/test/citi/test_signup_account_information.rb +85 -0
- data/test/test_helper.rb +4 -0
- data/test/test_ofx_version.rb +141 -0
- metadata +210 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
# Copyright © 2007 Chris Guidry <chrisguidry@gmail.com>
|
2
|
+
#
|
3
|
+
# This file is part of OFX for Ruby.
|
4
|
+
#
|
5
|
+
# OFX for Ruby is free software; you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation; either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# OFX for Ruby is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
module OFX
|
19
|
+
class Header
|
20
|
+
def to_ofx_102_s
|
21
|
+
headers = ""
|
22
|
+
|
23
|
+
headers += "OFXHEADER:" + self.header_version.to_compact_s + "\n"
|
24
|
+
headers += "DATA:" + self.content_type.to_s + "\n"
|
25
|
+
headers += "VERSION:" + self.document_version.to_compact_s + "\n"
|
26
|
+
headers += "SECURITY:" + self.security.to_s + "\n"
|
27
|
+
headers += "ENCODING:" + self.content_encoding.to_s + "\n"
|
28
|
+
headers += "CHARSET:" + self.content_character_set.to_s + "\n"
|
29
|
+
headers += "COMPRESSION:" + self.compression.to_s + "\n"
|
30
|
+
headers += "OLDFILEUID:" + self.previous_unique_identifier.to_s + "\n"
|
31
|
+
headers += "NEWFILEUID:" + self.unique_identifier.to_s + "\n"
|
32
|
+
|
33
|
+
headers
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.from_ofx_102_s(header_string)
|
37
|
+
header = OFX::Header.new
|
38
|
+
|
39
|
+
header_pattern = /^(\w+)\:(.*)$/
|
40
|
+
header_string.split("\n").each do |this_header|
|
41
|
+
header_match = header_pattern.match(this_header)
|
42
|
+
header[header_match[1]] = header_match[2]
|
43
|
+
end
|
44
|
+
|
45
|
+
header
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Copyright © 2007 Chris Guidry <chrisguidry@gmail.com>
|
2
|
+
#
|
3
|
+
# This file is part of OFX for Ruby.
|
4
|
+
#
|
5
|
+
# OFX for Ruby is free software; you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation; either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# OFX for Ruby is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
module OFX
|
19
|
+
class InterbankFundsTransferMessageSet < MessageSet
|
20
|
+
end
|
21
|
+
|
22
|
+
class InterbankFundsTransferMessageSetProfile < MessageSetProfile
|
23
|
+
def self.from_ofx_102_hash(message_set_description_hash)
|
24
|
+
profile = OFX::InterbankFundsTransferMessageSetProfile.new
|
25
|
+
profile.message_set_class = OFX::InterbankFundsTransferMessageSet
|
26
|
+
profile.from_ofx_102_hash(message_set_description_hash)
|
27
|
+
profile
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Copyright © 2007 Chris Guidry <chrisguidry@gmail.com>
|
2
|
+
#
|
3
|
+
# This file is part of OFX for Ruby.
|
4
|
+
#
|
5
|
+
# OFX for Ruby is free software; you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation; either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# OFX for Ruby is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
module OFX
|
19
|
+
class InvestmentSecurityListMessageSet < MessageSet
|
20
|
+
end
|
21
|
+
|
22
|
+
class InvestmentSecurityListMessageSetProfile < MessageSetProfile
|
23
|
+
def self.from_ofx_102_hash(message_set_description_hash)
|
24
|
+
profile = OFX::InvestmentSecurityListMessageSetProfile.new
|
25
|
+
profile.message_set_class = OFX::InvestmentSecurityListMessageSet
|
26
|
+
profile.from_ofx_102_hash(message_set_description_hash)
|
27
|
+
profile
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Copyright © 2007 Chris Guidry <chrisguidry@gmail.com>
|
2
|
+
#
|
3
|
+
# This file is part of OFX for Ruby.
|
4
|
+
#
|
5
|
+
# OFX for Ruby is free software; you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation; either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# OFX for Ruby is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
module OFX
|
19
|
+
class InvestmentStatementMessageSet < MessageSet
|
20
|
+
end
|
21
|
+
|
22
|
+
class InvestmentStatementMessageSetProfile < MessageSetProfile
|
23
|
+
def self.from_ofx_102_hash(message_set_description_hash)
|
24
|
+
profile = OFX::InvestmentStatementMessageSetProfile.new
|
25
|
+
profile.message_set_class = OFX::InvestmentStatementMessageSet
|
26
|
+
profile.from_ofx_102_hash(message_set_description_hash)
|
27
|
+
profile
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
#--
|
2
|
+
# DO NOT MODIFY!!!!
|
3
|
+
# This file is automatically generated by rex 1.0.2
|
4
|
+
# from lexical definition file "ofx_102.rex".
|
5
|
+
#++
|
6
|
+
|
7
|
+
require 'racc/parser'
|
8
|
+
# Copyright © 2007 Chris Guidry <chrisguidry@gmail.com>
|
9
|
+
#
|
10
|
+
# This file is part of OFX for Ruby.
|
11
|
+
#
|
12
|
+
# OFX for Ruby is free software; you can redistribute it and/or modify
|
13
|
+
# it under the terms of the GNU General Public License as published by
|
14
|
+
# the Free Software Foundation; either version 3 of the License, or
|
15
|
+
# (at your option) any later version.
|
16
|
+
#
|
17
|
+
# OFX for Ruby is distributed in the hope that it will be useful,
|
18
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
19
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
20
|
+
# GNU General Public License for more details.
|
21
|
+
#
|
22
|
+
# You should have received a copy of the GNU General Public License
|
23
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
24
|
+
|
25
|
+
module OFX
|
26
|
+
module OFX102
|
27
|
+
class Parser < Racc::Parser
|
28
|
+
require 'strscan'
|
29
|
+
|
30
|
+
class ScanError < StandardError ; end
|
31
|
+
|
32
|
+
attr_reader :lineno
|
33
|
+
attr_reader :filename
|
34
|
+
|
35
|
+
def scan_setup ; end
|
36
|
+
|
37
|
+
def action &block
|
38
|
+
yield
|
39
|
+
end
|
40
|
+
|
41
|
+
def scan_str( str )
|
42
|
+
scan_evaluate str
|
43
|
+
do_parse
|
44
|
+
end
|
45
|
+
|
46
|
+
def load_file( filename )
|
47
|
+
@filename = filename
|
48
|
+
open(filename, "r") do |f|
|
49
|
+
scan_evaluate f.read
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def scan_file( filename )
|
54
|
+
load_file filename
|
55
|
+
do_parse
|
56
|
+
end
|
57
|
+
|
58
|
+
def next_token
|
59
|
+
@rex_tokens.shift
|
60
|
+
end
|
61
|
+
|
62
|
+
def scan_evaluate( str )
|
63
|
+
scan_setup
|
64
|
+
@rex_tokens = []
|
65
|
+
@lineno = 1
|
66
|
+
ss = StringScanner.new(str)
|
67
|
+
state = nil
|
68
|
+
until ss.eos?
|
69
|
+
text = ss.peek(1)
|
70
|
+
@lineno += 1 if text == "\n"
|
71
|
+
case state
|
72
|
+
when nil
|
73
|
+
case
|
74
|
+
when (text = ss.scan(/\<\//))
|
75
|
+
@rex_tokens.push action { state = :TAG; [:etag_in, text] }
|
76
|
+
|
77
|
+
when (text = ss.scan(/\</))
|
78
|
+
@rex_tokens.push action { state = :TAG; [:tag_in, text] }
|
79
|
+
|
80
|
+
when (text = ss.scan(/\s+(?=\S)/))
|
81
|
+
;
|
82
|
+
|
83
|
+
when (text = ss.scan(/.*?(?=(\<|\<\/)+?)/))
|
84
|
+
@rex_tokens.push action { [:text, text] }
|
85
|
+
|
86
|
+
when (text = ss.scan(/.*\S(?=\s*$)/))
|
87
|
+
@rex_tokens.push action { [:text, text] }
|
88
|
+
|
89
|
+
when (text = ss.scan(/\s+(?=$)/))
|
90
|
+
;
|
91
|
+
|
92
|
+
else
|
93
|
+
text = ss.string[ss.pos .. -1]
|
94
|
+
raise ScanError, "can not match: '" + text + "'"
|
95
|
+
end # if
|
96
|
+
|
97
|
+
when :TAG
|
98
|
+
case
|
99
|
+
when (text = ss.scan(/\>/))
|
100
|
+
@rex_tokens.push action { state = nil; [:tag_out, text] }
|
101
|
+
|
102
|
+
when (text = ss.scan(/[\w\-\.]+/))
|
103
|
+
@rex_tokens.push action { [:element, text] }
|
104
|
+
|
105
|
+
else
|
106
|
+
text = ss.string[ss.pos .. -1]
|
107
|
+
raise ScanError, "can not match: '" + text + "'"
|
108
|
+
end # if
|
109
|
+
|
110
|
+
else
|
111
|
+
raise ScanError, "undefined state: '" + state.to_s + "'"
|
112
|
+
end # case state
|
113
|
+
end # until ss
|
114
|
+
end # def scan_evaluate
|
115
|
+
|
116
|
+
end # class
|
117
|
+
|
118
|
+
end # module OFX102
|
119
|
+
end # module OFX
|
120
|
+
|
121
|
+
=begin
|
122
|
+
if __FILE__ == $0
|
123
|
+
exit if ARGV.size != 1
|
124
|
+
filename = ARGV.shift
|
125
|
+
rex = OFX::OFX102::Parser.new
|
126
|
+
begin
|
127
|
+
rex.load_file filename
|
128
|
+
while token = rex.next_token
|
129
|
+
p token
|
130
|
+
end
|
131
|
+
rescue
|
132
|
+
$stderr.printf "%s:%d:%s\n", rex.filename, rex.lineno, $!.message
|
133
|
+
end
|
134
|
+
end
|
135
|
+
=end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# Copyright © 2007 Chris Guidry <chrisguidry@gmail.com>
|
2
|
+
#
|
3
|
+
# This file is part of OFX for Ruby.
|
4
|
+
#
|
5
|
+
# OFX for Ruby is free software; you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation; either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# OFX for Ruby is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
module OFX
|
19
|
+
class MessageSet
|
20
|
+
def to_ofx_102_s
|
21
|
+
request_message = requests.length > 0
|
22
|
+
request_message = responses.length == 0
|
23
|
+
|
24
|
+
message = ''
|
25
|
+
|
26
|
+
if request_message
|
27
|
+
message += " <#{ofx_102_message_set_name}MSGSRQV#{version}>\n"
|
28
|
+
requests.each do |request|
|
29
|
+
message += request.to_ofx_102_s + "\n"
|
30
|
+
end
|
31
|
+
message += " </#{ofx_102_message_set_name}MSGSRQV#{version}>\n"
|
32
|
+
else
|
33
|
+
message += " <#{ofx_102_message_set_name}MSGSRSV#{version}>\n"
|
34
|
+
responses.each do |response|
|
35
|
+
message += response.to_ofx_102_s + "\n"
|
36
|
+
end
|
37
|
+
message += " </#{ofx_102_message_set_name}MSGSRSV#{version}>\n"
|
38
|
+
end
|
39
|
+
|
40
|
+
message
|
41
|
+
end
|
42
|
+
|
43
|
+
def ofx_102_message_set_name
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.from_ofx_102_message_set_hash(message_set_name, message_set_hash)
|
48
|
+
match = /^(\w+)MSGSR([QS])V(\d+)$/.match(message_set_name)
|
49
|
+
raise NotImplementedError unless match
|
50
|
+
|
51
|
+
message_set_class = MESSAGE_SET_NAMES_TO_CLASSES[$1]
|
52
|
+
raise NotImplementedError unless message_set_class
|
53
|
+
|
54
|
+
return message_set_class.from_ofx_102_hash(message_set_hash)
|
55
|
+
end
|
56
|
+
|
57
|
+
def request_or_response_from_ofx_102_tag_name(response_or_request_name)
|
58
|
+
raise NotImplementedError
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
MESSAGE_SET_NAMES_TO_CLASSES =
|
63
|
+
{
|
64
|
+
'SIGNON' => SignonMessageSet,
|
65
|
+
'SIGNUP' => SignupMessageSet,
|
66
|
+
'PROF' => FinancialInstitutionProfileMessageSet,
|
67
|
+
'BANK' => BankingMessageSet,
|
68
|
+
'CREDITCARD' => CreditCardStatementMessageSet
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
class MessageSetProfile
|
73
|
+
def from_ofx_102_hash(message_set_description_hash)
|
74
|
+
message_set_core_hash = message_set_description_hash['MSGSETCORE']
|
75
|
+
@version = OFX::Version.new(message_set_core_hash['VER'])
|
76
|
+
@service_provider_name = message_set_core_hash['SPNAME']
|
77
|
+
@message_url = URI.parse(message_set_core_hash['URL']) if message_set_core_hash['URL']
|
78
|
+
@required_ofx_security = message_set_core_hash['OFXSEC']
|
79
|
+
@requires_transport_security = message_set_core_hash['TRANSPSEC'] == 'Y' ? true : false
|
80
|
+
@signon_realm = message_set_core_hash['SIGNONREALM']
|
81
|
+
@language = message_set_core_hash['LANGUAGE']
|
82
|
+
@synchronization_mode = message_set_core_hash['SYNCMODE']
|
83
|
+
@supports_response_file_error_recovery = message_set_core_hash['RESPFILEER'] == 'Y' ? true : false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Request
|
88
|
+
def to_ofx_102_s
|
89
|
+
request = ''
|
90
|
+
request += " <#{ofx_102_name}RQ>\n"
|
91
|
+
|
92
|
+
request += ofx_102_request_body + "\n"
|
93
|
+
|
94
|
+
request += " </#{ofx_102_name}RQ>"
|
95
|
+
request
|
96
|
+
end
|
97
|
+
|
98
|
+
def ofx_102_request_body
|
99
|
+
raise NotImplementedError
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class TransactionalRequest < Request
|
104
|
+
def to_ofx_102_s
|
105
|
+
request = ''
|
106
|
+
request += " <#{ofx_102_name}TRNRQ>\n"
|
107
|
+
|
108
|
+
request += " <TRNUID>#{transaction_identifier}\n"
|
109
|
+
request += " <CLTCOOKIE>#{client_cookie}\n" if client_cookie
|
110
|
+
request += " <TAN>#{transaction_authorization_number}\n" if transaction_authorization_number
|
111
|
+
|
112
|
+
request += " <#{ofx_102_name}RQ>\n"
|
113
|
+
|
114
|
+
request += ofx_102_request_body + "\n"
|
115
|
+
|
116
|
+
request += " </#{ofx_102_name}RQ>\n"
|
117
|
+
|
118
|
+
request += " </#{ofx_102_name}TRNRQ>"
|
119
|
+
request
|
120
|
+
end
|
121
|
+
|
122
|
+
def ofx_102_request_body
|
123
|
+
raise NotImplementedError
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class Response
|
128
|
+
def to_ofx_102_s
|
129
|
+
response = ''
|
130
|
+
response += " <#{ofx_102_name}RS>\n"
|
131
|
+
|
132
|
+
response += ofx_102_response_body + "\n"
|
133
|
+
|
134
|
+
response += " </#{ofx_102_name}RS>"
|
135
|
+
response
|
136
|
+
end
|
137
|
+
|
138
|
+
def ofx_102_response_body
|
139
|
+
raise NotImplementedError
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class TransactionalResponse < Response
|
144
|
+
def to_ofx_102_s
|
145
|
+
response = ''
|
146
|
+
response += " <#{ofx_102_name}TRNRS>\n"
|
147
|
+
|
148
|
+
response += " <TRNUID>#{transaction_identifier}\n"
|
149
|
+
response += " <CLTCOOKIE>#{client_cookie}\n" if client_cookie
|
150
|
+
|
151
|
+
response += " <#{ofx_102_name}RS>\n"
|
152
|
+
|
153
|
+
response += ofx_102_response_body + "\n"
|
154
|
+
|
155
|
+
response += " </#{ofx_102_name}RS>\n"
|
156
|
+
|
157
|
+
response += " </#{ofx_102_name}TRNRS>"
|
158
|
+
response
|
159
|
+
end
|
160
|
+
|
161
|
+
def ofx_102_response_body
|
162
|
+
raise NotImplementedError
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# Copyright © 2007 Chris Guidry <chrisguidry@gmail.com>
|
2
|
+
#
|
3
|
+
# This file is part of OFX for Ruby.
|
4
|
+
#
|
5
|
+
# OFX for Ruby is free software; you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation; either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# OFX for Ruby is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
class OFX::OFX102::Parser
|
19
|
+
|
20
|
+
rule
|
21
|
+
target :
|
22
|
+
| ofx_body
|
23
|
+
|
24
|
+
ofx_body : tag_from contents tag_to
|
25
|
+
|
26
|
+
tag_from : tag_in element tag_out { start_tag(val[1]) }
|
27
|
+
tag_to : etag_in element tag_out { end_tag(val[1]) }
|
28
|
+
|
29
|
+
contents : /* none */
|
30
|
+
| contents content
|
31
|
+
|
32
|
+
content : text
|
33
|
+
| aggregate
|
34
|
+
| value
|
35
|
+
|
36
|
+
aggregate : tag_from contents tag_to
|
37
|
+
value : tag_from text { data_value(val[1]) }
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
---- header ----
|
42
|
+
#
|
43
|
+
# generated by racc
|
44
|
+
#
|
45
|
+
require File.dirname(__FILE__) + '/lexer'
|
46
|
+
|
47
|
+
---- inner ----
|
48
|
+
|
49
|
+
attr :documents
|
50
|
+
attr :ofx_hashes
|
51
|
+
|
52
|
+
def initialize
|
53
|
+
@documents = []
|
54
|
+
@ofx_hashes = []
|
55
|
+
|
56
|
+
@tags_stack = []
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def start_tag(tag_name)
|
61
|
+
# push an empty aggregate/value tag
|
62
|
+
@tags_stack.push({ tag_name => {} })
|
63
|
+
end
|
64
|
+
|
65
|
+
def data_value(value)
|
66
|
+
# pop the most recent tag, and use it's name as the key in the next higher tag's hash
|
67
|
+
tag = @tags_stack.pop
|
68
|
+
@tags_stack.last[@tags_stack.last.keys[0]][tag.keys[0]] = CGI::unescapeHTML(value)
|
69
|
+
end
|
70
|
+
|
71
|
+
def end_tag(tag_name)
|
72
|
+
# pop the most recent tag, stop and recognize this as a full document if this is the last tag; otherwise,
|
73
|
+
# use this tag's naem as the key in the next higher tag's hash
|
74
|
+
tag = @tags_stack.pop
|
75
|
+
if (@tags_stack.empty?)
|
76
|
+
@ofx_hashes << tag
|
77
|
+
@documents << OFX::Document.from_ofx_102_hash(tag)
|
78
|
+
else
|
79
|
+
previous = @tags_stack.last[@tags_stack.last.keys[0]][tag.keys[0]]
|
80
|
+
if previous == nil
|
81
|
+
@tags_stack.last[@tags_stack.last.keys[0]][tag.keys[0]] = tag[tag.keys[0]]
|
82
|
+
elsif previous.kind_of?(Array)
|
83
|
+
previous << tag[tag.keys[0]]
|
84
|
+
else
|
85
|
+
@tags_stack.last[@tags_stack.last.keys[0]][tag.keys[0]] = [previous, tag[tag.keys[0]]]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
---- footer ----
|