qif 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 05d7562d52dc8df636d290de9e2cce5f412a6013
4
- data.tar.gz: f47615d7c5077148c7c508bab6f14839730e49ad
3
+ metadata.gz: 08266f25e1ffcdf187a4ebb8b1962c966174cccf
4
+ data.tar.gz: f6e324f5d4ad6dd5779998a106999d7a622c124f
5
5
  SHA512:
6
- metadata.gz: 4514f7c2a458fb6989ca943a5f8050f905f81e449ced6adfd79daab2ab97bbb033cb2125f42f8e576bdec73a1a0fa08657e23b77bf91f2d9c5759f8717664973
7
- data.tar.gz: 87fe80e1d2b395d875121fdea2a3679e53269fa4b632cafcaca467c02bc89ddb43630c5fe113c34d0a694976386ccbc8ed2d89898f6006d99f167f03a4bee146
6
+ metadata.gz: 2ee86e8d5aa5128e25fe50d65ded8ea804290d8b40f7013a4d83825acbf9c4ab3ace060dc2dd4c251b0033e0cecb160bca7cf32ed1bd6ce81c2e04663663592b
7
+ data.tar.gz: 856b067eaa0618b390c903cb236eeff28790afe131abc5bf570643ef86ddda93f494e09250046cd9a55529fffcff8e25518ebd12a5ea62de8525ffc6b480f8b7
@@ -6,23 +6,23 @@
6
6
 
7
7
  === INSTALL:
8
8
 
9
- * sudo gem install qif
9
+ * gem install qif
10
10
 
11
11
  === DOCUMENTATION:
12
12
 
13
- * http://rdoc.info/github/jemmyw/Qif/master/frames
13
+ * http://www.rubydoc.info/github/jemmyw/Qif
14
14
 
15
15
  === EXAMPLE:
16
16
 
17
17
  require 'qif'
18
18
  qif = Qif::Reader.new(open('/path/to/file.qif'))
19
19
  qif.each do |transaction|
20
- puts [transaction.name, transaction.description, transaction.amount].join(", ")
20
+ puts [transaction.payee, transaction.amount].join(", ")
21
21
  end
22
22
 
23
23
  === CSV conversion example
24
24
 
25
- # Ruby 1.9.2
25
+ # Ruby 2.1.0
26
26
  require 'csv'
27
27
  require 'qif'
28
28
 
@@ -33,10 +33,10 @@
33
33
  # [3] debit amount,
34
34
  # [4] credit amount
35
35
  # Ignore other fields
36
- basefile = 'file'
37
- bank_input = CSV.read("#{file}.csv")
36
+ basefile = 'filename'
37
+ bank_input = CSV.read("#{basefile}.csv")
38
38
 
39
- Qif::Writer.open("#{file}.qif", type = 'Bank', format = 'dd/mm/yyyy') do |qif|
39
+ Qif::Writer.open("#{basefile}.qif", type = 'Bank', format = 'dd/mm/yyyy') do |qif|
40
40
  bank_input.each do |row|
41
41
  # Fix the values depending on what state your CSV data is in
42
42
  row.each { |value| value.to_s.gsub!(/^\s+|\s+$/,'') }
@@ -51,7 +51,7 @@
51
51
 
52
52
  === LICENSE:
53
53
 
54
- Copyright (c) 2011 Jeremy Wells
54
+ Copyright (c) 2014 Jeremy Wells
55
55
 
56
56
  Permission is hereby granted, free of charge, to any person
57
57
  obtaining a copy of this software and associated documentation
data/Rakefile CHANGED
@@ -1,6 +1,12 @@
1
1
  require 'rspec/core/rake_task'
2
+ require 'rdoc/task'
2
3
 
3
4
  desc "Run specs"
4
5
  RSpec::Core::RakeTask.new :spec
5
6
 
7
+ RDoc::Task.new do |rdoc|
8
+ rdoc.main = "README.rdoc"
9
+ rdoc.rdoc_files.include("README.rdoc", "lib/**/*.rb")
10
+ end
11
+
6
12
  task :default => :spec
data/lib/qif.rb CHANGED
@@ -1,5 +1,11 @@
1
1
  $: << File.dirname(__FILE__)
2
2
 
3
+ #
4
+ # See Qif::Reader for reading qif files and Qif::Writer for writing them.
5
+ #
6
+ module Qif
7
+ end
8
+
3
9
  require 'qif/reader'
4
10
  require 'qif/writer'
