magicsheet 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.
Files changed (2) hide show
  1. data/lib/magicsheet.rb +184 -0
  2. metadata +74 -0
@@ -0,0 +1,184 @@
1
+ require 'yahoo_finance'
2
+
3
+ module MagicSheet
4
+ class Currency
5
+ attr_reader :name
6
+ alias :to_s :name
7
+
8
+ LIST = %w(USD EUR)
9
+
10
+ def self.[](str)
11
+ name = LIST.find do |name|
12
+ str.upcase.strip.end_with?(name)
13
+ end
14
+ return name ? self.new(name) : nil
15
+ end
16
+
17
+ def initialize(name)
18
+ @name = name
19
+ end
20
+
21
+ def ==(other)
22
+ self.name == other.name
23
+ end
24
+
25
+ def conversion_rate_to(other)
26
+ symbol = "#{self.name}#{other.name}=X".upcase
27
+ YahooFinance.quotes(symbol).first.last_trade_price.to_f
28
+ end
29
+ end
30
+
31
+ class Cost
32
+ attr_reader :currency, :amount
33
+
34
+ def self.[](arg)
35
+ arg = arg.to_s.strip
36
+ currency = Currency[arg]
37
+ if currency
38
+ amount = (arg[/\d+(\.\d+)?/,0] || 0).to_f
39
+ return self.new(amount,currency)
40
+ else
41
+ return nil
42
+ end
43
+ end
44
+
45
+ def initialize(amount,currency)
46
+ @amount = amount
47
+ @currency = currency
48
+ end
49
+
50
+ def to_s
51
+ "#{"%.2f" % @amount} #{@currency}"
52
+ end
53
+ end
54
+
55
+ class Sheet
56
+ DELEGATE = %w(rate fees convert_to chunk width log) # Which methods to make available in the global scope
57
+ DEFAULT_WIDTH = 20
58
+
59
+ def initialize
60
+ @fees = []
61
+ @chunk = 1
62
+ @width = DEFAULT_WIDTH
63
+ end
64
+
65
+ def width(w)
66
+ @width = w
67
+ end
68
+
69
+ def convert_to(currency_name)
70
+ @convert_to = Currency[currency_name]
71
+ end
72
+
73
+ def rate(rate)
74
+ @rate = Cost[rate]
75
+ end
76
+
77
+ def fees(*list)
78
+ @fees = list.map {|f| Cost[f]}.compact
79
+ end
80
+
81
+ def chunk(val)
82
+ unless val.between?(1,60)
83
+ raise ArgumentError, "Chunk must be between 1 (minute-wise calculation) and 60 (full-hour calculation)"
84
+ end
85
+ @chunk = val
86
+ end
87
+
88
+ def scan
89
+ STDERR.puts "Scanning sheet..."
90
+ @hours, @minutes = 0, 0;
91
+ if defined?(DATA)
92
+ DATA.read.scan(/(\d+)\:(\d+)/) do |h,m|
93
+ @hours += h.to_i
94
+ @minutes += m.to_i
95
+ end
96
+ @hours += @minutes / 60
97
+ @minutes = @minutes % 60
98
+ end
99
+ return self
100
+ end
101
+
102
+ def log(str)
103
+ @log << ((str == '-') ? ''.rjust(@width,'-') : str.to_s.rjust(@width))
104
+ end
105
+
106
+ def process
107
+ @log = []
108
+ log "#{"%.2d" % @hours}h:#{"%.2d" % @minutes}m"
109
+
110
+ if @chunk > 1 && @minutes > 0
111
+ @minutes = (@minutes.to_f/@chunk).ceil * @chunk
112
+ if @minutes == 60
113
+ @hours += 1
114
+ @minutes = 0
115
+ end
116
+ end
117
+
118
+ log "= #{"%.2d" % @hours}h:#{"%.2d" % @minutes}m" if @chunk > 1
119
+
120
+ # if @minutes == 60
121
+ # @hours += 1
122
+ # @minutes = 0
123
+ # end
124
+ # end
125
+
126
+ log "x #{@rate}"
127
+ total = @hours * @rate.amount + (@minutes * @rate.amount/60.0)
128
+ log '-'
129
+ log "= #{Cost.new(total,@rate.currency)}"
130
+
131
+ fees = false
132
+
133
+ @fees.select {|f| f.currency == @rate.currency}.each do |f|
134
+ fees = true
135
+ total -= f.amount
136
+ log "- #{f}"
137
+ end
138
+
139
+ if fees
140
+ #log '-'
141
+ log "= #{Cost.new(total,@rate.currency)}"
142
+ end
143
+
144
+
145
+ # Convert to target currency, if any
146
+ if @convert_to && (@convert_to != @rate.currency)
147
+ STDERR.puts "Getting conversion rate..."
148
+ c = @rate.currency.conversion_rate_to(@convert_to)
149
+ log ''
150
+ log "x #{c}"
151
+ log "(1h = #{Cost.new(@rate.amount * c,@convert_to)})"
152
+ log ''
153
+ total *= c
154
+ log "= #{Cost.new(total,@convert_to)}"
155
+
156
+ fees = false
157
+ # Substract fees in target currency
158
+ @fees.select {|f| f.currency == @convert_to}.each do |f|
159
+ total -= f.amount
160
+ log "- #{f}"
161
+ fees = true
162
+ end
163
+
164
+ if fees
165
+ log '-'
166
+ log "= #{Cost.new(total,@convert_to)}"
167
+ end
168
+ end
169
+
170
+ puts
171
+ puts @log.join("\n")
172
+ end # process
173
+ end
174
+
175
+ def self.current
176
+ @current ||= Sheet.new.scan
177
+ end
178
+ end
179
+
180
+ MagicSheet::Sheet::DELEGATE.each { |name| eval "def #{name}(*args); MagicSheet.current.#{name}(*args); end" }
181
+
182
+ at_exit do
183
+ MagicSheet.current.process
184
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: magicsheet
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ version: "0.1"
9
+ platform: ruby
10
+ authors:
11
+ - Esad Hajdarevic
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+
16
+ date: 2010-03-06 00:00:00 +01:00
17
+ default_executable:
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: yahoo-finance
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "="
25
+ - !ruby/object:Gem::Version
26
+ segments:
27
+ - 0
28
+ - 0
29
+ - 2
30
+ version: 0.0.2
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: Write your timesheets in ruby after the __END__. Supports currencies and tells you how much you've earned so far to keep you motivated
34
+ email: esad@eigenbyte.com
35
+ executables: []
36
+
37
+ extensions: []
38
+
39
+ extra_rdoc_files: []
40
+
41
+ files:
42
+ - lib/magicsheet.rb
43
+ has_rdoc: true
44
+ homepage: http://github.com/esad/magicsheet
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options: []
49
+
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.3.6
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Timesheets in ruby
73
+ test_files: []
74
+