cashflow 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![Useage screenshot](https://raw.github.com/6twenty/cashflow/master/screenshot.png)
|
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: []
|