moneybook 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. data/README.md +130 -0
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/bin/moneybook +30 -10
  5. data/moneybook.gemspec +4 -4
  6. metadata +7 -7
  7. data/README.rdoc +0 -19
data/README.md ADDED
@@ -0,0 +1,130 @@
1
+ INTRO
2
+ =====
3
+ MoneyBook is a simple ruby command line application to track group expenses
4
+ and know exactly how much each one of your friends owes you from your last trip!
5
+
6
+ It's not super easy to use, nor is it meant to be (since I wrote it for myself), but
7
+ it's meant to be powerful and useful.
8
+
9
+ QUICK START
10
+ ===========
11
+
12
+ to install simply do:
13
+ gem install moneybook
14
+
15
+ now to create your first moneybook file do:
16
+ moneybook new TITLE
17
+
18
+ and follow the onscreen instructions (give title, list of people in the group and optionally a currency exchange)
19
+ next edit the file as you wish and, when you want to know how things stand (how much
20
+ each one has to give/receive) simply do this:
21
+ moneybook parse moneybook_file.txt
22
+
23
+ If you want to see intermediate results (they will give you a better
24
+ understanding of the whys..) simply add a -i option, like this:
25
+ moneybook parse -i moneybook_file.txt
26
+
27
+ To see other available options do this:
28
+ moneybook parse --help
29
+
30
+ EXAMPLE FILE
31
+ ============
32
+
33
+ ### trip to paris ###
34
+ #### 2011-01-10 #####
35
+ #####################
36
+
37
+ PEOPLE: Jack Mary Kevin
38
+
39
+ # Jack payed 120$ for the tickets for all three..
40
+ flight tickets (120Jack)
41
+
42
+ # Jack payed 50$ and Mary 30$ for the dinner at hotel, and they didn't all spend the same:
43
+ # Kev got a dessert! He spent 10$ more than the others..
44
+ dinner at hotel (50Jack 30Mary) +10Kevin
45
+
46
+ # The museum was payed by Mary, and Kevin will pay only 5$ since he's a student.. Mary and
47
+ # Jack will then pay the rest (10$ each). Names can be abbrev: Kevin -> Kev -> K
48
+ museum (25Mary) 5Kev
49
+
50
+ # Knowing he was in debt, Kevin decides to give 50$ to Jack to start paying him back..
51
+ 50Kevin -> Jack
52
+
53
+ the output to the example file above is:
54
+
55
+ ### trip to paris ###
56
+ #### 2011-01-10 #####
57
+ #####################
58
+
59
+
60
+ # Jack payed 120$ for the tickets for all three..
61
+ ==== flight tickets (120Jack) ====
62
+ total: 120.0
63
+ +-------+-------+-------+
64
+ | jack | kevin | mary |
65
+ +-------+-------+-------+
66
+ | 120.0 | | |
67
+ | 40.0 | 40.0 | 40.0 |
68
+ +-------+-------+-------+
69
+ | 80.0 | -40.0 | -40.0 |
70
+ +-------+-------+-------+
71
+ | 80.0 | -40.0 | -40.0 |
72
+ +-------+-------+-------+
73
+
74
+ # Jack payed 50$ and Mary 30$ for the dinner at hotel, and they didn't all spend the same:
75
+ # Kev got a dessert! He spent 10$ more than the others..
76
+ ==== dinner at hotel (50Jack 30Mary) +10Kevin ====
77
+ total: 80.0
78
+ +-------+-------+-------+
79
+ | jack | kevin | mary |
80
+ +-------+-------+-------+
81
+ | 50.0 | | 30.0 |
82
+ | 23.3 | 33.3 | 23.3 |
83
+ +-------+-------+-------+
84
+ | 26.7 | -33.3 | 6.7 |
85
+ +-------+-------+-------+
86
+ | 106.7 | -73.3 | -33.3 |
87
+ +-------+-------+-------+
88
+
89
+ # The museum was payed by Mary, and Kevin will pay only 5$ since he's a student.. Mary and
90
+ # Jack will then pay the rest (10$ each). Names can be abbrev: Kevin -> Kev -> K
91
+ ==== museum (25Mary) 5Kev ====
92
+ total: 25.0
93
+ +-------+-------+-------+
94
+ | jack | kevin | mary |
95
+ +-------+-------+-------+
96
+ | | | 25.0 |
97
+ | 10.0 | 5.0 | 10.0 |
98
+ +-------+-------+-------+
99
+ | -10.0 | -5.0 | 15.0 |
100
+ +-------+-------+-------+
101
+ | 96.7 | -78.3 | -18.3 |
102
+ +-------+-------+-------+
103
+
104
+ # Knowing he was in debt, Kevin decides to give 50$ to Jack to start paying him back..
105
+ ==== 50Kevin -> Jack ====
106
+ total: 50.0
107
+ +-------+-------+-------+
108
+ | jack | kevin | mary |
109
+ +-------+-------+-------+
110
+ | | 50.0 | |
111
+ | 50.0 | | |
112
+ +-------+-------+-------+
113
+ | -50.0 | 50.0 | |
114
+ +-------+-------+-------+
115
+ | 46.7 | -28.3 | -18.3 |
116
+ +-------+-------+-------+
117
+ jack receives 46.67 spent 73.33 given 170.00
118
+ mary gives -18.33 spent 73.33 given 55.00
119
+ kevin gives -28.33 spent 78.33 given 50.00
120
+
121
+
122
+
123
+ each table is the result of each line in the source file, in each table there are 4 lines:
124
+ the first line is what's been payed [given], the second line is who should have payed it [spent],
125
+ then third line is the difference (so if the number is negative, the person owes money to
126
+ those whose number is positive), and the fourth line is the total balance that far.
127
+
128
+ At the end the total balance is also printed in words, kevin and mary both give something
129
+ to jack! And for each one is said how much he spent and how much he actually gave. The balance
130
+ is the difference: given - (spent + paybacks). Paybacks are not considered in the spent field.
data/Rakefile CHANGED
@@ -13,7 +13,7 @@ require 'jeweler'
13
13
  Jeweler::Tasks.new do |gem|