5
- require 'qif/transaction'
11
+ require 'qif/transaction'
@@ -0,0 +1,6 @@
1
+ module AmountParser
2
+ def self.parse(amount)
3
+ warn "= amounts are unsupported" if amount =~ /^=/
4
+ amount.gsub(/,/, '').gsub(/^=/, '').to_f
5
+ end
6
+ end
@@ -1,6 +1,7 @@
1
1
  require 'stringio'
2
2
  require 'qif/date_format'
3
3
  require 'qif/transaction'
4
+ require 'qif/transaction/builder'
4
5
 
5
6
  module Qif
6
7
  # The Qif::Reader class reads a qif file and provides access to
@@ -152,9 +153,8 @@ module Qif
152
153
  end
153
154
 
154
155
  def read_transaction
155
- if record = read_record
156
- transaction = Transaction.read(record)
157
- cache_transaction(transaction) if transaction
156
+ if transaction = read_record
157
+ cache_transaction(transaction)
158
158
  end
159
159
  end
160
160
 
@@ -163,17 +163,27 @@ module Qif
163
163
  end
164
164
 
165
165
  def read_record
166
- record = {}
166
+ builder = Qif::Transaction::Builder.new(->(dt){@format.parse(dt)})
167
167
  begin
168
- line = @data.readline
169
- key = line[0,1]
170
- record[key] = record.key?(key) ? record[key] + "\n" + line[1..-1].strip : line[1..-1].strip
171
-
172
- record[key].sub!(',','') if %w(T U $).include? key
173
- record[key] = @format.parse(record[key]) if %w(D).include? key
174
-
175
- end until line =~ /^\^/
176
- record
168
+ line = @data.readline.strip
169
+ key = line.slice!(0, 1)
170
+ builder =
171
+ case key
172
+ when 'D' then builder.set_date(line)
173
+ when 'T' then builder.set_amount(line)
174
+ when 'A' then builder.set_address(line)
175
+ when 'C' then builder.set_status(line)
176
+ when 'N' then builder.set_number(line)
177
+ when 'P' then builder.set_payee(line)
178
+ when 'M' then builder.set_memo(line)
179
+ when 'L' then builder.set_category(line)
180
+ when 'S' then builder.add_split(line)
181
+ when 'E' then builder.set_split_memo(line)
182
+ when '$' then builder.set_split_amount(line)
183
+ else builder
184
+ end
185
+ end until key == "^"
186
+ builder.build
177
187
  rescue EOFError => e
178
188
  @data.close
179
189
  nil
@@ -12,9 +12,6 @@ module Qif
12
12
  :memo => {"M" => "Memo" },
13
13
  :adress => {"A" => "Address (up to five lines; the sixth line is an optional message)" },
14
14
  :category => {"L" => "Category (Category/Subcategory/Transfer/Class)" },
15
- :split_category => {"S" => "Category in split (Category/Transfer/Class)" },
16
- :split_memo => {"E" => "Memo in split" },
17
- :split_amount => {"$" => "Dollar amount of split" },
18
15
  :end => {"^" => "End of entry" }
19
16
  }
20
17
  DEPRECATION_FIELDS = {
@@ -24,18 +21,20 @@ module Qif
24
21
  }
25
22
  SUPPORTED_FIELDS.keys.each{|s| attr_accessor s}
26
23
 
27
- def self.read(record) #::nodoc
28
- return nil unless record['D'].respond_to?(:strftime)
29
- SUPPORTED_FIELDS.each{|k,v| record[k] = record.delete(v.keys.first)}
30
- record.reject{|k,v| v.nil?}.each{|k,v| record[k] = record[k].to_f if k.to_s.include? "amount"}
31
- Transaction.new record
32
- end
24
+ attr_reader :splits
25
+ alias :address :adress
26
+ alias :address= :adress=
33
27
 
34
28
  def initialize(attributes = {})
29
+ @splits = []
35
30
  deprecate_attributes!(attributes)
36
31
  SUPPORTED_FIELDS.keys.each{|s| instance_variable_set("@#{s.to_s}", attributes[s])}
37
32
  end
38
33
 
34
+ def add_split(split)
35
+ @splits << split
36
+ end
37
+
39
38
  # Returns a representation of the transaction as it
40
39
  # would appear in a qif file.
41
40
  def to_s(format = 'dd/mm/yyyy')
@@ -52,7 +51,7 @@ module Qif
52
51
  else
53
52
  "#{field}#{current}"
54
53
  end
