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.
@@ -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
@@ -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) 2010 Jeremy Wells
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
 
@@ -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
- regex = convert_format_to_regex
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
- format = @format.dup
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
@@ -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 guissed
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
- date = line.strip.scan(/^D(\d{1,2}).(\d{1,2}).(\d{2,4})/).flatten
82
- if date.count == 3
83
- guessed_format = date[0].to_i.between?(1, 12) ? (date[1].to_i.between?(1, 12) ? nil : 'mm/dd') : 'dd/mm'
84
- guessed_format += '/' + 'y'*date[2].length if guessed_format
85
- end
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
- guessed_format
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
@@ -0,0 +1,19 @@
1
+ !Type:Bank
2
+ D1/ 1/10
3
+ T-10.00
4
+ LDebit
5
+ PDescription
6
+ MReference
7
+ ^
8
+ D6/ 1/10
9
+ T-20.00
10
+ LDebit
11
+ PDescription
12
+ MReference
13
+ ^
14
+ D12/29/10
15
+ T30.00
16
+ LCredit
17
+ PDescription
18
+ MReference
19
+ ^
@@ -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 == Time.mktime(2010, 9, 28)
7
+ time.should == Date.strptime('09/28/10', '%m/%d/%y')
8
8
  time = reader.parse('09/28/94')
9
- time.should == Time.mktime(1994, 9, 28)
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 == Time.mktime(2010, 9, 28)
15
+ time.should == Date.strptime('28/09/10', '%d/%m/%y')
16
16
  time = reader.parse('28/09/94')
17
- time.should == Time.mktime(1994, 9, 28)
17
+ time.should == Date.strptime('28/09/94', '%d/%m/%y')
18
18
  end
19
19
  end
@@ -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 == Time.mktime(2010, 1, 1)}
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 == Time.mktime(2010, 6, 1)}
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 == Time.mktime(2010, 12, 29)}
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
- it 'should guess the date format mm/dd/yy' do
67
- @instance = Qif::Reader.new(open('spec/fixtures/3_records_mmddyy.qif'))
68
- @instance.guess_date_format.should == 'mm/dd/yy'
69
- end
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
- it 'shouldn\t guess the date format because transactions are ambiguious, fall back on default dd/mm/yyyy and fail' do
72
- @instance = Qif::Reader.new(open('spec/fixtures/quicken_non_investement_account.qif'))
73
- @instance.guess_date_format.should == nil
74
- @instance.size.should == 0
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 should parse amounts with comma separator too' do
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]
@@ -11,7 +11,7 @@ describe Qif::Writer do
11
11
  describe '::open' do
12
12
  before do
13
13
  @path = '/tmp/test'
14
- File.stub!(:open).and_yield @io
14
+ File.stub(:open).and_yield @io
15
15
  end
16
16
 
17
17
  it 'should yield a Qif::Writer' do
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.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: 2011-03-07 00:00:00.000000000Z
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: &70212876609680 !ruby/object:Gem::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: *70212876609680
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.8.6
92
+ rubygems_version: 2.1.11
91
93
  signing_key:
92
- specification_version: 3
94
+ specification_version: 4
93
95
  summary: A library for reading and writing quicken QIF files.
94
96
  test_files: []
97
+ has_rdoc: