Empact-money 2.3.6

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 ADDED
@@ -0,0 +1,9 @@
1
+ *.gem
2
+ doc
3
+ *.sqlite3
4
+ *.log
5
+ spec/*.log
6
+ tmp/**/*
7
+ db/**/*
8
+ coverage/*
9
+ *.swp
data/History.txt ADDED
File without changes
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2005 Tobias Lutke
2
+ Copyright (c) 2008 Phusion
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,24 @@
1
+ History.txt
2
+ MIT-LICENSE
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/money.rb
7
+ lib/money/acts_as_money.rb
8
+ lib/money/core_extensions.rb
9
+ lib/money/errors.rb
10
+ lib/money/money.rb
11
+ lib/money/variable_exchange_bank.rb
12
+ money.gemspec
13
+ rails/init.rb
14
+ script/console
15
+ script/destroy
16
+ script/generate
17
+ spec/db/database.yml
18
+ spec/db/schema.rb
19
+ spec/money/acts_as_money_spec.rb
20
+ spec/money/core_extensions_spec.rb
21
+ spec/money/exchange_bank_spec.rb
22
+ spec/money/money_spec.rb
23
+ spec/spec.opts
24
+ spec/spec_helper.rb
data/README.rdoc ADDED
@@ -0,0 +1,132 @@
1
+ = Money $
2
+
3
+ This library aids one in handling money and different currencies. Features:
4
+
5
+ - Provides a Money class which encapsulates all information about an certain
6
+ amount of money, such as its value and its currency.
7
+ - Represents monetary values as integers, in cents. This avoids floating point
8
+ rounding errors.
9
+ - Provides APIs for exchanging money from one currency to another.
10
+ - Has the ability to parse a money string into a Money object.
11
+ - Provides ActiveRecord "has_money" method.
12
+
13
+ Resources:
14
+
15
+ - Website: http://money.rubyforge.org
16
+ - RDoc API: http://money.rubyforge.org
17
+ - This fork: http://github.com/ShadowBelmolve/money
18
+ - Forked from: http://github.com/nofxx/money
19
+ - Git repository: http://github.com/FooBarWidget/money/tree/master
20
+
21
+
22
+ == Download
23
+
24
+ Install stable releases with the following command:
25
+
26
+ gem install money
27
+
28
+ The development version (hosted on Github) can be installed with:
29
+
30
+ gem sources -a http://gems.github.com
31
+ gem install nofxx-money
32
+
33
+
34
+ == Usage
35
+
36
+ === Synopsis
37
+
38
+ require 'money'
39
+
40
+ # 10.00 USD
41
+ money = Money.new(1000, "USD")
42
+ money.cents # => 1000
43
+ money.currency # => "USD"
44
+ money.format # => "$10.00"
45
+
46
+ Money.new(880088, "EUR").format # => €8,800.88
47
+ Money.new(-8000).format(:no_cents => true) # => $-80
48
+
49
+ Money.new(1000, "USD") == Money.new(1000, "USD") # => true
50
+ Money.new(1000, "USD") == Money.new( 100, "USD") # => false
51
+ Money.new(1000, "USD") == Money.new(1000, "EUR") # => false
52
+
53
+
54
+ === Currency Exchange
55
+
56
+ Exchanging money is performed through an exchange bank object. The default
57
+ exchange bank object requires one to manually specify the exchange rate. Here's
58
+ an example of how it works:
59
+
60
+ Money.add_rate("CAD", 0.803115)
61
+ Money.add_rate("USD", 1.24515)
62
+
63
+ Money.us_dollar(100_00).exchange_to("CAD") # => Money.new(15504, "CAD")
64
+ Money.ca_dollar(100_00).exchange_to("USD") # => Money.new(6450, "USD")
65
+
66
+ or
67
+
68
+ Money.us_dollar(100).as_cad # => Money.new(155, "CAD")
69
+ Money.ca_dollar(100).as_usd # => Money.new(64, "USD")
70
+
71
+ Comparison and arithmetic operations work as expected:
72
+
73
+ Money.new(1000, "USD") <=> Money.new(900, "USD") # => 1; 9.00 USD is smaller
74
+ Money.new(1000, "EUR") + Money.new(10, "EUR") == Money.new(1010, "EUR")
75
+
76
+ Money.add_rate("EUR", 0.5)
77
+ Money.new(1000, "EUR") + Money.new(1000, "USD") == Money.new(1500, "EUR")
78
+
79
+ Fetch the exchange rates published by the European Bank
80
+
81
+ Money.default_bank.fetch_rates # Fetch the rates
82
+ Money.default_bank.auto_fetch 3600 # Fetch the rates every hour
83
+ Money.default_bank.stop_fetch # Stop auto-fetch
84
+
85
+ There is nothing stopping you from creating bank objects which scrapes
86
+ www.xe.com for the current rates or just returns <tt>rand(2)</tt>:
87
+
88
+ Money.default_bank = ExchangeBankWhichScrapesXeDotCom.new
89
+
90
+
91
+ === Ruby on Rails
92
+
93
+ Use the +has_money+ method to embed the money object in your models.
94
+ The following example requires a +price_cents+ and a +price_currency+
95
+ fields on the database.
96
+
97
+ config/enviroment.rb
98
+
99
+ require.gem 'nofxx-money', :lib => 'money'
100
+
101
+ app/models/product.rb
102
+
103
+ class Product < ActiveRecord::Base
104
+ belongs_to :product
105
+ has_money :price
106
+
107
+ validates_numericality_of :price_cents, :greater_than => 0
108
+ end
109
+
110
+ migration:
111
+
112
+ create_table :products do |t|
113
+ t.integer :price_cents
114
+ t.string :price_currency
115
+ end
116
+
117
+
118
+ === Default Currency
119
+
120
+ By default Money defaults to USD as its currency. This can be overwritten using:
121
+
122
+ Money.default_currency = "CAD"
123
+
124
+ If you use Rails, then environment.rb is a very good place to put this.
125
+
126
+
127
+ == TODO
128
+
129
+ * Better validation (fix composed_of allow_nil)
130
+ * Interest (almost there..)
131
+ * Remote rate fetching
132
+
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "money"
8
+ gem.summary = "This library aids one in handling money and different currencies."
9
+ gem.description = "This library aids one in handling money and different currencies."
10
+ gem.email = "see@reame"
11
+ gem.homepage = "http://github.com/nofxx/money"
12
+ gem.authors = ["Money Team"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
18
+ end
19
+
20
+ require 'spec/rake/spectask'
21
+ Spec::Rake::SpecTask.new(:spec) do |spec|
22
+ spec.libs << 'lib' << 'spec'
23
+ spec.spec_files = FileList['spec/**/*_spec.rb']
24
+ end
25
+
26
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
27
+ spec.libs << 'lib' << 'spec'
28
+ spec.pattern = 'spec/**/*_spec.rb'
29
+ spec.rcov = true
30
+ end
31
+
32
+ task :default => :spec
33
+
34
+ require 'rake/rdoctask'
35
+ Rake::RDocTask.new do |rdoc|
36
+ if File.exist?('VERSION.yml')
37
+ config = YAML.load(File.read('VERSION.yml'))
38
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
39
+ else
40
+ version = ""
41
+ end
42
+
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = "money #{version}"
45
+ rdoc.rdoc_files.include('README*')
46
+ rdoc.rdoc_files.include('lib/**/*.rb')
47
+ end
48
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.3.6
data/lib/money.rb ADDED
@@ -0,0 +1,25 @@
1
+ # Copyright (c) 2005 Tobias Luetke
2
+ # Copyright (c) 2008 Phusion
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
24
+ require 'money/money'
25
+ require 'money/core_extensions'
@@ -0,0 +1,41 @@
1
+ # Money require 'money'
2
+ # based on github.com/collectiveidea/acts_as_money
3
+ module ActsAsMoney #:nodoc:
4
+ def self.included(base) #:nodoc:
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ #
10
+ # class Product
11
+ # has_money :value, :tax, :opts => opts
12
+ # end
13
+ #
14
+ # @product.value.class #=> "Money"
15
+ # @product.value_cents #=> "1000"
16
+ # @product.tax_currency #=> "USD"
17
+ #
18
+ # Opts:
19
+ # :cents => "pennys" #=> @product.pennys
20
+ # :currency => "currency" #=> @product.currency
21
+ # :allow_nil => true
22
+ # :with_currency => false
23
+ # :with_cents => true #=> 1000.to_money #=> #<Money @cents=1000>
24
+ #
25
+ def has_money(*attributes)
26
+ config = {:with_currency => true, :with_cents => false,
27
+ :allow_nil => false }.update(attributes.extract_options!)
28
+
29
+ for attribute in attributes do
30
+ mapping = [[config[:cents] || "#{attribute}_cents", 'cents']]
31
+ mapping << [config[:currency] || "#{attribute}_currency", 'currency'] if config[:with_currency]
32
+
33
+ composed_of attribute, :class_name => 'Money', :allow_nil => config[:allow_nil],
34
+ :mapping => mapping, :converter => lambda { |m| (m||0).to_money(config[:with_cents]) }
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,139 @@
1
+ class Numeric
2
+ # Converts this numeric to a Money object in the default currency. It
3
+ # multiplies the numeric value by 100 and treats that as cents if receive false.
4
+ #
5
+ # 100.to_money => #<Money @cents=100>
6
+ # 100.37.to_money => #<Money @cents=10037>
7
+ # 100.to_money(false) => #<Money @cents=10000>
8
+ def to_money(cents = false)
9
+ if cents
10
+ if self.is_a? Integer
11
+ Money.new(self)
12
+ else
13
+ Money.new(self.to_s.gsub(/\./,'').to_i)
14
+ end
15
+ else
16
+ Money.new(self * 100)
17
+ end
18
+ end
19
+ end
20
+
21
+ class String
22
+ # Parses the current string and converts it to a Money object.
23
+ # Excess characters will be discarded.
24
+ #
25
+ # '100'.to_money # => #<Money @cents=100>
26
+ # '100.37'.to_money # => #<Money @cents=10037>
27
+ # '100 USD'.to_money # => #<Money @cents=100, @currency="USD">
28
+ # 'USD 100'.to_money # => #<Money @cents=100, @currency="USD">
29
+ # '$100 USD'.to_money # => #<Money @cents=100, @currency="USD">
30
+ # '$100 USD'.to_money(false) # => #<Money @cents=10000, @currency="USD">
31
+ def to_money(with_cents = false)
32
+ # Get the currency.
33
+ matches = scan /([A-Z]{2,3})/
34
+ currency = matches[0] ? matches[0][0] : Money.default_currency
35
+ cents = calculate_cents(self, with_cents)
36
+ Money.new(cents, currency)
37
+ end
38
+
39
+ private
40
+
41
+ def calculate_cents(number, with_cents = true)
42
+ # remove anything that's not a number, potential delimiter, or minus sign
43
+ num = number.gsub(/[^\d|\.|,|\'|\s|\-]/, '').strip
44
+
45
+ # set a boolean flag for if the number is negative or not
46
+ negative = num.split(//).first == "-"
47
+
48
+ # if negative, remove the minus sign from the number
49
+ num = num.gsub(/^-/, '') if negative
50
+
51
+ # gather all separators within the result number
52
+ used_separators = num.scan /[^\d]/
53
+
54
+ # determine the number of unique separators within the number
55
+ #
56
+ # e.g.
57
+ # $1,234,567.89 would return 2 (, and .)
58
+ # $125,00 would return 1
59
+ # $199 would return 0
60
+ # $1 234,567.89 would raise an error (separators are space, comma, and period)
61
+ case used_separators.uniq.length
62
+ # no separator or delimiter; major (dollars) is the number, and minor (cents) is 0
63
+ when 0 then major, minor = num, 0
64
+
65
+ # two separators, so we know the last item in this array is the
66
+ # major/minor delimiter and the rest are separators
67
+ when 2
68
+ separator, delimiter = used_separators.uniq
69
+ # remove all separators, split on the delimiter
70
+ major, minor = num.gsub(separator, '').split(delimiter)
71
+ min = 0 unless min
72
+ when 1
73
+ # we can't determine if the comma or period is supposed to be a separator or a delimiter
74
+ # e.g.
75
+ # 1,00 - comma is a delimiter
76
+ # 1.000 - period is a delimiter
77
+ # 1,000 - comma is a separator
78
+ # 1,000,000 - comma is a separator
79
+ # 10000,00 - comma is a delimiter
80
+ # 1000,000 - comma is a delimiter
81
+
82
+ # assign first separator for reusability
83
+ separator = used_separators.first
84
+
85
+ # separator is used as a separator when there are multiple instances, always
86
+ if num.scan(separator).length > 1 # multiple matches; treat as separator
87
+ major, minor = num.gsub(separator, ''), 0
88
+ else
89
+ # ex: 1,000 - 1.0000 - 10001.000
90
+ # split number into possible major (dollars) and minor (cents) values
91
+ possible_major, possible_minor = num.split(separator)
92
+
93
+ # if the minor (cents) length isn't 3, assign major/minor from the possibles
94
+ # e.g.
95
+ # 1,00 => 1.00
96
+ # 1.0000 => 1.00
97
+ # 1.2 => 1.20
98
+ if possible_minor.length != 3 # delimiter
99
+ major, minor = possible_major, possible_minor
100
+ else
101
+ # minor length is three
102
+ # let's try to figure out intent of the delimiter
103
+
104
+ # the major length is greater than three, which means
105
+ # the comma or period is used as a delimiter
106
+ # e.g.
107
+ # 1000,000
108
+ # 100000,000
109
+ if possible_major.length > 3
110
+ major, minor = possible_major, possible_minor
111
+ else
112
+ # number is in format ###{sep}### or ##{sep}### or #{sep}###
113
+ # handle as , is sep, . is delimiter
114
+ if separator == '.'
115
+ major, minor = possible_major, possible_minor
116
+ else
117
+ major, minor = "#{possible_major}#{possible_minor}", 0
118
+ end
119
+ end
120
+ end
121
+ end
122
+ else
123
+ raise ArgumentError, "Invalid currency amount"
124
+ end
125
+
126
+ # build the string based on major/minor since separator/delimiters have been removed
127
+ # transform to a float, multiply by 100 to convert to cents
128
+ if with_cents and minor == 0
129
+ cents = major.to_i
130
+ else
131
+ cents = (major.to_i * 100) + minor.to_i
132
+ end
133
+
134
+
135
+ # if negative, multiply by -1; otherwise, return positive cents
136
+ negative ? cents * -1 : cents
137
+ end
138
+
139
+ end