55
- end.flatten.compact.join("\n")
54
+ end.concat(@splits.collect{|s| s.to_s}).flatten.compact.join("\n")
56
55
  end
57
56
 
58
57
  private
@@ -0,0 +1,61 @@
1
+ require_relative "split/builder"
2
+ require_relative "builderable"
3
+ require_relative "../amount_parser"
4
+
5
+ #
6
+ # Factory class for building transactions.
7
+ #
8
+ # @usage
9
+ # txn = Qif::Transaction::Builder.new
10
+ # .set_date('10/06/1983')
11
+ # .set_amount('-10.0')
12
+ # .set_memo('debit $10')
13
+ # .add_split('jules')
14
+ # .set_split_memo('half to jules')
15
+ # .add_split_amount('-5.0')
16
+ # .build
17
+ #
18
+ class Qif::Transaction::Builder
19
+ include Builderable
20
+
21
+ def initialize(date_parser = ->(date) { Time.parse(date) })
22
+ @date_parser = date_parser
23
+ @splits = []
24
+ end
25
+
26
+ set_builder_method :date, :parse_date
27
+ set_builder_method :amount, ->(amt) { AmountParser.parse(amt) }
28
+ set_builder_method :status
29
+ set_builder_method :number
30
+ set_builder_method :payee
31
+ set_builder_method :memo
32
+ set_builder_method :category
33
+
34
+ def set_address(address)
35
+ @address = [@address, address].compact.join("\n")
36
+ self
37
+ end
38
+ alias :set_adress :set_address
39
+
40
+ def add_split(split)
41
+ Qif::Transaction::Split::Builder.new(self).set_split_category(split).tap do |builder|
42
+ @splits << builder
43
+ end
44
+ end
45
+
46
+ def build
47
+ _build(Qif::Transaction.new).tap do |txn|
48
+ txn.address = @address
49
+
50
+ @splits.each do |split_builder|
51
+ txn.add_split(split_builder.build_split)
52
+ end
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def parse_date(date)
59
+ @date_parser.call(date)
60
+ end
61
+ end
@@ -0,0 +1,39 @@
1
+ module Builderable
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ end
5
+
6
+ module ClassMethods
7
+ def builder_options(options)
8
+ @options = options
9
+ end
10
+
11
+ def set_builder_method(attribute, massager = nil)
12
+ options = @options || {}
13
+ method_name = ["set", options[:prefix], attribute].compact.join("_")
14
+
15
+ define_method(method_name) do |new_value|
16
+ @_builder_values ||= {}
17
+
18
+ unless massager.nil?
19
+ if massager.kind_of?(Symbol)
20
+ new_value = self.send(massager, new_value)
21
+ else
22
+ new_value = massager.call(new_value)
23
+ end
24
+ end
25
+
26
+ @_builder_values[attribute] = new_value
27
+ self
28
+ end
29
+ end
30
+ end
31
+
32
+ def _build(object)
33
+ (@_builder_values||{}).each do |attribute, value|
34
+ object.public_send("#{attribute}=", value)
35
+ end
36
+
37
+ object
38
+ end
39
+ end
@@ -0,0 +1,26 @@
1
+ class Qif::Transaction::Split
2
+ SUPPORTED_FIELDS = {
3
+ :category => {"S" => "Category in split (Category/Transfer/Class)" },
4
+ :memo => {"E" => "Memo in split" },
5
+ :amount => {"$" => "Dollar amount of split" },
6
+ }
7
+
8
+ attr_accessor :memo, :amount, :category
9
+
10
+ # Returns a representation of the split as it
11
+ # would appear in a qif file.
12
+ def to_s()
13
+ SUPPORTED_FIELDS.collect do |k,v|
14
+ next unless current = instance_variable_get("@#{k}")
15
+ field = v.keys.first
16
+ case current.class.to_s
17
+ when "Float"
18
+ "#{field}#{'%.2f'%current}"
19
+ when "String"
20
+ current.split("\n").collect {|x| "#{field}#{x}" }
21
+ else
22
+ "#{field}#{current}"
23
+ end
24
+ end.flatten.compact.join("\n")
25
+ end
26
+ end
@@ -0,0 +1,32 @@
1
+ require_relative '../builderable'
2
+ require_relative '../split'
3
+
4
+ #
5
+ # Factory class for buliding splits. Do not construct this directly, rather use
6
+ # Qif::Transaction::Builder#add_split
7
+ #
8
+ class Qif::Transaction::Split::Builder
9
+ include Builderable
10
+
11
+ def initialize(transaction_builder)
12
+ @transaction_builder = transaction_builder
13
+ yield self if block_given?
14
+ end
15
+
16
+ def add_split(split_memo)
17
+ @transaction_builder.add_split(split_memo)
18
+ end
19
+
20
+ builder_options prefix: 'split'
21
+ set_builder_method :memo
22
+ set_builder_method :amount, ->(amt) { AmountParser.parse(amt) }
23
+ set_builder_method :category
24
+
25
+ def build_split
26
+ _build(Qif::Transaction::Split.new)
27
+ end
28
+
29
+ def method_missing(name, *args, &block)
30
+ @transaction_builder.send(name, *args, &block)
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ !Type:Bank
2
+ D6/1/94
3
+ T-1,000.00
4
+ CX
5
+ N1005
6
+ PBank Of Mortgage
7
+ MMemo
8
+ L[linda]
9
+ S[steve]
10
+ ECash
11
+ $-253.64
12
+ SMort Int
13
+ $=746.36
14
+ ^
15
+ D6/2/94
16
+ Scategory 1
17
+ $23
18
+ T75.00
19
+ PDeposit
20
+ ^
@@ -107,4 +107,42 @@ describe Qif::Reader do
107
107
  @instance = Qif::Reader.new(open('spec/fixtures/3_records_ddmmyyyy.qif'), 'mm/dd/yyyy')
