qif 1.1.1 → 1.1.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.
- checksums.yaml +7 -0
- data/README.rdoc +3 -1
- data/Rakefile +0 -7
- data/lib/qif/date_format.rb +30 -43
- data/lib/qif/reader.rb +21 -7
- data/spec/fixtures/3_records_spaced.qif +19 -0
- data/spec/lib/date_format_spec.rb +4 -4
- data/spec/lib/reader_spec.rb +29 -22
- data/spec/lib/writer_spec.rb +1 -1
- metadata +16 -13
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 05d7562d52dc8df636d290de9e2cce5f412a6013
|
4
|
+
data.tar.gz: f47615d7c5077148c7c508bab6f14839730e49ad
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4514f7c2a458fb6989ca943a5f8050f905f81e449ced6adfd79daab2ab97bbb033cb2125f42f8e576bdec73a1a0fa08657e23b77bf91f2d9c5759f8717664973
|
7
|
+
data.tar.gz: 87fe80e1d2b395d875121fdea2a3679e53269fa4b632cafcaca467c02bc89ddb43630c5fe113c34d0a694976386ccbc8ed2d89898f6006d99f167f03a4bee146
|
data/README.rdoc
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
{<img src="https://travis-ci.org/jemmyw/Qif.png?branch=master" alt="Build Status" />}[https://travis-ci.org/jemmyw/Qif]
|
2
|
+
|
1
3
|
= Library for reading Quicken Interchange Format (QIF) files
|
2
4
|
|
3
5
|
* http://github.com/jemmyw/Qif
|
@@ -49,7 +51,7 @@
|
|
49
51
|
|
50
52
|
=== LICENSE:
|
51
53
|
|
52
|
-
Copyright (c)
|
54
|
+
Copyright (c) 2011 Jeremy Wells
|
53
55
|
|
54
56
|
Permission is hereby granted, free of charge, to any person
|
55
57
|
obtaining a copy of this software and associated documentation
|
data/Rakefile
CHANGED
@@ -1,12 +1,5 @@
|
|
1
|
-
require 'echoe'
|
2
1
|
require 'rspec/core/rake_task'
|
3
2
|
|
4
|
-
Echoe.new('qif') do |gem|
|
5
|
-
gem.author = "Jeremy Wells"
|
6
|
-
gem.summary = "A library for reading and writing quicken QIF files."
|
7
|
-
gem.email = "jemmyw@gmail.com"
|
8
|
-
end
|
9
|
-
|
10
3
|
desc "Run specs"
|
11
4
|
RSpec::Core::RakeTask.new :spec
|
12
5
|
|
data/lib/qif/date_format.rb
CHANGED
@@ -3,59 +3,46 @@ require 'time'
|
|
3
3
|
module Qif
|
4
4
|
class DateFormat
|
5
5
|
attr_reader :format
|
6
|
-
|
6
|
+
|
7
|
+
SUPPORTED_DATEFORMAT = {
|
8
|
+
"dd/mm/yyyy" => "%d/%m/%Y",
|
9
|
+
"d/mm/yyyy" => "%d/%m/%Y",
|
10
|
+
"dd/m/yyyy" => "%d/%m/%Y",
|
11
|
+
"d/m/yyyy" => "%d/%m/%Y",
|
12
|
+
|
13
|
+
"dd/mm/yy" => "%d/%m/%y",
|
14
|
+
"d/mm/yy" => "%d/%m/%y",
|
15
|
+
"dd/m/yy" => "%d/%m/%y",
|
16
|
+
"d/m/yy" => "%d/%m/%y",
|
17
|
+
|
18
|
+
"mm/dd/yyyy" => "%m/%d/%Y",
|
19
|
+
"m/dd/yyyy" => "%m/%d/%Y",
|
20
|
+
"mm/d/yyyy" => "%m/%d/%Y",
|
21
|
+
"m/d/yyyy" => "%m/%d/%Y",
|
22
|
+
|
23
|
+
"mm/dd/yy" => "%m/%d/%y",
|
24
|
+
"m/dd/yy" => "%m/%d/%y",
|
25
|
+
"mm/d/yy" => "%m/%d/%y",
|
26
|
+
"m/d/yy" => "%m/%d/%y",
|
27
|
+
}
|
28
|
+
|
7
29
|
def initialize(format = 'dd/mm/yyyy')
|
8
30
|
@format = format
|
9
31
|
end
|
10
|
-
|
32
|
+
|
11
33
|
def parse(date)
|
12
|
-
|
13
|
-
order = date_order
|
14
|
-
|
15
|
-
if match = regex.match(date)
|
16
|
-
year = match[order.index('y')+1].to_i
|
17
|
-
|
18
|
-
if year < 100
|
19
|
-
start_year = Time.now.year - 50
|
20
|
-
start_cent = (start_year/100).to_i*100
|
21
|
-
if year > start_year-start_cent
|
22
|
-
year += start_cent
|
23
|
-
else
|
24
|
-
year += ((Time.now.year/100).to_i*100)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
Time.mktime(year, *%w(m d).map{|t| match[order.index(t) + 1].to_i })
|
29
|
-
end
|
34
|
+
Date.strptime(date, convert_format_to_strftime)
|
30
35
|
end
|
31
|
-
|
36
|
+
|
32
37
|
def format(date)
|
33
38
|
date.strftime(convert_format_to_strftime)
|
34
39
|
end
|
35
|
-
|
40
|
+
|
36
41
|
private
|
37
|
-
|
38
|
-
def date_order
|
39
|
-
%w(d m y).sort{|a,b| @format.index(a) <=> @format.index(b) }
|
40
|
-
end
|
41
|
-
|
42
|
+
|
42
43
|
def convert_format_to_strftime
|
43
|
-
|
44
|
-
format.gsub!('dd', '%d')
|
45
|
-
format.gsub!('mm', '%m')
|
46
|
-
format.gsub!('yyyy', '%Y')
|
47
|
-
format.gsub!('yy', '%y')
|
48
|
-
format
|
49
|
-
end
|
50
|
-
|
51
|
-
def convert_format_to_regex
|
52
|
-
format = @format.dup
|
53
|
-
format.gsub!('dd', '([0-3][0-9])')
|
54
|
-
format.gsub!('mm', '([0-1][0-9])')
|
55
|
-
format.gsub!('yyyy', '([1-2][0-9]{3})')
|
56
|
-
format.gsub!('yy', '([0-9]{2})')
|
57
|
-
|
58
|
-
/#{format}/mi
|
44
|
+
SUPPORTED_DATEFORMAT[@format]
|
59
45
|
end
|
46
|
+
|
60
47
|
end
|
61
48
|
end
|
data/lib/qif/reader.rb
CHANGED
@@ -15,6 +15,8 @@ module Qif
|
|
15
15
|
# end
|
16
16
|
class Reader
|
17
17
|
include Enumerable
|
18
|
+
|
19
|
+
attr_reader :index
|
18
20
|
|
19
21
|
SUPPORTED_ACCOUNTS = {
|
20
22
|
"!Type:Bank" => "Bank account transactions",
|
@@ -31,7 +33,7 @@ module Qif
|
|
31
33
|
# either an IO object or a String containing the Qif file data.
|
32
34
|
#
|
33
35
|
# The optional format argument specifies the date format in the file.
|
34
|
-
# Giving a format will force it, otherwise the format will
|
36
|
+
# Giving a format will force it, otherwise the format will guessed
|
35
37
|
# reading the transactions in the file, this defaults to 'dd/mm/yyyy'
|
36
38
|
# if guessing method fails.
|
37
39
|
def initialize(data, format = nil)
|
@@ -78,17 +80,29 @@ module Qif
|
|
78
80
|
begin
|
79
81
|
line = @data.gets
|
80
82
|
break if line.nil?
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
83
|
+
|
84
|
+
date = line[1..-1]
|
85
|
+
guessed_format = Qif::DateFormat::SUPPORTED_DATEFORMAT.find { |format_string, format|
|
86
|
+
test_date_with_format?(date, format_string, format)
|
87
|
+
}
|
86
88
|
end until guessed_format
|
89
|
+
|
87
90
|
@data.rewind
|
88
|
-
|
91
|
+
|
92
|
+
guessed_format ? guessed_format.first : @fallback_format
|
89
93
|
end
|
90
94
|
|
91
95
|
private
|
96
|
+
|
97
|
+
def test_date_with_format?(date, format_string, format)
|
98
|
+
parsed_date = Date.strptime(date, format)
|
99
|
+
if parsed_date > Date.strptime('01/01/1900', '%d/%m/%Y')
|
100
|
+
@fallback_format ||= format_string
|
101
|
+
parsed_date.day > 12
|
102
|
+
end
|
103
|
+
rescue
|
104
|
+
false
|
105
|
+
end
|
92
106
|
|
93
107
|
def read_all_transactions
|
94
108
|
while next_transaction; end
|
@@ -4,16 +4,16 @@ describe Qif::DateFormat do
|
|
4
4
|
it 'should work with 2 digit years in mm/dd/yy format' do
|
5
5
|
reader = Qif::DateFormat.new('mm/dd/yy')
|
6
6
|
time = reader.parse('09/28/10')
|
7
|
-
time.should ==
|
7
|
+
time.should == Date.strptime('09/28/10', '%m/%d/%y')
|
8
8
|
time = reader.parse('09/28/94')
|
9
|
-
time.should ==
|
9
|
+
time.should == Date.strptime('09/28/94', '%m/%d/%y')
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'should work with 2 digit years in dd/mm/yy format' do
|
13
13
|
reader = Qif::DateFormat.new('dd/mm/yy')
|
14
14
|
time = reader.parse('28/09/10')
|
15
|
-
time.should ==
|
15
|
+
time.should == Date.strptime('28/09/10', '%d/%m/%y')
|
16
16
|
time = reader.parse('28/09/94')
|
17
|
-
time.should ==
|
17
|
+
time.should == Date.strptime('28/09/94', '%d/%m/%y')
|
18
18
|
end
|
19
19
|
end
|
data/spec/lib/reader_spec.rb
CHANGED
@@ -6,21 +6,21 @@ shared_examples_for "3 record files" do
|
|
6
6
|
end
|
7
7
|
|
8
8
|
it 'should have a debit of $10 on the 1st of January 2010' do
|
9
|
-
transaction = instance.transactions.detect{|t| t.date ==
|
9
|
+
transaction = instance.transactions.detect{|t| t.date == Date.new(2010, 1, 1)}
|
10
10
|
transaction.should_not be_nil
|
11
11
|
transaction.category.should == 'Debit'
|
12
12
|
transaction.amount.should == -10.0
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'should have a debit of $20 on the 1st of June 1020' do
|
16
|
-
transaction = instance.transactions.detect{|t| t.date ==
|
16
|
+
transaction = instance.transactions.detect{|t| t.date == Date.new(2010, 6, 1)}
|
17
17
|
transaction.should_not be_nil
|
18
18
|
transaction.category.should == 'Debit'
|
19
19
|
transaction.amount.should == -20.0
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'should have a credit of $30 on the 29th of December 2010' do
|
23
|
-
transaction = instance.transactions.detect{|t| t.date ==
|
23
|
+
transaction = instance.transactions.detect{|t| t.date == Date.new(2010, 12, 29)}
|
24
24
|
transaction.should_not be_nil
|
25
25
|
transaction.category.should == 'Credit'
|
26
26
|
transaction.amount.should == 30.0
|
@@ -46,6 +46,12 @@ describe Qif::Reader do
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
context "when format has spaces" do
|
50
|
+
it_behaves_like "3 record files" do
|
51
|
+
let(:instance) { Qif::Reader.new(open('spec/fixtures/3_records_spaced.qif').read) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
49
55
|
context "it should still work when the record header is followed by an invalid transaction terminator" do
|
50
56
|
it_behaves_like "3 record files" do
|
51
57
|
let(:instance) { Qif::Reader.new(open('spec/fixtures/3_records_invalid_header.qif'), 'dd/mm/yy') }
|
@@ -58,29 +64,30 @@ describe Qif::Reader do
|
|
58
64
|
it 'should reject the wrong file and raise an UnrecognizedData exception' do
|
59
65
|
expect{ Qif::Reader.new(open('spec/fixtures/not_a_QIF_file.txt')) }.to raise_error(Qif::Reader::UnrecognizedData)
|
60
66
|
end
|
61
|
-
it 'should guess the date format dd/mm/yyyy' do
|
62
|
-
@instance = Qif::Reader.new(open('spec/fixtures/3_records_ddmmyyyy.qif'))
|
63
|
-
@instance.guess_date_format.should == 'dd/mm/yyyy'
|
64
|
-
end
|
65
67
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
describe '#guess_date_format' do
|
69
|
+
it 'should guess the date format dd/mm/yyyy' do
|
70
|
+
@instance = Qif::Reader.new(open('spec/fixtures/3_records_ddmmyyyy.qif'))
|
71
|
+
@instance.guess_date_format.should == 'dd/mm/yyyy'
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should guess the date format mm/dd/yy' do
|
75
|
+
@instance = Qif::Reader.new(open('spec/fixtures/3_records_mmddyy.qif'))
|
76
|
+
@instance.guess_date_format.should == 'mm/dd/yy'
|
77
|
+
end
|
70
78
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
79
|
+
it 'should fall back to best guess if the date are ambiguious' do
|
80
|
+
@instance = Qif::Reader.new(open('spec/fixtures/quicken_non_investement_account.qif'))
|
81
|
+
@instance.guess_date_format.should == 'dd/mm/yy'
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should guess the date format d/m/yy' do
|
85
|
+
@instance = Qif::Reader.new(open('spec/fixtures/3_records_dmyy.qif'))
|
86
|
+
@instance.guess_date_format.should == 'dd/mm/yy'
|
87
|
+
end
|
75
88
|
end
|
76
|
-
|
77
|
-
# TODO Date parser should be more flexible and efficient, probably using Date.strptime(str, format)
|
78
|
-
# it 'should initialize if leading zeros are missing too' do
|
79
|
-
# @instance = Qif::Reader.new(open('spec/fixtures/3_records_dmyy.qif'))
|
80
|
-
# @instance.size.should == 3
|
81
|
-
# end
|
82
89
|
|
83
|
-
it 'should
|
90
|
+
it 'should parse amounts with comma separator too' do
|
84
91
|
@instance = Qif::Reader.new(open('spec/fixtures/3_records_separator.qif'))
|
85
92
|
@instance.size.should == 3
|
86
93
|
@instance.collect(&:amount).should == [-1010.0, -30020.0, 30.0]
|
data/spec/lib/writer_spec.rb
CHANGED
metadata
CHANGED
@@ -1,27 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qif
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
5
|
-
prerelease:
|
4
|
+
version: 1.1.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Jeremy Wells
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2013-12-14 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rspec
|
16
|
-
requirement:
|
17
|
-
none: false
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: 2.5.0
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
|
-
version_requirements:
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.5.0
|
25
27
|
description: A library for reading and writing quicken QIF files.
|
26
28
|
email: jemmyw@gmail.com
|
27
29
|
executables: []
|
@@ -52,6 +54,7 @@ files:
|
|
52
54
|
- spec/fixtures/3_records_mmddyy.qif
|
53
55
|
- spec/fixtures/3_records_mmddyyyy.qif
|
54
56
|
- spec/fixtures/3_records_separator.qif
|
57
|
+
- spec/fixtures/3_records_spaced.qif
|
55
58
|
- spec/fixtures/not_a_QIF_file.txt
|
56
59
|
- spec/fixtures/quicken_investment_account.qif
|
57
60
|
- spec/fixtures/quicken_non_investement_account.qif
|
@@ -63,6 +66,7 @@ files:
|
|
63
66
|
- spec/spec_helper.rb
|
64
67
|
homepage: http://qif.github.com/qif
|
65
68
|
licenses: []
|
69
|
+
metadata: {}
|
66
70
|
post_install_message:
|
67
71
|
rdoc_options:
|
68
72
|
- --line-numbers
|
@@ -74,21 +78,20 @@ rdoc_options:
|
|
74
78
|
require_paths:
|
75
79
|
- lib
|
76
80
|
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
-
none: false
|
78
81
|
requirements:
|
79
|
-
- -
|
82
|
+
- - '>='
|
80
83
|
- !ruby/object:Gem::Version
|
81
84
|
version: '0'
|
82
85
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
-
none: false
|
84
86
|
requirements:
|
85
|
-
- -
|
87
|
+
- - '>='
|
86
88
|
- !ruby/object:Gem::Version
|
87
89
|
version: '1.2'
|
88
90
|
requirements: []
|
89
91
|
rubyforge_project: qif
|
90
|
-
rubygems_version: 1.
|
92
|
+
rubygems_version: 2.1.11
|
91
93
|
signing_key:
|
92
|
-
specification_version:
|
94
|
+
specification_version: 4
|
93
95
|
summary: A library for reading and writing quicken QIF files.
|
94
96
|
test_files: []
|
97
|
+
has_rdoc:
|