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.
- data/lib/magicsheet.rb +184 -0
- metadata +74 -0
data/lib/magicsheet.rb
ADDED
@@ -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
|
+
|