fantastic_currency 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +4 -0
- data/README.textile +92 -0
- data/Rakefile +14 -0
- data/fantastic_currency.gemspec +30 -0
- data/lib/fantastic_currency.rb +138 -0
- metadata +65 -0
data/Manifest
ADDED
data/README.textile
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
h1. Fantastic Currency
|
2
|
+
|
3
|
+
Currency magic with Active Record & Rails.
|
4
|
+
|
5
|
+
h1. Install
|
6
|
+
|
7
|
+
<pre><code>
|
8
|
+
gem install fantastic_currency
|
9
|
+
|
10
|
+
config.gem "fantastic_currency" (put in environment.rb)
|
11
|
+
</code></pre>
|
12
|
+
|
13
|
+
h1. Usage & Examples
|
14
|
+
|
15
|
+
Let's imagine you're building an online shop, and you want to manage items which have a price, and a shipping price.
|
16
|
+
You operate from the US and the UK, some of your items are for sale in US Dollars, others in British Pounds.
|
17
|
+
|
18
|
+
*In your model:*
|
19
|
+
<pre><code>
|
20
|
+
class Products < ActiveRecord::Base
|
21
|
+
currency :price
|
22
|
+
currency :shipping_price
|
23
|
+
end
|
24
|
+
</code></pre>
|
25
|
+
This will ensure your monetary values are stored safely in the database.
|
26
|
+
You should store the relevant 3-letter currency code in your table in a column named @currency@.
|
27
|
+
If you do not do this, you will have to pass in a :currency => :something whenever you access the field,
|
28
|
+
otherwise the default currency, USD is assumed.
|
29
|
+
|
30
|
+
You can work with your currencies like this:
|
31
|
+
<pre><code>
|
32
|
+
record.price = "42.50"
|
33
|
+
record.price # => 42.5 (as BigDecimal)
|
34
|
+
record.price :format => true # => $42.50
|
35
|
+
record.price :format => true,
|
36
|
+
:convert_to => :GBP # => £26.80
|
37
|
+
|
38
|
+
record.price = "12345.67"
|
39
|
+
record.price :format => true,
|
40
|
+
:delimiter => "-",
|
41
|
+
:separator => "_" # => $12_345-67
|
42
|
+
|
43
|
+
record.price = 500
|
44
|
+
record.price :format => true # => $500
|
45
|
+
record.price :format => true,
|
46
|
+
:extra_zeros => true # => $500.00
|
47
|
+
record.price :format => true,
|
48
|
+
:before_unit => "(",
|
49
|
+
:after_unit => ")" # => ($)500
|
50
|
+
|
51
|
+
record.price = 0
|
52
|
+
record.price :format => true,
|
53
|
+
:free_as_text => true # => "free"
|
54
|
+
|
55
|
+
</code></pre>
|
56
|
+
|
57
|
+
*No floats are allowed* When setting a currency field, feel free to use a string, an integer, or a bigdecimal.
|
58
|
+
Do not use floats, because they are not accurate enough. You'll get told off if you try to!
|
59
|
+
|
60
|
+
You can also manually call the currency formatter from within a model or controller.
|
61
|
+
Note that you will need to manually specify the currency in this case.
|
62
|
+
<pre><code>
|
63
|
+
format_currency(4280, :format => true, :currency => :GBP) # => £42.80
|
64
|
+
|
65
|
+
format_currency(4280, :format => true,
|
66
|
+
:currency => :GBP, :precise_input => true) # => £4280
|
67
|
+
|
68
|
+
format_currency("4280.50", :format => true,
|
69
|
+
:currency => :GBP, :precise_input => true) # => £4280.50
|
70
|
+
|
71
|
+
format_currency("42", :currency => :GBP,
|
72
|
+
:convert_to => :USD, :precise_input => true) # => 65.70
|
73
|
+
</code></pre>
|
74
|
+
|
75
|
+
|
76
|
+
h1. Configuration
|
77
|
+
|
78
|
+
By default, a few currencies are set up, but you may wish to configure your own.
|
79
|
+
If you do, in environment.rb (or anywhere you like) you can do something like this:
|
80
|
+
|
81
|
+
<pre><code>
|
82
|
+
FantasticCurrency::Config.define_currencies({
|
83
|
+
:USD => { :symbol => "$", :precision => 2, :name => "US Dollars", :nominal_value => 1 },
|
84
|
+
:GBP => { :symbol => "£", :precision => 2, :name => "British Pounds", :nominal_value => "1.5666" },
|
85
|
+
:CAD => { :symbol => "CA $", :precision => 2, :name => "Canadian Dollars", :nominal_value => "0.95" },
|
86
|
+
:AUD => { :symbol => "AU $", :precision => 2, :name => "Australian Dollars", :nominal_value => "0.8886" }
|
87
|
+
})
|
88
|
+
</code></pre>
|
89
|
+
|
90
|
+
Nominal value is used for converting between currencies. An easy way to work this out is to make your first currency's nominal value 1, and then work out all the following currencies values, in comparison to the first. E.g. 1 USD = 1 USD. 1 GBP = 1.5666 USD. etc.
|
91
|
+
|
92
|
+
You may wish to write an automated system for generating and storing these nominal values based on current market values. That is up to you.
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('fantastic_currency', '0.1.0') do |p|
|
6
|
+
p.description = "Manage currencies with Active Record"
|
7
|
+
p.url = "http://github.com/iamcalledrob/FantasticCurrency"
|
8
|
+
p.author = "Rob Mason"
|
9
|
+
p.email = "info+gems@slightlyfantastic.com"
|
10
|
+
p.ignore_pattern = ["tmp/*", "script/*"]
|
11
|
+
p.development_dependencies = []
|
12
|
+
end
|
13
|
+
|
14
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{fantastic_currency}
|
5
|
+
s.version = "0.1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Rob Mason"]
|
9
|
+
s.date = %q{2010-02-14}
|
10
|
+
s.description = %q{Manage currencies with Active Record}
|
11
|
+
s.email = %q{info+gems@slightlyfantastic.com}
|
12
|
+
s.extra_rdoc_files = ["README.textile", "lib/fantastic_currency.rb"]
|
13
|
+
s.files = ["README.textile", "Rakefile", "lib/fantastic_currency.rb", "Manifest", "fantastic_currency.gemspec"]
|
14
|
+
s.homepage = %q{http://github.com/iamcalledrob/FantasticCurrency}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Fantastic_currency", "--main", "README.textile"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{fantastic_currency}
|
18
|
+
s.rubygems_version = %q{1.3.5}
|
19
|
+
s.summary = %q{Manage currencies with Active Record}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
26
|
+
else
|
27
|
+
end
|
28
|
+
else
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module FantasticCurrency
|
2
|
+
|
3
|
+
class Helper
|
4
|
+
include Singleton
|
5
|
+
include ActionView::Helpers
|
6
|
+
end
|
7
|
+
|
8
|
+
class Currency
|
9
|
+
def self.format(value, options={})
|
10
|
+
options = {
|
11
|
+
:currency => nil,
|
12
|
+
:format => false,
|
13
|
+
:extra_zeros => false,
|
14
|
+
:delimiter => ",",
|
15
|
+
:separator => ".",
|
16
|
+
:free_as_text => true,
|
17
|
+
:display_unit => true,
|
18
|
+
:before_unit => "",
|
19
|
+
:after_unit => " ",
|
20
|
+
:convert_to => nil,
|
21
|
+
:precise_input => false,
|
22
|
+
}.merge(options)
|
23
|
+
|
24
|
+
if options[:precise_input] == true
|
25
|
+
value = BigDecimal.new(value.to_s) * 10**FantasticCurrency::Config.get_currency(options[:currency])[:precision]
|
26
|
+
end
|
27
|
+
|
28
|
+
if options[:convert_to]
|
29
|
+
source_currency = FantasticCurrency::Config.get_currency(options[:currency])
|
30
|
+
dest_currency = FantasticCurrency::Config.get_currency(options[:convert_to])
|
31
|
+
value = value * BigDecimal.new(source_currency[:nominal_value].to_s) / BigDecimal.new(dest_currency[:nominal_value].to_s)
|
32
|
+
value = value / 10**(source_currency[:precision] - dest_currency[:precision])
|
33
|
+
|
34
|
+
active_currency = dest_currency
|
35
|
+
else
|
36
|
+
active_currency = FantasticCurrency::Config.get_currency(options[:currency])
|
37
|
+
end
|
38
|
+
|
39
|
+
precision_factor = 10**active_currency[:precision]
|
40
|
+
|
41
|
+
if options[:format] == true
|
42
|
+
if value == 0 and options[:free_as_text]
|
43
|
+
return "free"
|
44
|
+
end
|
45
|
+
helper = FantasticCurrency::Helper.instance
|
46
|
+
|
47
|
+
if options[:extra_zeros] == false and (value.to_i / precision_factor * precision_factor) == value.to_i
|
48
|
+
precision = 0
|
49
|
+
else
|
50
|
+
precision = active_currency[:precision]
|
51
|
+
end
|
52
|
+
|
53
|
+
value_as_string = helper.number_with_precision(BigDecimal.new(value.to_s) / precision_factor,
|
54
|
+
:precision => precision,
|
55
|
+
:delimiter => options[:delimiter],
|
56
|
+
:separator => options[:separator])
|
57
|
+
|
58
|
+
if options[:display_unit] == true
|
59
|
+
options[:before_unit] + active_currency[:symbol] + options[:after_unit] + value_as_string
|
60
|
+
else
|
61
|
+
value_as_string
|
62
|
+
end
|
63
|
+
else
|
64
|
+
BigDecimal.new(value.to_s) / precision_factor
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
class Config
|
72
|
+
# Some initial currencies to have fun with. Exchange rates will be inaccurate.
|
73
|
+
@@currencies = {
|
74
|
+
:USD => { :symbol => "$", :precision => 2, :name => "US Dollars", :nominal_value => 1 },
|
75
|
+
:GBP => { :symbol => "£", :precision => 2, :name => "British Pounds", :nominal_value => "1.5666" },
|
76
|
+
:CAD => { :symbol => "CA $", :precision => 2, :name => "Canadian Dollars", :nominal_value => "0.95" },
|
77
|
+
:AUD => { :symbol => "AU $", :precision => 2, :name => "Australian Dollars", :nominal_value => "0.8886" },
|
78
|
+
:EUR => { :symbol => "€", :precision => 2, :name => "Euro", :nominal_value => "1.3617" },
|
79
|
+
:JPY => { :symbol => "¥", :precision => 0, :name => "Japanese Yen", :nominal_value => "0.011116" },
|
80
|
+
:KRW => { :symbol => "₩", :precision => 0, :name => "South Korean Won", :nominal_value => "0.000869" }
|
81
|
+
}
|
82
|
+
def self.define_currencies currencies
|
83
|
+
@@currencies = currencies
|
84
|
+
end
|
85
|
+
def self.currencies
|
86
|
+
@@currencies
|
87
|
+
end
|
88
|
+
def self.get_currency currency=nil
|
89
|
+
if currency and FantasticCurrency::Config.currencies[currency.to_sym]
|
90
|
+
return FantasticCurrency::Config.currencies[currency.to_sym]
|
91
|
+
else
|
92
|
+
return { :symbol => "$", :precision => 2, :nominal_value => 1 } #default currency.
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
module ActiveRecord
|
98
|
+
def self.included(base)
|
99
|
+
base.extend(ClassMethods)
|
100
|
+
end
|
101
|
+
|
102
|
+
def format_currency(value, options={})
|
103
|
+
FantasticCurrency::Currency.format(value, options)
|
104
|
+
end
|
105
|
+
|
106
|
+
module ClassMethods
|
107
|
+
|
108
|
+
def currency(field_name)
|
109
|
+
|
110
|
+
define_method "#{field_name.to_s}" do |*args|
|
111
|
+
if self[field_name]
|
112
|
+
format_currency(self[field_name], { :currency => self[:currency] }.merge(args.first || {}))
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
define_method "#{field_name.to_s}=" do |value|
|
117
|
+
raise "Money doesn't float!" if value.class.name == "Float"
|
118
|
+
|
119
|
+
if self[field_name]
|
120
|
+
currency = FantasticCurrency::Config.get_currency(self[:currency])
|
121
|
+
self[field_name] = (BigDecimal.new(value.to_s) * (10**currency[:precision])).to_i
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
module ActionController
|
130
|
+
def format_currency(value, options={})
|
131
|
+
FantasticCurrency::Currency.format(value, options)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
ActiveRecord::Base.send(:include, FantasticCurrency::ActiveRecord)
|
138
|
+
ActionController::Base.send(:include, FantasticCurrency::ActionController)
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fantastic_currency
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rob Mason
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-14 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Manage currencies with Active Record
|
17
|
+
email: info+gems@slightlyfantastic.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.textile
|
24
|
+
- lib/fantastic_currency.rb
|
25
|
+
files:
|
26
|
+
- README.textile
|
27
|
+
- Rakefile
|
28
|
+
- lib/fantastic_currency.rb
|
29
|
+
- Manifest
|
30
|
+
- fantastic_currency.gemspec
|
31
|
+
has_rdoc: true
|
32
|
+
homepage: http://github.com/iamcalledrob/FantasticCurrency
|
33
|
+
licenses: []
|
34
|
+
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options:
|
37
|
+
- --line-numbers
|
38
|
+
- --inline-source
|
39
|
+
- --title
|
40
|
+
- Fantastic_currency
|
41
|
+
- --main
|
42
|
+
- README.textile
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "1.2"
|
56
|
+
version:
|
57
|
+
requirements: []
|
58
|
+
|
59
|
+
rubyforge_project: fantastic_currency
|
60
|
+
rubygems_version: 1.3.5
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: Manage currencies with Active Record
|
64
|
+
test_files: []
|
65
|
+
|