108
108
  @instance.size.should == 2
109
109
  end
110
+
111
+ context 'when reading splits' do
112
+ let(:reader) { Qif::Reader.new(open('spec/fixtures/splits.qif'), 'd/m/yyyy') }
113
+
114
+ context 'the first transaction' do
115
+ let (:transaction) { reader.transactions[0] }
116
+
117
+ it 'should have the correct number of splits' do
118
+ expect(transaction.splits.size).to eq(2)
119
+ end
120
+
121
+ context 'the first split' do
122
+ let (:split) { transaction.splits.first }
123
+
124
+ it { expect(split.category).to eq("[steve]") }
125
+ it { expect(split.memo).to eq("Cash") }
126
+ it { expect(split.amount).to eq(-253.64)}
127
+ end
128
+
129
+ context 'the second split' do
130
+ let (:split) { transaction.splits[1] }
131
+
132
+ it { expect(split.category).to eq("Mort Int") }
133
+ it { expect(split.amount).to eq(746.36) }
134
+ end
135
+
136
+ end
137
+
138
+ context "when the transaction has splits first" do
139
+ let(:transaction) { reader.transactions[1] }
140
+
141
+ it 'should correctly add the amount to the transaction' do
142
+ expect(transaction.amount).to eq(75)
143
+ end
144
+
145
+ it { expect(transaction.splits.first.amount).to eq(23) }
146
+ end
147
+ end
110
148
  end
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+ require 'qif/transaction/builder'
3
+ require 'qif/transaction/split/builder'
4
+
5
+ describe Qif::Transaction::Builder do
6
+ let(:builder) { Qif::Transaction::Builder.new }
7
+ def split_builder
8
+ double(Qif::Transaction::Split::Builder).tap do |b|
9
+ allow(b).to receive(:set_split_category).and_return(b)
10
+ end
11
+ end
12
+ def do_build
13
+ builder.build
14
+ end
15
+
16
+ context '#build' do
17
+ it 'should return a transaction object' do
18
+ expect(builder.build).to be_kind_of(Qif::Transaction)
19
+ end
20
+
21
+ context 'with splits' do
22
+ it 'should call build_split on each of the splits' do
23
+ sb1 = split_builder
24
+ sb2 = split_builder
25
+ allow(Qif::Transaction::Split::Builder).to receive(:new).and_return(sb1, sb2)
26
+ expect(sb1).to receive(:build_split).ordered
27
+ expect(sb2).to receive(:build_split).ordered
28
+ builder.add_split("a")
29
+ builder.add_split("b")
30
+ builder.build
31
+ end
32
+
33
+ it 'should add the splits to the transaction' do
34
+ builder.add_split("a")
35
+ builder.add_split("b")
36
+ expect(builder.build.splits.count).to eq(2)
37
+ end
38
+ end
39
+ end
40
+
41
+ context '#set_date' do
42
+ it_should_behave_like 'builder method', :date, '1994-06-01', :set_date, Time.mktime(1994, 6, 1)
43
+ it 'should use a given date parser' do
44
+ b = Qif::Transaction::Builder.new(->(date) { date })
45
+ b.set_date('1994')
46
+ expect(b.build.date).to eq('1994')
47
+ end
48
+ end
49
+
50
+ context '#set_amount' do
51
+ it_should_behave_like 'builder method', :amount, '-1000.00', :set_amount, -1000.0
52
+ it 'should parse commas out of the amount' do
53
+ builder.set_amount('1,000,000')
54
+ expect(builder.build.amount).to eq(1_000_000)
55
+ end
56
+ end
57
+
58
+ context '#set_status' do
59
+ it_should_behave_like 'builder method', :status, 'X', :set_status, 'X'
60
+ end
61
+
62
+ context '#set_number' do
63
+ it_should_behave_like 'builder method', :number, '1005', :set_number, '1005'
64
+ end
65
+
66
+ context '#set_payee' do
67
+ it_should_behave_like 'builder method', :payee, 'Bank of Mortgage', :set_payee, 'Bank of Mortgage'
68
+ end
69
+
70
+ context '#set_memo' do
71
+ it_should_behave_like 'builder method', :memo, 'Some stuff that happened', :set_memo, 'Some stuff that happened'
72
+ end
73
+
74
+ context '#set_category' do
75
+ it_should_behave_like 'builder method', :category, 'Fishing', :set_category, 'Fishing'
76
+ end
77
+
78
+ context '#set_address' do
79
+ address = <<-EOA
80
+ P.O. Box 1234
81
+ Somewhereton
82
+ 12345
83
+ EOA
84
+
85
+ it_should_behave_like 'builder method', :adress, address, :set_adress, address
86
+
87
+ context 'when called consecutively' do
88
+ it "should append to the address" do
89
+ builder
90
+ .set_address("Line 1")
91
+ .set_address("Line 2")
92
+ expect(do_build.address).to eq("Line 1\nLine 2")
93
+ end
94
+ end
95
+ end
96
+
97
+ context "#add_split" do
98
+ it 'should create a new split builder' do
99
+ expect(Qif::Transaction::Split::Builder).to receive(:new).with(builder).and_return(split_builder)
100
+ builder.add_split("aaa")
101
+ end
102
+
103
+ it 'should set the category on the split builder' do
104
+ sb = split_builder
105
+ allow(Qif::Transaction::Split::Builder).to receive(:new).and_return(sb)
106
+ expect(sb).to receive(:set_split_category).with('aaaa')
107
+ builder.add_split('aaaa')
108
+ end
109
+
110
+ it "should return a split builder" do
111
+ expect(builder.add_split('aaaa')).to be_kind_of(Qif::Transaction::Split::Builder)
112
+ end
113
+
114
+ context "when called multiple times" do
115
+ it "should return multiple different builders" do
116
+ sb1 = builder.add_split("aaaa")
117
+ expect(builder.add_split("bbbb")).to_not eq(sb1)
118
+ end
119
+ end
120
+ end
121
+
122
+ end
@@ -1,76 +1,59 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Qif::Transaction do
4
- describe '::read' do
5
- it 'should return a new transaction with all attributes set' do
6
- t = Qif::Transaction.read(
7
- 'D' => Time.parse('1994-06-01'),
8
- 'T' => '-1000.00'.to_f,
9
- 'C' => 'X',
10
- 'N' => '1005',
11
- 'P' => 'Bank Of Mortgage',
12
- 'M' => 'aMemo',
13
- 'L' => 'aCategory',
14
- # TODO Support correctly splits with an array of hash
15
- # 'S' => '[linda]
16
- #Mort Int',
17
- # 'E' => 'Cash',
18
- # '$' => '-253.64
19
- #=746.36',
20
- 'A' => 'P.O. Box 27027
21
- Tucson, AZ
22
- 85726',
23
- '^' => nil
24
- )
25
-
26
- t.should be_a(Qif::Transaction)
27
- t.date.should == Time.mktime(1994,6,1)
28
- t.amount.should == -1000.00
29
- t.status.should == 'X'
30
- t.number.should == '1005'
31
- t.payee.should == 'Bank Of Mortgage'
32
- t.memo.should == 'aMemo'
33
- t.category.should == 'aCategory'
34
- t.adress.should == 'P.O. Box 27027
35
- Tucson, AZ
36
- 85726'
37
- end
38
-
39
- it 'should return nil if the date does not respond to strftime' do
40
- Qif::Transaction.read('D' => 'hello').should be_nil
41
- end
42
- end
43
-
44
4
  describe '#to_s' do
