Empact-money 2.3.6

Sign up to get free protection for your applications and to get access to all the features.
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