fantastic_currency 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest ADDED
@@ -0,0 +1,4 @@
1
+ README.textile
2
+ Rakefile
3
+ lib/fantastic_currency.rb
4
+ Manifest
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
+