45
- before do
46
- @instance = Qif::Transaction.new(
47
- :date => Time.mktime(2010, 1, 2),
48
- :amount => -10.0,
49
- :category => 'Debit',
50
- :memo => 'Supermarket',
51
- :payee => 'abcde'
52
- )
5
+ let(:builder) do
6
+ Qif::Transaction::Builder.new
7
+ .set_date(Time.mktime(2010, 1, 2).to_s)
8
+ .set_amount('-10.0')
9
+ .set_category('Debit')
10
+ .set_memo('Supermarket')
11
+ .set_payee('abcde')
53
12
  end
54
-
13
+
14
+ let(:txn) do
15
+ builder.build
16
+ end
17
+
18
+ subject { txn.to_s }
19
+
55
20
  it 'should format the date in the format specified as D' do
56
- @instance.to_s('dd/mm/yyyy').should include('D02/01/2010')
57
- @instance.to_s('mm/dd/yyyy').should include('D01/02/2010')
21
+ expect(txn.to_s('dd/mm/yyyy')).to include('D02/01/2010')
22
+ expect(txn.to_s('mm/dd/yyyy')).to include('D01/02/2010')
58
23
  end
59
24
 
60
25
  it 'should put the amount in T' do
61
- @instance.to_s.should include('T-10.00')
26
+ expect(subject).to include('T-10.00')
62
27
  end
