picklive-currency 1.0.0
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/.gitignore +1 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +68 -0
- data/Guardfile +6 -0
- data/README.md +52 -0
- data/Rakefile +6 -0
- data/lib/picklive-currency.rb +3 -0
- data/lib/picklive/currency.rb +243 -0
- data/lib/picklive/currency/autoload.rb +5 -0
- data/picklive-currency.gemspec +25 -0
- data/spec/picklive/currency_spec.rb +53 -0
- metadata +103 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pkg
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
picklive-currency (0.0.1)
|
5
|
+
actionpack
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
actionpack (3.1.2)
|
11
|
+
activemodel (= 3.1.2)
|
12
|
+
activesupport (= 3.1.2)
|
13
|
+
builder (~> 3.0.0)
|
14
|
+
erubis (~> 2.7.0)
|
15
|
+
i18n (~> 0.6)
|
16
|
+
rack (~> 1.3.5)
|
17
|
+
rack-cache (~> 1.1)
|
18
|
+
rack-mount (~> 0.8.2)
|
19
|
+
rack-test (~> 0.6.1)
|
20
|
+
sprockets (~> 2.1.0)
|
21
|
+
activemodel (3.1.2)
|
22
|
+
activesupport (= 3.1.2)
|
23
|
+
builder (~> 3.0.0)
|
24
|
+
i18n (~> 0.6)
|
25
|
+
activesupport (3.1.2)
|
26
|
+
multi_json (~> 1.0)
|
27
|
+
builder (3.0.0)
|
28
|
+
diff-lcs (1.1.3)
|
29
|
+
erubis (2.7.0)
|
30
|
+
guard (0.8.8)
|
31
|
+
thor (~> 0.14.6)
|
32
|
+
guard-rspec (0.5.1)
|
33
|
+
guard (>= 0.8.4)
|
34
|
+
hike (1.2.1)
|
35
|
+
i18n (0.6.0)
|
36
|
+
multi_json (1.0.3)
|
37
|
+
rack (1.3.5)
|
38
|
+
rack-cache (1.1)
|
39
|
+
rack (>= 0.4)
|
40
|
+
rack-mount (0.8.3)
|
41
|
+
rack (>= 1.0.0)
|
42
|
+
rack-test (0.6.1)
|
43
|
+
rack (>= 1.0)
|
44
|
+
rake (0.9.2)
|
45
|
+
rspec (2.7.0)
|
46
|
+
rspec-core (~> 2.7.0)
|
47
|
+
rspec-expectations (~> 2.7.0)
|
48
|
+
rspec-mocks (~> 2.7.0)
|
49
|
+
rspec-core (2.7.1)
|
50
|
+
rspec-expectations (2.7.0)
|
51
|
+
diff-lcs (~> 1.1.2)
|
52
|
+
rspec-mocks (2.7.0)
|
53
|
+
sprockets (2.1.1)
|
54
|
+
hike (~> 1.2)
|
55
|
+
rack (~> 1.0)
|
56
|
+
tilt (~> 1.1, != 1.3.0)
|
57
|
+
thor (0.14.6)
|
58
|
+
tilt (1.3.3)
|
59
|
+
|
60
|
+
PLATFORMS
|
61
|
+
ruby
|
62
|
+
|
63
|
+
DEPENDENCIES
|
64
|
+
guard
|
65
|
+
guard-rspec
|
66
|
+
picklive-currency!
|
67
|
+
rake
|
68
|
+
rspec
|
data/Guardfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# picklive-currency
|
2
|
+
|
3
|
+
Classes for representing amounts of money or virtual money.
|
4
|
+
|
5
|
+
Currently it has GBP and Chips currencies built in.
|
6
|
+
|
7
|
+
## Examples
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
# The amount can be given in as the usual value or the integer value:
|
11
|
+
GBP[5.20] == GBP.new(520) # => true
|
12
|
+
|
13
|
+
# They are comparable:
|
14
|
+
GBP[3.10] > GBP[3.09] # => true
|
15
|
+
GBP[3.10] > GBP[3.10] # => false
|
16
|
+
|
17
|
+
# You can compare with an integer value
|
18
|
+
GBP[3.10] > 309 # => true
|
19
|
+
|
20
|
+
# Equality check is more strict:
|
21
|
+
GBP[3.10] == 310 # => false
|
22
|
+
|
23
|
+
# Objects are instances of Picklive::Currency::Base
|
24
|
+
GBP[3.10].is_a? Picklive::Currency::Base # => true
|
25
|
+
|
26
|
+
GBP[3.10].to_s # => "£3.10"
|
27
|
+
Chips[100].to_s # => "100 Chips"
|
28
|
+
|
29
|
+
Chips[100].class.real? # => false
|
30
|
+
Chips[100].class.virtual? # => true
|
31
|
+
GBP[3.10].class.real? # => true
|
32
|
+
|
33
|
+
class Transaction
|
34
|
+
attr_accessor :amount_in_pennies
|
35
|
+
attr_accessor :currency_code
|
36
|
+
include Picklive::Currency::ModelMethods
|
37
|
+
include Picklive::Currency::Converters
|
38
|
+
currency_field :amount
|
39
|
+
end
|
40
|
+
|
41
|
+
t = Transaction.new
|
42
|
+
t.amount_in_pennies = 310
|
43
|
+
t.currency_code = 'GBP'
|
44
|
+
|
45
|
+
t.amount # => <GBP:3.1>
|
46
|
+
t.amount_in_currency # => <GBP:3.1>
|
47
|
+
|
48
|
+
t.amount = Chips[100]
|
49
|
+
t.amount_in_pennies # => 100
|
50
|
+
t.currency_code # => "chips"
|
51
|
+
```
|
52
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'action_view'
|
4
|
+
|
5
|
+
module Picklive
|
6
|
+
module Currency
|
7
|
+
|
8
|
+
def self.[](code)
|
9
|
+
puts "WARN: Currency code #{code.inspect} is DEPRECATED" if code == 'chip'
|
10
|
+
return GBP if code == 'GBP'
|
11
|
+
return Chips if code == 'chips' || code == 'chip'
|
12
|
+
raise ArgumentError.new("unknown currency code: #{code.inspect}")
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.all ; [GBP, Chips] ; end
|
16
|
+
def self.cash_codes ; 'GBP' ; end
|
17
|
+
def self.virtual_codes ; 'chips' ; end
|
18
|
+
|
19
|
+
# To create a currency object:
|
20
|
+
#
|
21
|
+
# GBP.new(1000) # -> <GBP:10.0>
|
22
|
+
# GBP[10] # -> <GBP:10.0>
|
23
|
+
# GBP['10.00'] # -> <GBP:10.0>
|
24
|
+
# Chips[1000] # -> <Chips:1000>
|
25
|
+
class Base
|
26
|
+
def self.fake? ; not real? ; end
|
27
|
+
def self.cash? ; real? ; end
|
28
|
+
def self.virtual? ; !cash? ; end
|
29
|
+
def self.html_symbol ; symbol ; end
|
30
|
+
|
31
|
+
attr_accessor :integer_amount
|
32
|
+
private :integer_amount=
|
33
|
+
|
34
|
+
def initialize integer_amount
|
35
|
+
self.integer_amount = integer_amount.to_i
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.[](human_value)
|
39
|
+
v = human_value.to_f
|
40
|
+
new((v * self.precision).round)
|
41
|
+
end
|
42
|
+
|
43
|
+
def amount
|
44
|
+
if self.class.precision == 1
|
45
|
+
integer_amount
|
46
|
+
else
|
47
|
+
integer_amount / self.class.precision.to_f
|
48
|
+
end
|
49
|
+
end
|
50
|
+
alias_method :to_decimal, :amount
|
51
|
+
|
52
|
+
def +(other); same_type!(other); self.class.new(integer_amount + num_from(other)); end
|
53
|
+
def -(other); same_type!(other); self.class.new(integer_amount - num_from(other)); end
|
54
|
+
def *(multiplier); self.class.new(integer_amount * multiplier); end
|
55
|
+
def /(divider); self.*(1/divider.to_f); end
|
56
|
+
def -@; self.class.new(-integer_amount); end
|
57
|
+
def abs; self.class.new(integer_amount.abs); end
|
58
|
+
|
59
|
+
include Comparable
|
60
|
+
def <=>(other); same_type!(other); self.integer_amount <=> num_from(other); end
|
61
|
+
def ==(other)
|
62
|
+
if other.is_a?(self.class)
|
63
|
+
self.integer_amount == other.integer_amount
|
64
|
+
else
|
65
|
+
self.integer_amount == 0 && (other.respond_to?(:to_i) && other.to_i == 0)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
def eql?(other); self == other; end
|
69
|
+
|
70
|
+
def same_type!(other)
|
71
|
+
unless other.is_a?(Fixnum) || other.is_a?(Float)
|
72
|
+
unless other.is_a?(Picklive::Currency::Base)
|
73
|
+
raise "Not a currency: #{other.inspect}"
|
74
|
+
end
|
75
|
+
if self.class.code != other.class.code
|
76
|
+
raise ArgumentError.new("Different currencies: #{self.class.code} vs #{other.class.code}")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def num_from(other)
|
82
|
+
other.respond_to?(:integer_amount) ? other.integer_amount : other
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_i; integer_amount; end
|
86
|
+
def to_f; integer_amount.to_f; end
|
87
|
+
def inspect; "<#{self.class.code}:#{amount}>"; end
|
88
|
+
|
89
|
+
def for_sentence
|
90
|
+
to_s(:short => true)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
class GBP < Base
|
96
|
+
def self.precision ; 100 ; end
|
97
|
+
def self.code ; 'GBP' ; end
|
98
|
+
def self.real? ; true ; end
|
99
|
+
def self.symbol ; '£' ; end
|
100
|
+
def self.html_symbol ; '£' ; end
|
101
|
+
|
102
|
+
include ActionView::Helpers::NumberHelper
|
103
|
+
|
104
|
+
def to_s options = {}
|
105
|
+
s = number_to_currency(amount, :unit => '£')
|
106
|
+
if options[:short]
|
107
|
+
if amount < 1.0
|
108
|
+
s = "#{integer_amount}p"
|
109
|
+
else
|
110
|
+
s = s.gsub(/\.0+$/, '')
|
111
|
+
end
|
112
|
+
end
|
113
|
+
s
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
class Chips < Base
|
119
|
+
def self.precision ; 1 ; end
|
120
|
+
def self.code ; 'chips' ; end
|
121
|
+
def self.real? ; false ; end
|
122
|
+
def self.symbol ; '' ; end
|
123
|
+
|
124
|
+
include ActionView::Helpers::TextHelper
|
125
|
+
|
126
|
+
def to_s
|
127
|
+
pluralize(amount, "Chip")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
# It provides scopes for models that have a `currency_code` method.
|
133
|
+
module ModelMethods
|
134
|
+
|
135
|
+
def self.included(base)
|
136
|
+
if defined?(ActiveRecord::Base) && base.superclass == ActiveRecord::Base
|
137
|
+
base.class_eval do
|
138
|
+
scope :cash_only, where(:currency_code => Picklive::Currency.cash_codes)
|
139
|
+
scope :virtual_only, where(:currency_code => Picklive::Currency.virtual_codes)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def currency
|
145
|
+
Picklive::Currency[currency_code]
|
146
|
+
end
|
147
|
+
|
148
|
+
def amount_in_currency
|
149
|
+
currency.new(amount_in_pennies)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Provides setters and getters for `something_in_currency`, alias: `something`
|
154
|
+
# Mix it into classes that have `something_in_pennies` setter/getter and call:
|
155
|
+
# cuurrency_field :something
|
156
|
+
module Converters
|
157
|
+
def self.included(base)
|
158
|
+
base.instance_eval do
|
159
|
+
def currency_field(*fields)
|
160
|
+
self.class_eval do
|
161
|
+
|
162
|
+
fields.each do |field|
|
163
|
+
define_method "#{field}_in_currency" do
|
164
|
+
pennies = self.send("#{field}_in_pennies")
|
165
|
+
currency.new(pennies) unless pennies.nil?
|
166
|
+
end
|
167
|
+
|
168
|
+
define_method "#{field}_in_currency=" do |amount_in_currency|
|
169
|
+
if ! amount_in_currency.is_a?(Picklive::Currency::Base)
|
170
|
+
amount_in_currency = Picklive::Currency::GBP.new((amount_in_currency.to_f * Picklive::Currency::GBP.precision).round)
|
171
|
+
end
|
172
|
+
if self.respond_to?("currency_code=")
|
173
|
+
self.currency_code = amount_in_currency.class.code
|
174
|
+
end
|
175
|
+
self.send("#{field}_in_pennies=", amount_in_currency.integer_amount)
|
176
|
+
end
|
177
|
+
|
178
|
+
alias_method :"#{field}", :"#{field}_in_currency"
|
179
|
+
alias_method :"#{field}=", :"#{field}_in_currency="
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def currency_fields(*fields)
|
186
|
+
currency_field(*fields)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
GBP = Picklive::Currency::GBP
|
196
|
+
Chips = Picklive::Currency::Chips
|
197
|
+
|
198
|
+
class Fixnum
|
199
|
+
def percent
|
200
|
+
self.to_f / 100
|
201
|
+
end
|
202
|
+
|
203
|
+
def pounds
|
204
|
+
(self * 100).pennies
|
205
|
+
end
|
206
|
+
alias_method :pound, :pounds
|
207
|
+
|
208
|
+
def pennies
|
209
|
+
Picklive::Currency::GBP.new(self)
|
210
|
+
end
|
211
|
+
alias_method :pence, :pennies
|
212
|
+
|
213
|
+
def to_pounds
|
214
|
+
self / 100.0
|
215
|
+
end
|
216
|
+
|
217
|
+
def to_pennies
|
218
|
+
self * 100
|
219
|
+
end
|
220
|
+
|
221
|
+
def chips
|
222
|
+
Picklive::Currency::Chips.new(self)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
class Float
|
227
|
+
def pounds
|
228
|
+
(self * 100).round.pennies
|
229
|
+
end
|
230
|
+
alias_method :pound, :pounds
|
231
|
+
|
232
|
+
def to_pennies
|
233
|
+
(self * 100).round
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
class String
|
238
|
+
def pounds
|
239
|
+
to_f.pounds
|
240
|
+
end
|
241
|
+
alias_method :pound, :pounds
|
242
|
+
end
|
243
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
require 'picklive/currency/autoload'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "picklive-currency"
|
8
|
+
s.version = "1.0.0"
|
9
|
+
s.authors = ["Levente Bagi"]
|
10
|
+
s.email = ["levente@picklive.com"]
|
11
|
+
s.homepage = "https://tech.picklive.com"
|
12
|
+
s.summary = %q{Picklive Currency}
|
13
|
+
s.description = %q{Currency classes that can represent GBP or Chips}
|
14
|
+
|
15
|
+
s.rubyforge_project = "picklive-currency"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency "actionpack"
|
23
|
+
s.add_development_dependency "rspec"
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'picklive/currency'
|
4
|
+
|
5
|
+
describe Picklive::Currency do
|
6
|
+
|
7
|
+
it "knows about GBP" do
|
8
|
+
Picklive::Currency['GBP'].should == GBP
|
9
|
+
end
|
10
|
+
|
11
|
+
it "knows about chips" do
|
12
|
+
Picklive::Currency['chips'].should == Chips
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "currency 0 value" do
|
16
|
+
it "is equal to 0 as integer" do
|
17
|
+
(GBP.new(0) == 0).should be_true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "currency nonzero value" do
|
22
|
+
it "is not equal to the integer value" do
|
23
|
+
(GBP.new(100) == 100).should be_false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can be created in to different ways, which create equal objects" do
|
28
|
+
GBP[5.20].should == GBP.new(520)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "is comparable with same type" do
|
32
|
+
(GBP[3.10] > GBP[3.09]).should be_true
|
33
|
+
(GBP[3.10] > GBP[3.10]).should be_false
|
34
|
+
end
|
35
|
+
|
36
|
+
it "is not comparable with a different type" do
|
37
|
+
expect { GBP[3.10] > Chips[309] }.to raise_error
|
38
|
+
end
|
39
|
+
|
40
|
+
it "is comparable to an integer, except for equality" do
|
41
|
+
(GBP[3.10] > 309).should be_true
|
42
|
+
(GBP[3.10] > 310).should be_false
|
43
|
+
(GBP[3.10] == 310).should be_false
|
44
|
+
(GBP[3.10] != 310).should be_true
|
45
|
+
end
|
46
|
+
|
47
|
+
it "has a format that can be included in sentences" do
|
48
|
+
"Give me #{GBP[5].for_sentence}".should == "Give me £5"
|
49
|
+
"Give me #{GBP[0.1].for_sentence}".should == "Give me 10p"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: picklive-currency
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Levente Bagi
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-02-14 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: actionpack
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rspec
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
description: Currency classes that can represent GBP or Chips
|
49
|
+
email:
|
50
|
+
- levente@picklive.com
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- .gitignore
|
59
|
+
- Gemfile
|
60
|
+
- Gemfile.lock
|
61
|
+
- Guardfile
|
62
|
+
- README.md
|
63
|
+
- Rakefile
|
64
|
+
- lib/picklive-currency.rb
|
65
|
+
- lib/picklive/currency.rb
|
66
|
+
- lib/picklive/currency/autoload.rb
|
67
|
+
- picklive-currency.gemspec
|
68
|
+
- spec/picklive/currency_spec.rb
|
69
|
+
homepage: https://tech.picklive.com
|
70
|
+
licenses: []
|
71
|
+
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
hash: 3
|
83
|
+
segments:
|
84
|
+
- 0
|
85
|
+
version: "0"
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
requirements: []
|
96
|
+
|
97
|
+
rubyforge_project: picklive-currency
|
98
|
+
rubygems_version: 1.8.10
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: Picklive Currency
|
102
|
+
test_files:
|
103
|
+
- spec/picklive/currency_spec.rb
|