cashflow 0.0.1
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/.gitignore +17 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +1 -0
- data/bin/cashflow +16 -0
- data/cashflow.gemspec +28 -0
- data/db/cashflow.db +0 -0
- data/lib/cashflow/cli.rb +93 -0
- data/lib/cashflow/patches.rb +21 -0
- data/lib/cashflow/report.rb +112 -0
- data/lib/cashflow/transaction.rb +60 -0
- data/lib/cashflow/version.rb +3 -0
- data/lib/cashflow.rb +49 -0
- data/log/cashflow.log +0 -0
- data/screenshot.png +0 -0
- metadata +147 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 576c6d93c4d566d3524dd76bf75aff69239aaa3f
|
4
|
+
data.tar.gz: ffc085bdaf1a9d72824f2bc87528311723dc5449
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 84adc6c6a09b7f9825e46443013e1658ea66f174e2147277445e6dcb80a62236ba790dccba2dc709e7725c641f9ebcf52b50bc56290ea2c81629c1c76a0d6b7b
|
7
|
+
data.tar.gz: cd3096ba3cab1d2bb8ddfe1be8f21c5e5f44ab51ba926c63e09e04a12feab8cfb4a95c7855bd33794ea5326e4768ac81b8ff2267270f1b3015ad0a5e6799f3c3
|
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 2.0.0@cashflow --create
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Mike Fulcher
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
Cashflow
|
2
|
+
--------
|
3
|
+
|
4
|
+
**Compare your latest income & expenses to your average.**
|
5
|
+
|
6
|
+
Cashflow is a simple command-line application written in Ruby using [Thor](https://github.com/wycats/thor). It's purpose is simple: to analyse your bank transactions (collecting only the _date_ and _amount_) in order to build an average if your income and expenses over the last few months and compare these with the income and expenses for the current month.
|
7
|
+
|
8
|
+
Only transactions up to the current day of the month are included, so the app gives you good insight into your financial position at this exact point in the month - extremely useful when you have regular monthly transactions such as rent and salary.
|
9
|
+
|
10
|
+
By default, only transactions from 4 months prior are factored into the calculations (or as far back as the stored transactions go), but this is configurable.
|
11
|
+
|
12
|
+
Usage
|
13
|
+
-----
|
14
|
+
|
15
|
+
Basic useage is simple: you load a set of transactions (in OFX format), then print a report.
|
16
|
+
|
17
|
+

|
18
|
+
|
19
|
+
Commands
|
20
|
+
--------
|
21
|
+
|
22
|
+
```
|
23
|
+
cashflow help # Show this help message
|
24
|
+
cashflow help [COMMAND] # Describe available commands or one specific command
|
25
|
+
cashflow load PATH # Loads the OFX file at the specified PATH
|
26
|
+
cashflow report # Analyses the stored transactions and prints a comparison report
|
27
|
+
cashflow flush # Flushes the database, removing all stored transactions
|
28
|
+
cashflow version # Prints the current version number
|
29
|
+
```
|
30
|
+
|
31
|
+
**`cashflow report`** takes a single optional parameter. You can specify the date range for the transactions to compare against by passing "-s" or "--span" followed by a number repensenting the number of months to use.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/cashflow
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Add load path and require the module.
|
4
|
+
lib = File.expand_path('../../lib', __FILE__)
|
5
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
|
+
require 'cashflow'
|
7
|
+
|
8
|
+
# Quick check for shortcuts to the "help"
|
9
|
+
# and "version" commands.
|
10
|
+
if %w(-v --version).include?(ARGV[0])
|
11
|
+
Cashflow::Cli.new.version
|
12
|
+
elsif %w(-h --help).include?(ARGV[0])
|
13
|
+
Cashflow::Cli.new.help
|
14
|
+
else
|
15
|
+
Cashflow::Cli.start
|
16
|
+
end
|
data/cashflow.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cashflow/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cashflow"
|
8
|
+
spec.version = Cashflow::VERSION
|
9
|
+
spec.authors = ["Mike Fulcher"]
|
10
|
+
spec.email = ["mike@drawingablank.me"]
|
11
|
+
spec.description = %q{Compares your latest income & expense to your average, giving you a quick at-a-glance view of your financial position.}
|
12
|
+
spec.summary = %q{Compares your latest income & expense to your average.}
|
13
|
+
spec.homepage = "http://github.com/6twenty/cashflow"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
|
24
|
+
spec.add_dependency "iconv"
|
25
|
+
spec.add_dependency "sqlite3"
|
26
|
+
spec.add_dependency "thor"
|
27
|
+
spec.add_dependency "ofx"
|
28
|
+
end
|
data/db/cashflow.db
ADDED
Binary file
|
data/lib/cashflow/cli.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
module Cashflow
|
2
|
+
class Cli < ::Thor
|
3
|
+
include Thor::Actions
|
4
|
+
|
5
|
+
desc "version", "Prints the current version number"
|
6
|
+
def version
|
7
|
+
say("Cashflow v#{Cashflow::VERSION}", :green)
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "load PATH", "Loads the OFX file at the specified PATH"
|
11
|
+
def load(ofx_file_path)
|
12
|
+
map_transactions(OFX(ofx_file_path).account.transactions).each(&:save)
|
13
|
+
say("Successfully loaded the transactions from '#{ofx_file_path}'", :green)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Option to factor only up to the day of the month.
|
17
|
+
desc "report", "Analyses the stored transactions and prints a comparison report"
|
18
|
+
option :span, :type => :numeric, :aliases => "-s", :default => 4
|
19
|
+
def report
|
20
|
+
# Build report.
|
21
|
+
report = Report.new(options)
|
22
|
+
report.build_report
|
23
|
+
|
24
|
+
# Define columns widths.
|
25
|
+
cols = "%-11s %-11s %-11s %-11s\n"
|
26
|
+
|
27
|
+
# Intro.
|
28
|
+
say("\n")
|
29
|
+
say("Using transactions up to and including the #{Date.today.day.ordinalize} of the month")
|
30
|
+
say("Transactions begin #{report.start.strftime('%B')} #{report.start.day.ordinalize}, #{report.start.strftime('%Y')}")
|
31
|
+
say("\n")
|
32
|
+
|
33
|
+
# Table header.
|
34
|
+
printf(cols, "", "Average", "This month", "Difference")
|
35
|
+
|
36
|
+
# Print the debits & credits comparison.
|
37
|
+
Transaction::TYPES.each do |type|
|
38
|
+
# Calculate comparison.
|
39
|
+
avg = report.send("#{type}_average")
|
40
|
+
current = report.send("#{type}_current_sum")
|
41
|
+
diff = report.send("#{type}_diff")
|
42
|
+
in_or_out = type == :debits ? 'out' : 'in'
|
43
|
+
is_good = diff <= 0
|
44
|
+
colour = is_good ? :green : :red
|
45
|
+
|
46
|
+
# Table row.
|
47
|
+
printf(cols, "Money #{in_or_out}:", currencify(avg), currencify(current), set_color(currencify(diff), colour))
|
48
|
+
end
|
49
|
+
|
50
|
+
# Calculate net position.
|
51
|
+
net_position = report.debits_diff + report.credits_diff
|
52
|
+
is_good = net_position <= 0
|
53
|
+
colour = is_good ? :green : :red
|
54
|
+
position = is_good ? 'improvement' : 'decline'
|
55
|
+
|
56
|
+
# Print the net position.
|
57
|
+
say("\n")
|
58
|
+
say("Comparison: #{set_color(currencify(net_position) + ' ' + position + ' on average', colour)}")
|
59
|
+
say("\n")
|
60
|
+
end
|
61
|
+
|
62
|
+
desc "flush", "Flushes the database, removing all stored transactions"
|
63
|
+
def flush
|
64
|
+
Cashflow.flush
|
65
|
+
say("Successfully flushed database", :green)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def currencify(number)
|
71
|
+
is_neg = number < 0
|
72
|
+
number = number / 100.0
|
73
|
+
int, frac = ("%.2f" % number).split('.')
|
74
|
+
int.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1,")
|
75
|
+
int = int.slice(1..-1) if is_neg
|
76
|
+
"$" + int + "." + frac
|
77
|
+
end
|
78
|
+
|
79
|
+
# Takes a collection of OFX transactions and maps them to
|
80
|
+
# Transaction instances, which are saved to the database.
|
81
|
+
def map_transactions(transactions)
|
82
|
+
transactions.map do |record|
|
83
|
+
Transaction.new({
|
84
|
+
:id => record.fit_id.to_s,
|
85
|
+
:type => record.type,
|
86
|
+
:amount => record.amount_in_pennies,
|
87
|
+
:date => record.posted_at.to_date
|
88
|
+
})
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class String
|
2
|
+
def to_date
|
3
|
+
year, month, day = self.split("-").map(&:to_i)
|
4
|
+
Date.strptime("{ #{year}, #{month}, #{day} }", "{ %Y, %m, %d }")
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Fixnum
|
9
|
+
def ordinalize
|
10
|
+
if (11..13).include?(self % 100)
|
11
|
+
"#{self}th"
|
12
|
+
else
|
13
|
+
case self % 10
|
14
|
+
when 1; "#{self}st"
|
15
|
+
when 2; "#{self}nd"
|
16
|
+
when 3; "#{self}rd"
|
17
|
+
else "#{self}th"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Cashflow
|
2
|
+
class Report
|
3
|
+
TYPES = Cashflow::Transaction::TYPES
|
4
|
+
attr_accessor *TYPES
|
5
|
+
attr_accessor *TYPES.map { |type| :"#{type}_sums" }
|
6
|
+
attr_accessor *TYPES.map { |type| :"#{type}_current" }
|
7
|
+
attr_accessor *TYPES.map { |type| :"#{type}_current_sum" }
|
8
|
+
attr_accessor *TYPES.map { |type| :"#{type}_average" }
|
9
|
+
attr_accessor *TYPES.map { |type| :"#{type}_diff" }
|
10
|
+
attr_accessor :start
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
date = Date.today << options[:span]
|
14
|
+
@start = Date.new(date.year, date.month, 1)
|
15
|
+
end
|
16
|
+
|
17
|
+
def debits
|
18
|
+
@debits ||= Transaction.merged(Transaction.debits(start))
|
19
|
+
end
|
20
|
+
|
21
|
+
def credits
|
22
|
+
@credits ||= Transaction.merged(Transaction.credits(start))
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_report
|
26
|
+
filter_up_to_day_of_the_month!
|
27
|
+
separate_current_month!
|
28
|
+
group_priors_by_month!
|
29
|
+
sum_all_by_month!
|
30
|
+
calculate_averages!
|
31
|
+
calculate_differences!
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# Filters the credits and debits to only include the transactions
|
37
|
+
# that occurred up to the current day of the month.
|
38
|
+
def filter_up_to_day_of_the_month!
|
39
|
+
TYPES.each do |type|
|
40
|
+
filtered = send(type).select do |transaction|
|
41
|
+
transaction.date.day <= Date.today.day
|
42
|
+
end
|
43
|
+
send("#{type}=", filtered)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Divide the credits & debits into those for the current month
|
48
|
+
# and all others prior.
|
49
|
+
def separate_current_month!
|
50
|
+
TYPES.each do |type|
|
51
|
+
this_month = send(type).select do |transaction|
|
52
|
+
transaction.date.year == Date.today.year &&
|
53
|
+
transaction.date.month == Date.today.month
|
54
|
+
end
|
55
|
+
send("#{type}_current=", this_month)
|
56
|
+
send("#{type}=", send(type) - this_month)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Group all prior transactions into months (as a hash).
|
61
|
+
def group_priors_by_month!
|
62
|
+
TYPES.each do |type|
|
63
|
+
by_month = send(type).inject({}) do |hash, transaction|
|
64
|
+
key = "#{transaction.date.year}-#{transaction.date.month}"
|
65
|
+
hash[key] ||= []
|
66
|
+
hash[key] << transaction
|
67
|
+
hash
|
68
|
+
end
|
69
|
+
send("#{type}=", by_month)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Sum all transactions by month.
|
74
|
+
def sum_all_by_month!
|
75
|
+
TYPES.each do |type|
|
76
|
+
# Current month:
|
77
|
+
current_sum = send("#{type}_current").inject(0) do |sum, transaction|
|
78
|
+
sum += transaction.amount
|
79
|
+
sum
|
80
|
+
end
|
81
|
+
send("#{type}_current_sum=", current_sum)
|
82
|
+
|
83
|
+
# Prior months:
|
84
|
+
prior_sums = {}
|
85
|
+
send("#{type}").each do |key, transactions|
|
86
|
+
prior_sums[key] = transactions.inject(0) do |sum, transaction|
|
87
|
+
sum += transaction.amount
|
88
|
+
sum
|
89
|
+
end
|
90
|
+
end
|
91
|
+
send("#{type}_sums=", prior_sums)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Calculate the average debits & credits based on the prior transactions.
|
96
|
+
def calculate_averages!
|
97
|
+
TYPES.each do |type|
|
98
|
+
average = send("#{type}_sums").values.inject(0, :+) / send("#{type}_sums").values.length
|
99
|
+
send("#{type}_average=", average)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Calculate the difference between the current month totals and the prior averages.
|
104
|
+
def calculate_differences!
|
105
|
+
TYPES.each do |type|
|
106
|
+
diff = send("#{type}_average") - send("#{type}_current_sum")
|
107
|
+
send("#{type}_diff=", diff)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Cashflow
|
2
|
+
class Transaction
|
3
|
+
attr_accessor :id, :type, :amount, :date
|
4
|
+
|
5
|
+
TYPES = %i(debits credits)
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def select_from_sql(sql)
|
10
|
+
Cashflow.db.execute(sql).map do |(id, type, amount, date)|
|
11
|
+
Transaction.new({
|
12
|
+
:id => id,
|
13
|
+
:type => type.to_sym,
|
14
|
+
:amount => amount,
|
15
|
+
:date => date.to_date
|
16
|
+
})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def all(since)
|
21
|
+
select_from_sql("SELECT * FROM #{Cashflow.table_name} WHERE date >= '#{since.to_s}' ORDER BY date ASC")
|
22
|
+
end
|
23
|
+
|
24
|
+
def debits(since)
|
25
|
+
select_from_sql("SELECT * FROM #{Cashflow.table_name} WHERE date >= '#{since.to_s}' AND type = 'debit' ORDER BY date ASC")
|
26
|
+
end
|
27
|
+
|
28
|
+
def credits(since)
|
29
|
+
select_from_sql("SELECT * FROM #{Cashflow.table_name} WHERE date >= '#{since.to_s}' AND type = 'credit' ORDER BY date ASC")
|
30
|
+
end
|
31
|
+
|
32
|
+
def merged(transactions)
|
33
|
+
by_date = {}
|
34
|
+
transactions.each do |transaction|
|
35
|
+
key = "#{transaction.date.to_s}-#{transaction.type.to_s}"
|
36
|
+
# If a transaction already exists for this date and type, combine them.
|
37
|
+
transaction.amount += by_date[key].amount if by_date[key]
|
38
|
+
by_date[key] = transaction
|
39
|
+
end
|
40
|
+
# Return the combined transactions, sorted by date.
|
41
|
+
by_date.values.sort_by { |transaction| transaction.date }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(attrs)
|
46
|
+
attrs.each do |(attribute, value)|
|
47
|
+
send("#{attribute}=", value)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def save
|
52
|
+
begin
|
53
|
+
Cashflow.db.execute("INSERT INTO activity (id, type, amount, date) VALUES (?, ?, ?, ?)", [ id, type.to_s, amount, date.to_s ])
|
54
|
+
true
|
55
|
+
rescue Exception => e
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/cashflow.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Ruby libs.
|
2
|
+
require 'kconv' # For OFX gem fix.
|
3
|
+
require 'logger'
|
4
|
+
require 'sqlite3'
|
5
|
+
require 'thor'
|
6
|
+
require 'ofx'
|
7
|
+
|
8
|
+
# App modules.
|
9
|
+
require 'cashflow/version'
|
10
|
+
require 'cashflow/patches'
|
11
|
+
require 'cashflow/transaction'
|
12
|
+
require 'cashflow/report'
|
13
|
+
require 'cashflow/cli'
|
14
|
+
|
15
|
+
module Cashflow
|
16
|
+
|
17
|
+
# Define the database table name.
|
18
|
+
def self.table_name
|
19
|
+
"activity"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Define the root directory.
|
23
|
+
def self.root
|
24
|
+
@root ||= File.expand_path(File.join(__FILE__, '../../'))
|
25
|
+
end
|
26
|
+
|
27
|
+
# Set up & retrieve the logger.
|
28
|
+
def self.logger
|
29
|
+
@logger ||= Logger.new(File.join(root, 'log/cashflow.log'))
|
30
|
+
end
|
31
|
+
|
32
|
+
# Initialize & retrieve the database.
|
33
|
+
def self.db
|
34
|
+
@db ||= begin
|
35
|
+
# Initialize the database. Create the table if necessary.
|
36
|
+
db = SQLite3::Database.new(File.join(root, 'db/cashflow.db'))
|
37
|
+
db.execute("CREATE TABLE IF NOT EXISTS #{table_name} (id string primary key, type varchar(6), amount integer, date date);")
|
38
|
+
# Return the database connection.
|
39
|
+
db
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Erase the database.
|
44
|
+
def self.flush
|
45
|
+
db.execute("DROP TABLE IF EXISTS #{table_name};")
|
46
|
+
@db = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/log/cashflow.log
ADDED
File without changes
|
data/screenshot.png
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cashflow
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Fulcher
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-04-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: iconv
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sqlite3
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: thor
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: ofx
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Compares your latest income & expense to your average, giving you a quick
|
98
|
+
at-a-glance view of your financial position.
|
99
|
+
email:
|
100
|
+
- mike@drawingablank.me
|
101
|
+
executables:
|
102
|
+
- cashflow
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- .gitignore
|
107
|
+
- .rvmrc
|
108
|
+
- Gemfile
|
109
|
+
- LICENSE.txt
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- bin/cashflow
|
113
|
+
- cashflow.gemspec
|
114
|
+
- db/cashflow.db
|
115
|
+
- lib/cashflow.rb
|
116
|
+
- lib/cashflow/cli.rb
|
117
|
+
- lib/cashflow/patches.rb
|
118
|
+
- lib/cashflow/report.rb
|
119
|
+
- lib/cashflow/transaction.rb
|
120
|
+
- lib/cashflow/version.rb
|
121
|
+
- log/cashflow.log
|
122
|
+
- screenshot.png
|
123
|
+
homepage: http://github.com/6twenty/cashflow
|
124
|
+
licenses:
|
125
|
+
- MIT
|
126
|
+
metadata: {}
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
require_paths:
|
130
|
+
- lib
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - '>='
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - '>='
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
requirements: []
|
142
|
+
rubyforge_project:
|
143
|
+
rubygems_version: 2.0.0
|
144
|
+
signing_key:
|
145
|
+
specification_version: 4
|
146
|
+
summary: Compares your latest income & expense to your average.
|
147
|
+
test_files: []
|