63
28
 
64
29
  it 'should put the category in L' do
65
- @instance.to_s.should include('LDebit')
30
+ expect(subject).to include('LDebit')
66
31
  end
67
32
 
68
33
  it 'should put the description in M' do
69
- @instance.to_s.should include('MSupermarket')
34
+ expect(subject).to include('MSupermarket')
70
35
  end
71
36
 
72
37
  it 'should put the reference in P' do
73
- @instance.to_s.should include('Pabcde')
38
+ expect(subject).to include('Pabcde')
39
+ end
40
+
41
+ it 'adds splits to the output' do
42
+ builder.add_split('split_1')
43
+ .set_split_memo('test split 1')
44
+ .set_split_amount('-5.0')
45
+ .add_split('split_2')
46
+ .set_split_memo('test split 2')
47
+ .set_split_amount('-5.0')
48
+
49
+ expect(subject).to include(<<SPLIT.strip)
50
+ Ssplit_1
51
+ Etest split 1
52
+ $-5.00
53
+ Ssplit_2
54
+ Etest split 2
55
+ $-5.0
56
+ SPLIT
74
57
  end
75
58
  end
76
59
  end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+ require 'qif/transaction/split/builder'
3
+
4
+ describe Qif::Transaction::Split::Builder do
5
+ let(:parent) { double(Qif::Transaction::Builder) }
6
+ let(:builder) { Qif::Transaction::Split::Builder.new(parent) }
7
+ def do_build
8
+ builder.build_split
9
+ end
10
+
11
+ context '#add_split' do
12
+ it 'should call add_split on the parent' do
13
+ expect(parent).to receive(:add_split).with("12345")
14
+ described_class.new(parent).add_split("12345")
15
+ end
16
+
17
+ it 'should return the new split builder' do
18
+ new_builder = double("split-builder")
19
+ allow(parent).to receive(:add_split).and_return(new_builder)
20
+ expect(described_class.new(parent).add_split(anything)).to eq(new_builder)
21
+ end
22
+ end
23
+
24
+ context '#set_memo' do
25
+ it_should_behave_like "builder method", :memo, 'Split', :set_split_memo
26
+ end
27
+
28
+ context '#set_amount' do
29
+ it_should_behave_like "builder method", :amount, '-10,000', :set_split_amount, -10000
30
+ end
31
+
32
+ context '#set_category' do
33
+ it_should_behave_like 'builder method', :category, '[Cash]', :set_split_category
34
+ end
35
+
36
+ context "when receiving an unknown message" do
37
+ it "should pass the message to the parent" do
38
+ expect(parent).to receive(:set_date).with('2012-12-31')
39
+ builder.set_date('2012-12-31')
40
+ end
41
+
42
+ it 'should return the result of the message' do
43
+ allow(parent).to receive(:set_date).and_return(parent)
44
+ expect(builder.set_date(anything)).to eq(parent)
45
+ end
46
+ end
47
+ end
@@ -2,98 +2,94 @@ require 'spec_helper'
2
2
  require 'stringio'
3
3
 
4
4
  describe Qif::Writer do