14
14
  # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
15
  gem.name = "moneybook"
16
- gem.homepage = "http://github.com/vircheck/moneybook"
16
+ gem.homepage = "http://github.com/vircheck/MoneyBook"
17
17
  gem.license = "MIT"
18
18
  gem.summary = "keep track of money in a group"
19
19
  gem.description = "keep track of money in a group"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.8
1
+ 0.2.0
data/bin/moneybook CHANGED
@@ -11,6 +11,9 @@ require 'date'
11
11
  require 'abbrev'
12
12
  require 'pp'
13
13
 
14
+ REGEXP_COMP_LINE = /^\s*([\w\ \']+?)\s*\((.+)\)\s*(.+)?/
15
+ REGEXP_PAYBACK_LINE = /\A\s*(.+)\s*\-+\>\s*(.+)\Z/
16
+
14
17
  class Array
15
18
  def normalize_length
16
19
  max = self.inject{|longest, current| longest.to_s.length > current.to_s.length ? longest : current }.to_s.length
@@ -31,6 +34,14 @@ def process_command(cmd)
31
34
  opts.on("-i", "--[no-]intermediate", "Show intermediate computations") do |i|
32
35
  options[:intermediate] = i
33
36
  end
37
+ opts.on("-t", "--tail NUMBER", Integer, "Show intermediate computations only on last NUMBER lines") do |t|
38
+ options[:tail] = true
39
+ options[:tail_number] = t
40
+ end
41
+ opts.on_tail("-h", "--help", "Show this message") do
42
+ puts opts
43
+ exit
44
+ end
34
45
  end.parse!(args)
35
46
  command_parse(options, args.last)
36
47
  else
@@ -96,27 +107,36 @@ def command_parse(options, filename)
96
107
  people_abbreviations = []
97
108
  sq = [] #big table to make the computations..
98
109
  f=File.open(filename,'r').read
110
+ ### in case it's a UTF-16LE file...
99
111
  if f[0]==255 && f[1]==254 then
100
112
  f=Iconv.iconv('UTF-8', 'UTF-16LE', f)[0][3..-1]
101
113
  puts "converting from UTF-16LE..."
102
114
  end
115
+ ### count number of computational lines (for the --tail option)
116
+ number_of_computational_lines = f.to_a.count{|x|x =~ REGEXP_PAYBACK_LINE || x =~ REGEXP_COMP_LINE}
117
+ reached_tail = !options[:tail]
118
+ current_comp_line = 0
119
+ ### start computation
103
120
  f.each_line{|line|
104
121
  if line =~ /^\s*\#/ || line =~ /\A\s*\Z/
105
122
  # this is interpreted as a comment line
106
- puts line if intermediate
123
+ puts line if intermediate && reached_tail
107
124
  elsif (m = line.match(/\A\s*PEOPLE\s*\:?\s*((\s+\w+)+)\s*\Z/).andand[1].andand.split(/\s+/).andand.delete_if{|x|x==""}) != nil then
108
125
  m.each{|z|people<<z.to_s.downcase}
109
126
  people_abbreviations = people.abbrev
127
+ puts "[people in list: #{people.join(', ')}]" if intermediate
110
128
  elsif (currency_change == 0) && ((m = line.match(/\A\s*CHANGE\s*\:\s*([\.\d]+)\s*\Z/).andand[1].to_f) != 0) then
111
129
  currency_change = m
112
- puts "currency change is #{currency_change}"
130
+ puts "[currency change is #{currency_change}]" if intermediate
113
131
  else
132
+ current_comp_line += 1
133
+ reached_tail = true if options[:tail] && current_comp_line > number_of_computational_lines - options[:tail_number]
114
134
  original_str = false
115
- if temp=line.match(/\A\s*(.+)\s*\-+\>\s*(.+)\Z/) then
135
+ if temp=line.match(REGEXP_PAYBACK_LINE) then
116
136
  original_str = line.strip
117
137
  line = "PAYBACK (#{temp[1].to_s}) #{temp[2].to_s}"
118
138
  end
119
- x = line.strip.match(/^\s*([\w\ \']+?)\s*\((.+)\)\s*(.+)?/).to_a.map{|z| z.to_s}
139
+ x = line.strip.match(REGEXP_COMP_LINE).to_a.map{|z| z.to_s}
120
140
  sq << {:name => x[1].to_s, :original_str => original_str || line.strip, :true_str => x[2].to_s, :virtual_str => x[3].to_s, :true => {}, :virtual => {}, :balance => {},
121
141
  :true_total => 0, :virtual_total => 0}
122
142
  c=sq.last
@@ -190,8 +210,8 @@ def command_parse(options, filename)
190
210
  c[:balance][person] = c[:true][person] - c[:virtual][person]
191
211
  }
192
212
  ### PRINT INTERMEDIATE RESULTS ###
193
- if intermediate then
194
- puts "==== #{c[:original_str].to_s} ===="
213
+ if intermediate && reached_tail then
214
+ puts "==== #{"%2d: " % current_comp_line} #{c[:original_str].to_s} ===="
195
215
  puts "total: #{c[:true_total].to_s}"
196
216
  mytable = table do |t|
197
217
  t.headings = people.sort
@@ -210,16 +230,16 @@ def command_parse(options, filename)
210
230
  }
211
231
  ### PRINT FINAL RESULTS ###
212
232
  final = get_final(sq, people)
213
- puts "in local currency:" if currency_change > 0
233
+ puts "=== in local currency:" if currency_change > 0
214
234
  people_to_print = people.normalize_length
215
235
  people.each_index{|i|
216
236
  person = people[i]
217
- puts "#{people_to_print[i]} \t #{final[0][person] > 0 ? 'receives' : 'gives '} #{"% 8.2f" % final[0][person]} spent #{"% 8.2f" % final[1][person]} given #{"% 8.2f" % final[2][person]}"
237
+ puts "#{people_to_print[i]} #{final[0][person] > 0 ? 'receives' : 'gives '} #{"% 8.2f" % final[0][person]} spent #{"% 8.2f" % final[1][person]} given #{"% 8.2f" % final[2][person]}"
218
238
  }
219
- puts "in converted currency:" if currency_change > 0
239
+ puts "=== in converted currency (multiplying by #{currency_change}):" if currency_change > 0
220
240
  people.each_index{|i|
221
241
  person = people[i]
222
- puts "#{people_to_print[i]} \t #{final[0][person] > 0 ? 'receives' : 'gives '} #{"% 8.2f" % (final[0][person]*currency_change)} spent #{"% 8.2f" % (final[1][person]*currency_change)} given #{"% 8.2f" % (final[2][person]*currency_change)}" if currency_change > 0
242
+ puts "#{people_to_print[i]} #{final[0][person] > 0 ? 'receives' : 'gives '} #{"% 8.2f" % (final[0][person]*currency_change)} spent #{"% 8.2f" % (final[1][person]*currency_change)} given #{"% 8.2f" % (final[2][person]*currency_change)}" if currency_change > 0
223
243
  }
224
244
  end
225
245
 
data/moneybook.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{moneybook}
8
- s.version = "0.1.7"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["luca cioria"]
@@ -16,14 +16,14 @@ Gem::Specification.new do |s|
16
16
  s.executables = ["moneybook"]
17
17
  s.extra_rdoc_files = [
18
18
  "LICENSE.txt",
19
- "README.rdoc"
19
+ "README.md"
20
20
  ]
21
21
  s.files = [
22
22
  ".document",
23
23
  "Gemfile",
24
24
  "Gemfile.lock",
25
25
  "LICENSE.txt",
26
- "README.rdoc",
26
+ "README.md",
27
27
  "Rakefile",
28
28
  "VERSION",
29
29
  "bin/moneybook",
@@ -32,7 +32,7 @@ Gem::Specification.new do |s|
32
32
  "test/helper.rb",
33
33
  "test/test_moneybook.rb"
34
34
  ]
35
- s.homepage = %q{http://github.com/vircheck/moneybook}
35
+ s.homepage = %q{http://github.com/vircheck/MoneyBook}
36
36
  s.licenses = ["MIT"]
37
37
  s.require_paths = ["lib"]
38
38
  s.rubygems_version = %q{1.4.2}
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moneybook
3
3
  version: !ruby/object:Gem::Version
4
- hash: 11
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 8
10
- version: 0.1.8
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - luca cioria
@@ -128,13 +128,13 @@ extensions: []
128
128
 
129
129
  extra_rdoc_files:
130
130
  - LICENSE.txt
131
- - README.rdoc
131
+ - README.md
132
132
  files:
133
133
  - .document
134
134
  - Gemfile
135
135
  - Gemfile.lock
136
136
  - LICENSE.txt
137
- - README.rdoc
137
+ - README.md
138
138
  - Rakefile
139
139
  - VERSION
140
140
  - bin/moneybook
@@ -143,7 +143,7 @@ files:
143
143
  - test/helper.rb
144
144
  - test/test_moneybook.rb
145
145
  has_rdoc: true
146
- homepage: http://github.com/vircheck/moneybook
146
+ homepage: http://github.com/vircheck/MoneyBook
147
147
  licenses:
148
148
  - MIT
149
149
  post_install_message:
data/README.rdoc DELETED
@@ -1,19 +0,0 @@
1
- = moneybook
2
-
3
- Simple program to keep track of money in a ground, for example during a trip
4
-
5
- == Contributing to moneybook
6
-
7
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
- * Fork the project
10
- * Start a feature/bugfix branch
11
- * Commit and push until you are happy with your contribution
12
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
-
15
- == Copyright
16
-
17
- Copyright (c) 2011 luca cioria. See LICENSE.txt for
18
- further details.
19
-