5
- before do
6
- @buffer = ''
7
- @io = StringIO.new(@buffer)
8
- @instance = Qif::Writer.new(@io)
9
- end
5
+ let(:buffer) { "" }
6
+ let(:io) { StringIO.new(buffer) }
7
+ let(:instance) { Qif::Writer.new(io) }
8
+ let(:path) { "/tmp/test" }
10
9
 
11
10
  describe '::open' do
12
11
  before do
13
- @path = '/tmp/test'
14
- File.stub(:open).and_yield @io
12
+ File.stub(:open).and_yield io
15
13
  end
16
14
 
17
15
  it 'should yield a Qif::Writer' do
18
16
  ran = false
19
- Qif::Writer.open(@path) do |writer|
17
+ Qif::Writer.open(path) do |writer|
20
18
  ran = true
21
19
  writer.should be_a(Qif::Writer)
22
20
  end
23
- ran.should be_true
21
+ ran.should eq true
24
22
  end
25
23
 
26
24
  it 'should write the transactions' do
27
25
  date = Time.now
28
26
 
29
- Qif::Writer.open(@path) do |writer|
27
+ Qif::Writer.open(path) do |writer|
30
28
  writer << Qif::Transaction.new(:date => date, :amount => 10.0, :category => 'Credit')
31
29
  end
32
30
 
33
- @buffer.should include('D%s' % date.strftime('%d/%m/%Y'))
34
- @buffer.should include('T10.0')
35
- @buffer.should include('LCredit')
31
+ buffer.should include('D%s' % date.strftime('%d/%m/%Y'))
32
+ buffer.should include('T10.0')
33
+ buffer.should include('LCredit')
36
34
  end
37
35
 
38
36
  it 'should perform a File.open on the given path' do
39
- File.should_receive(:open).with(@path, 'w')
40
- Qif::Writer.open(@path) do |writer|
37
+ File.should_receive(:open).with(path, 'w')
38
+ Qif::Writer.open(path) do |writer|
41
39
  end
42
40
  end
43
41
  end
42
+
44
43
  describe '::new' do
45
44
  it 'should yield a Qif::Writer' do
46
45
  ran = false
47
- Qif::Writer.new(@io) do |writer|
46
+ Qif::Writer.new(io) do |writer|
48
47
  ran = true
49
48
  writer.should be_a(Qif::Writer)
50
49
  end
51
- ran.should be_true
50
+ expect(ran).to eq true
52
51
  end
53
52
 
54
53
  it 'should write the transactions' do
55
54
  date = Time.now
56
55
 
57
- Qif::Writer.new(@io) do |writer|
56
+ Qif::Writer.new(io) do |writer|
58
57
  writer << Qif::Transaction.new(:date => date, :amount => 10.0, :category => 'Credit')
59
58
  end
60
59
 
61
- @buffer.should include('D%s' % date.strftime('%d/%m/%Y'))
62
- @buffer.should include('T10.0')
63
- @buffer.should include('LCredit')
60
+ buffer.should include('D%s' % date.strftime('%d/%m/%Y'))
61
+ buffer.should include('T10.0')
62
+ buffer.should include('LCredit')
64
63
  end
65
64
 
66
65
  it 'should perform a File.open on the given path' do
67
- File.should_receive(:open).with(@path, 'w')
68
- Qif::Writer.open(@path) do |writer|
66
+ File.should_receive(:open).with(path, 'w')
67
+ Qif::Writer.open(path) do |writer|
69
68
  end
70
69
  end
71
70
  end
72
71
 
73
-
74
-
75
-
76
-
77
72
  describe '#write' do
73
+ let(:date) { Time.now }
74
+
78
75
  it 'should write the header' do
79
- @instance.write
80
- @buffer.should include("!Type:Bank\n")
76
+ instance.write
77
+ buffer.should include("!Type:Bank\n")
81
78
  end
82
79
 
83
80
  it 'should write any pending transactions' do
84
- date = Time.now
85
- @instance << Qif::Transaction.new(:date => date, :amount => 10.0, :category => 'Credit')
81
+ instance << Qif::Transaction.new(:date => date, :amount => 10.0, :category => 'Credit')
86
82
 
87
- @buffer.should_not include('D%s' % date.strftime('%d/%m/%Y'))
88
- @instance.write
89
- @buffer.should include('D%s' % date.strftime('%d/%m/%Y'))
83
+ buffer.should_not include('D%s' % date.strftime('%d/%m/%Y'))
84
+ instance.write
85
+ buffer.should include('D%s' % date.strftime('%d/%m/%Y'))
90
86
  end
91
87
  end
92
88
 
93
89
  describe '#close' do
94
90
  it 'should close the io stream' do
95
- @io.should_receive(:close)
96
- @instance.close
91
+ io.should_receive(:close)
92
+ instance.close
97
93
  end
98
94
  end
99
95
  end
@@ -1 +1,3 @@
1
- require File.dirname(__FILE__) + '/../lib/qif'
1
+ require File.dirname(__FILE__) + '/../lib/qif'
2
+
3
+ require_relative "support/shared/builder_method"
@@ -0,0 +1,14 @@
1
+ shared_context 'builder method' do |attribute, input, method = nil, expected = nil|
2
+ expected = input if expected.nil?
3
+ method = "set_#{attribute}" if method.nil?
4
+
5
+ it 'and set the #{attribute} on the transaction' do
6
+ builder.send(method, input)
7
+ expect(do_build.send(attribute)).to eq(expected)
8
+ end
9
+
10
+ it 'and return the builder' do
11
+ expect(builder.send(method, input)).to be_kind_of described_class
12
+ end
13
+ end
14
+
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qif
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Wells
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-14 00:00:00.000000000 Z
11
+ date: 2016-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 2.5.0
19
+ version: 3.0.0
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 2.5.0
26
+ version: 3.0.0
27
27
  description: A library for reading and writing quicken QIF files.
28
28
  email: jemmyw@gmail.com
29
29
  executables: []
@@ -38,15 +38,20 @@ extra_rdoc_files:
38
38
  - lib/qif/transaction.rb
39
39
  - lib/qif/writer.rb
40
40
  files:
41
- - README.rdoc
42
- - LICENSE
43
41
  - CHANGELOG
42
+ - LICENSE
43
+ - README.rdoc
44
44
  - Rakefile
45
+ - lib/qif.rb
46
+ - lib/qif/amount_parser.rb
45
47
  - lib/qif/date_format.rb
46
48
  - lib/qif/reader.rb
47
49
  - lib/qif/transaction.rb
50
+ - lib/qif/transaction/builder.rb
51
+ - lib/qif/transaction/builderable.rb
52
+ - lib/qif/transaction/split.rb
53
+ - lib/qif/transaction/split/builder.rb
48
54
  - lib/qif/writer.rb
49
- - lib/qif.rb
50
55
  - spec/fixtures/3_records_ddmmyy.qif
51
56
  - spec/fixtures/3_records_ddmmyyyy.qif
52
57
  - spec/fixtures/3_records_dmyy.qif
@@ -58,38 +63,42 @@ files:
58
63
  - spec/fixtures/not_a_QIF_file.txt
59
64
  - spec/fixtures/quicken_investment_account.qif
60
65
  - spec/fixtures/quicken_non_investement_account.qif
66
+ - spec/fixtures/splits.qif
61
67
  - spec/lib/date_format_spec.rb
62
68
  - spec/lib/reader_spec.rb
69
+ - spec/lib/transaction_builder_spec.rb
63
70
  - spec/lib/transaction_spec.rb
71
+ - spec/lib/transaction_split_builder_spec.rb
64
72
  - spec/lib/writer_spec.rb
65
73
  - spec/spec.opts
66
74
  - spec/spec_helper.rb
75
+ - spec/support/shared/builder_method.rb
67
76
  homepage: http://qif.github.com/qif
68
77
  licenses: []
69
78
  metadata: {}
70
79
  post_install_message:
71
80
  rdoc_options:
72
- - --line-numbers
73
- - --inline-source
74
- - --title
81
+ - "--line-numbers"
82
+ - "--inline-source"
83
+ - "--title"
75
84
  - Qif
76
- - --main
85
+ - "--main"
77
86
  - README.rdoc
78
87
  require_paths:
79
88
  - lib
80
89
  required_ruby_version: !ruby/object:Gem::Requirement
81
90
  requirements:
82
- - - '>='
91
+ - - ">="
83
92
  - !ruby/object:Gem::Version
84
93
  version: '0'
85
94
  required_rubygems_version: !ruby/object:Gem::Requirement
86
95
  requirements:
87
- - - '>='
96
+ - - ">="
88
97
  - !ruby/object:Gem::Version
89
98
  version: '1.2'
90
99
  requirements: []
91
100
  rubyforge_project: qif
92
- rubygems_version: 2.1.11
101
+ rubygems_version: 2.4.5.1
93
102
  signing_key:
94
103
  specification_version: 4
95
104
  summary: A library for reading and writing quicken QIF files.