money_extensions 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f56893765e2c83d2ffea6e7e80ca2cab0963370c
4
+ data.tar.gz: 799f31640b6dc664c300e7ca4a01b3275e378cfb
5
+ SHA512:
6
+ metadata.gz: a4144e85e7838f52c2cc39adb3e845cc4cbc48fa6c88c2e1836caf861e7592f65712b734e8b1c8306187b9a1040dfa0e7cc2e281c5344694e1b6cbac54c461a4
7
+ data.tar.gz: c1d03bfdecca1db109625f7bd4f2ace3839f68ea389dfaf1460e48a01bd34802d37e172eb5dbe3404c5fe6ce7540dde68c50cc357bc998af567705660734117a
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ pkg
2
+ coverage
3
+ Gemfile.lock
4
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,30 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9
4
+ - 2.0
5
+ - 2.1
6
+ - 2.2
7
+ gemfile:
8
+ - gemfiles/rails2.gemfile
9
+ - gemfiles/rails3.gemfile
10
+ - gemfiles/rails4.gemfile
11
+ matrix:
12
+ exclude:
13
+ - rvm: 1.9
14
+ gemfile: gemfiles/rails4.gemfile
15
+ - rvm: 1.9
16
+ gemfile: gemfiles/rails2.gemfile
17
+ - rvm: 2.0
18
+ gemfile: gemfiles/rails2.gemfile
19
+ - rvm: 2.1
20
+ gemfile: gemfiles/rails2.gemfile
21
+ - rvm: 2.2
22
+ gemfile: gemfiles/rails2.gemfile
23
+ script: "bundle exec rake spec"
24
+ notifications:
25
+ email:
26
+ - support@travellink.com.au
27
+ flowdock:
28
+ secure: A0Fr9j0kvKPEWuPbJu/DPMYPvx98UI8k43adKN9UjTB6FRVAQM3kXmKJIL3A495scc2Civrg2KHDkbwNwVThY+HdLeU7q71Bm6obP6ZkCe43MIoc9NiVJymCNguku6+tLEyusdWIceFiBW7P5p/sU1QU66GFj1t4wNTpVq9A4Kw=
29
+ sudo: false
30
+ cache: bundler
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) Tom Preston-Werner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ Money Extensions
2
+ ================
3
+
4
+ [![Build Status](https://travis-ci.org/sealink/money_extensions.png?branch=master)](https://travis-ci.org/sealink/money_extensions)
5
+ [![Coverage Status](https://coveralls.io/repos/sealink/money_extensions/badge.png)](https://coveralls.io/r/sealink/money_extensions)
6
+ [![Dependency Status](https://gemnasium.com/sealink/money_extensions.png?travis)](https://gemnasium.com/sealink/money_extensions)
7
+ [![Code Climate](https://codeclimate.com/github/sealink/money_extensions.png)](https://codeclimate.com/github/sealink/money_extensions)
8
+
9
+ # DESCRIPTION
10
+
11
+ Extends the money classes with helpful functions and currency functions
12
+
13
+ e.g. in model
14
+ money_fields :total, :discount
15
+
16
+ This will by default map a money object "total" to a database column total_in_cents and the same for discount.
17
+
18
+ Various numerical functions for money, e.g. round, split_between, sign, etc.
19
+
20
+ See spec directory for examples.
21
+
22
+ # INSTALLATION
23
+
24
+ Add to your Gemfile:
25
+ gem 'money_extensions'
26
+
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc 'Default: run specs.'
4
+ task :default => :spec
5
+
6
+ require 'rspec/core/rake_task'
7
+
8
+ desc "Run specs"
9
+ RSpec::Core::RakeTask.new do |t|
10
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
11
+ # Put spec opts in a file named .rspec in root
12
+ end
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+ gemspec :path => "../"
3
+
4
+ gem 'money', '< 5.1', :platform => :ruby_18
5
+
6
+ group :development, :test do
7
+ gem 'activerecord', '~> 2.3.0'
8
+ end
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+ gemspec :path => "../"
3
+
4
+ gem 'money', '< 5.1', :platform => :ruby_18
5
+
6
+ group :development, :test do
7
+ gem 'activerecord', '~> 3.2.0'
8
+ end
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+ gemspec :path => '../'
3
+
4
+ gem 'money', '< 5.1', :platform => :ruby_18
5
+
6
+ group :development, :test do
7
+ gem 'activerecord', '~> 4.0'
8
+ end
@@ -0,0 +1,57 @@
1
+ module ActiveRecord
2
+ module Extensions
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ # Assign a :currency (Money::Currency object) reader/writer for the given
10
+ # field name.
11
+ #
12
+ # NOTES:
13
+ # * Currency is identified by a 3-letter ISO code,
14
+ # * Lookup is doen via Money::Currency.find(iso_code)
15
+ def has_currency(*args)
16
+ options = args.extract_options!
17
+ attr_name = args.first || :currency
18
+
19
+ field_name = "#{attr_name}_iso_code"
20
+
21
+ composed_of attr_name, :class_name => "Money::Currency",
22
+ :mapping => [field_name, 'iso_code'],
23
+ :allow_nil => true,
24
+ :constructor => Proc.new{|value| Money::Currency.new(value) unless value.blank?}
25
+
26
+ if Rails.version > '3'
27
+ scope :for_currency, lambda{ |currency|
28
+ where(:currency_iso_code => currency.iso_code)
29
+ }
30
+ else
31
+ named_scope :for_currency, lambda{ |currency|
32
+ {:conditions => {:currency_iso_code => currency.iso_code}}
33
+ }
34
+ end
35
+
36
+ if options[:default]
37
+ before_validation :set_default_currency
38
+ class_eval <<-METHOD
39
+ def set_default_currency
40
+ self.currency ||= EnabledCurrency.base_currency
41
+ true
42
+ end
43
+ METHOD
44
+ end
45
+
46
+ if options[:required]
47
+ validates_presence_of :currency_iso_code
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+ end
55
+
56
+ ::ActiveRecord::Base.send(:include, MoneyField)
57
+ ::ActiveRecord::Base.send(:include, ActiveRecord::Extensions)
@@ -0,0 +1,6 @@
1
+ class Array
2
+ # Returns sum as a money - and returns 0 for empty arrays
3
+ def total_money
4
+ empty? ? ::Money.new(0) : inject(:+)
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ class BigDecimal
2
+
3
+ def negative?
4
+ self < BigDecimal.new('0')
5
+ end
6
+
7
+ def positive?
8
+ self > BigDecimal.new('0')
9
+ end
10
+
11
+ end
12
+
@@ -0,0 +1,189 @@
1
+ require 'money'
2
+
3
+ module Extensions
4
+
5
+ def self.included(base)
6
+ base.class_eval do
7
+ alias_method :to_s_without_currency_hack, :to_s
8
+ alias_method :to_s, :to_s_with_currency_hack
9
+ alias_method :to_d_without_currency_hack, :to_d
10
+ alias_method :to_d, :to_d_with_currency_hack
11
+ end
12
+ end
13
+
14
+ # Override division -- should not do it, but use split_between
15
+ def /(*params)
16
+ raise "Division of money NOT allowed - use 'split_between' to avoid rounding errors"
17
+ end
18
+
19
+ def to_d_with_currency_hack
20
+ BigDecimal.new("#{cents/100.0}")
21
+ end
22
+
23
+ # Split the money between the specified number - and return an array of money
24
+ # Remainder in splits are given to the highest value e.g.
25
+ # $2.00 splits [40, 81, 40] into [49, 102, 49]
26
+ # $1.01 splits [ 1, 1, 1] into [35, 33, 33]
27
+ def split_between(params)
28
+ #if just a number is passed in, then money is split equally
29
+ if params.is_a?(Fixnum)
30
+ divisor = params
31
+ raise ArgumentError, "Can only split up over a positive number" if divisor < 1
32
+
33
+ rounded_split = self.lossy_divide(divisor)
34
+ results = Array.new(divisor, rounded_split) # Create with 'divisor' num elements
35
+
36
+ #if an array of monies is passed in, then split in proportions
37
+ elsif params.is_a?(Array)
38
+
39
+ raise ArgumentError, "Can only split up over at least one ration" if params.empty? && !self.zero?
40
+
41
+
42
+ results = params.map { |p| p.is_a?(::Money) ? p.cents : p }
43
+
44
+ total = results.inject(:+)
45
+ if total.zero?
46
+ return Array.new(results.size, ::Money.new(0)) if self.zero?
47
+ raise ArgumentError, "Total of ratios should not be zero! You sent in: #{params.inspect}"
48
+ end
49
+
50
+ results.map! do |ratio|
51
+ ::Money.new((self.cents * (ratio.to_f / total)).round)
52
+ end
53
+ else
54
+ raise "Either a fixnum or array has to be passed in for splitting money"
55
+ end
56
+
57
+ # Distribute rounding to max absolute to avoid a $0 amount getting the rounding
58
+ biggest_value_index = results.index(results.max_by(&:abs))
59
+ results[biggest_value_index] += self - results.total_money
60
+
61
+ return results
62
+ end
63
+
64
+
65
+ DEFAULT_FORMAT_RULES = [:html]
66
+ # HACK COZ WE ARE PRETENDING WE DON'T HAVE CURRENCY!!!
67
+ #
68
+ # TODO: Make this not a hack...
69
+ def to_s_with_currency_hack
70
+ sprintf("%.2f", cents.to_f / 100) #currency.subunit_to_unit)
71
+ end
72
+
73
+ def inspect
74
+ inbuilt_inspect_style_id = '%x' % (object_id << 1)
75
+ "\#<Money:0x#{inbuilt_inspect_style_id} $#{self}>"
76
+ end
77
+
78
+ # Money's default formatted is not flexible enought.
79
+ # - we don't want it to say 'free' when 0 (not correct for 'discounts' and such)
80
+ def format(*rules)
81
+ rules = rules.empty? ? DEFAULT_FORMAT_RULES.dup : rules.flatten
82
+
83
+ options = {}
84
+ rules.each do |rule|
85
+ if rule.is_a? Hash
86
+ options = rule
87
+ end
88
+ end
89
+
90
+ html_wrap = rules.include?(:html)
91
+
92
+ options[:delimiter] ||= ','
93
+ options[:separator] ||= '.'
94
+
95
+ ''.tap do |formatted|
96
+ formatted << "<span class=\"money #{direction_class}\">" if html_wrap
97
+
98
+ rules << :signed if cents < 0
99
+
100
+ if rules.include?(:signed)
101
+ formatted << if cents > 0
102
+ '+'
103
+ elsif cents < 0
104
+ '-'
105
+ else
106
+ ''
107
+ end
108
+ end
109
+
110
+ no_cents = rules.include?(:no_cents) ||
111
+ (rules.include?(:hide_zero_cents) && cents % 100 == 0)
112
+ format_string = no_cents ? "$%d" : "$%.2f"
113
+ amount = format_string % (cents.abs.to_f / 100)
114
+ amount.gsub!('.', options[:separator])
115
+ amount.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
116
+ formatted << amount
117
+
118
+ formatted << "</span>" if html_wrap
119
+ end
120
+ end
121
+
122
+ def direction_class
123
+ if cents > 0
124
+ 'positive'
125
+ elsif cents < 0
126
+ 'negative'
127
+ else
128
+ 'zero'
129
+ end
130
+ end
131
+
132
+ def mark_up(mark_up_definition)
133
+ mark_up_cents = mark_up_definition.cents || 0
134
+ rounding_direction = mark_up_definition.rounding_direction.try(:to_sym)
135
+ multiplier = ((100 + mark_up_definition.percent)/100.0)
136
+ (self * multiplier).round(mark_up_definition.rounding_cents, rounding_direction) + ::Money.new(mark_up_cents)
137
+ end
138
+
139
+ # money = Money.new(501)
140
+ # money.round
141
+ # => 500
142
+ # money.round(10)
143
+ # => 510
144
+ # money.round(1)
145
+ # => 511
146
+
147
+ # money.round(100, 'up')
148
+ # => 600
149
+ # money.round(100, 'down')
150
+ # => 500
151
+ # money.round(100, 'nearest')
152
+ # => 500
153
+ def round(round_to_cents = 100, direction = :nearest)
154
+ round_to_cents = 100 if round_to_cents.nil?
155
+ case direction.to_sym
156
+ when :nearest
157
+ rounded_cents = (cents + round_to_cents/2) / round_to_cents * round_to_cents
158
+ when :up
159
+ rounded_cents = (cents + round_to_cents) / round_to_cents * round_to_cents
160
+ when :down
161
+ rounded_cents = (cents) / round_to_cents * round_to_cents
162
+ else
163
+ end
164
+ ::Money.new(rounded_cents)
165
+ end
166
+
167
+ def abs
168
+ if cents >= 0
169
+ self.clone
170
+ else
171
+ ::Money.new(0 - cents)
172
+ end
173
+ end
174
+
175
+ def positive?
176
+ cents > 0
177
+ end
178
+
179
+ def negative?
180
+ cents < 0
181
+ end
182
+
183
+ end
184
+
185
+ ::Money.class_eval do
186
+ alias_method :lossy_divide, :/
187
+ end
188
+
189
+ ::Money.send(:include, Extensions)
@@ -0,0 +1,6 @@
1
+ class Fixnum
2
+ #Returns self as a money (treated as cents)
3
+ def total_money
4
+ zero? ? ::Money.new(0) : ::Money.new(self)
5
+ end
6
+ end
@@ -0,0 +1,44 @@
1
+ module MoneyField
2
+
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ # Add a money field attribute
9
+ #
10
+ # By default it will use attribute_in_cents for cents value
11
+ def money_field(attribute)
12
+ module_eval <<-METHOD
13
+ def #{attribute}
14
+ Money.new(#{attribute}_in_cents) if #{attribute}_in_cents.present?
15
+ end
16
+
17
+ # Allow assigning of non money objects directly
18
+ # Allows form data to be directly passed through
19
+ # e.g. object.cost = '5.1'
20
+ def #{attribute}=(money)
21
+ self.#{attribute}_in_cents = money.try(:to_money).try(:cents)
22
+ end
23
+ METHOD
24
+ end
25
+
26
+
27
+ def money_fields(*attributes)
28
+ attributes.each {|a| self.money_field(a)}
29
+ end
30
+
31
+
32
+ def money(*fields)
33
+ fields.each do |field|
34
+ class_eval <<-METHOD
35
+ def #{field}
36
+ Money.new(#{field}_in_cents)
37
+ end
38
+ METHOD
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,16 @@
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.
4
+ #
5
+ # NOTE!!!
6
+ # This is overriden as per the default Money .to_money because it assumes
7
+ # a different default currency...
8
+ #
9
+ # 100.to_money => #<Money @cents=10000>
10
+ # 100.37.to_money => #<Money @cents=10037>
11
+ # require 'bigdecimal'
12
+ # BigDecimal.new('100').to_money => #<Money @cents=10000>
13
+ def to_money(currency = nil)
14
+ ::Money.new((self * 100).round, currency)
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ class String
2
+ # Converts this string to a float and then to a Money object in the default currency.
3
+ # It multiplies the converted numeric value by 100 and treats that as cents.
4
+ #
5
+ # NOTE!!!
6
+ # This is overriden as per the default Money .to_money because it assumes
7
+ # a different default currency...
8
+ #
9
+ # '100'.to_money => #<Money @cents=10000>
10
+ # '100.37'.to_money => #<Money @cents=10037>
11
+ def to_money(currency = nil)
12
+ Money.new((self.to_f * 100).round, currency)
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module MoneyExtensions
2
+ VERSION = '0.0.2'
3
+ end
@@ -0,0 +1,17 @@
1
+ module MoneyExtensions
2
+ require 'money'
3
+ require 'money_extensions/array'
4
+ require 'money_extensions/big_decimal'
5
+ require 'money_extensions/fixnum'
6
+ require 'money_extensions/extensions'
7
+ require 'money_extensions/money_field'
8
+ require 'money_extensions/numeric'
9
+ require 'money_extensions/string'
10
+
11
+ # TODO: Defer requiring the active record extension to who needs it
12
+ if Module.const_defined?('ActiveRecord')
13
+ require 'money_extensions/active_record/extensions'
14
+ end
15
+
16
+ end
17
+
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'money_extensions/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'money_extensions'
8
+ spec.version = MoneyExtensions::VERSION
9
+ spec.authors = ["Michael Noack", "Alessandro Berardi"]
10
+ spec.email = 'support@travellink.com.au'
11
+ spec.description = "These are extensions from the money/currency classes."
12
+ spec.summary = "Set of extensions to the money gem used by TravelLink Technology."
13
+ spec.homepage = 'http://github.com/sealink/money_extensions'
14
+
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency 'money', '>= 3.0.0', '< 6.0.0'
23
+ spec.add_development_dependency 'rake'
24
+ spec.add_development_dependency 'rspec'
25
+ spec.add_development_dependency 'simplecov'
26
+ spec.add_development_dependency 'simplecov-rcov'
27
+ spec.add_development_dependency 'coveralls'
28
+ spec.add_development_dependency 'sqlite3'
29
+ spec.add_development_dependency 'activerecord'
30
+ spec.add_development_dependency 'travis'
31
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ ::ActiveRecord::Base.send(:include, ActiveRecord::Extensions)
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe BigDecimal do
4
+
5
+ it "should allow verifying if it's a positive value" do
6
+ expect(BigDecimal.new('-1')).to_not be_positive
7
+ expect(BigDecimal.new('0')).to_not be_positive
8
+ expect(BigDecimal.new('1')).to be_positive
9
+ end
10
+
11
+ it "should allow verifying if it's a negative value" do
12
+ expect(BigDecimal.new('-1')).to be_negative
13
+ expect(BigDecimal.new('0')).to_not be_negative
14
+ expect(BigDecimal.new('1')).to_not be_negative
15
+ end
16
+
17
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ DB_FILE = 'tmp/test_db'
4
+ FileUtils.mkdir_p File.dirname(DB_FILE)
5
+ FileUtils.rm_f DB_FILE
6
+
7
+ ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => DB_FILE
8
+
9
+ load('spec/schema.rb')
10
+
11
+ load('rails/init.rb')
12
+
13
+ describe "ActiveRecord::Base" do
14
+ class Model < ActiveRecord::Base
15
+ money_fields :price, :cost
16
+ end
17
+
18
+ let(:model) { Model.create!(:price => 5.to_money, :cost_in_cents => 300) }
19
+
20
+ it "should convert to from money and underlying cents object" do
21
+ expect(model.price_in_cents).to eq 500
22
+ expect(model.cost.cents).to eq 300
23
+ end
24
+ end
25
+
@@ -0,0 +1,117 @@
1
+ require 'spec_helper'
2
+
3
+ describe Money do
4
+ it "should get correct direction class" do
5
+ expect(Money.new(-1).direction_class).to eq 'negative'
6
+ expect(Money.new(0).direction_class).to eq 'zero'
7
+ expect(Money.new(1).direction_class).to eq 'positive'
8
+ end
9
+
10
+ it "should round correctly" do
11
+ money = Money.new(511)
12
+ expect(money.round.cents).to eq 500
13
+ expect(money.round(10).cents).to eq 510
14
+ expect(money.round(1).cents).to eq 511
15
+
16
+ expect(money.round(100, 'up').cents).to eq 600
17
+ expect(money.round(100, 'down').cents).to eq 500
18
+ expect(money.round(100, 'nearest').cents).to eq 500
19
+ end
20
+
21
+ it "should deny division of money (to prevent rounding errors)" do
22
+ expect { Money.new(50)/10 }.to raise_error(RuntimeError)
23
+ end
24
+
25
+ it "should split money correctly" do
26
+ money = Money.new(100)
27
+
28
+ expect { money.split_between(0) }.to raise_error(ArgumentError)
29
+ expect { money.split_between(-100) }.to raise_error(ArgumentError)
30
+ expect { money.split_between(0.1) }.to raise_error(RuntimeError)
31
+
32
+ expect(money.split_between(3)).to eq [34,33,33].map{ |i| Money.new(i)}
33
+
34
+ expect { money.split_between([]) }.to raise_error(ArgumentError)
35
+ expect { money.split_between([-1,1]) }.to raise_error(ArgumentError)
36
+
37
+ expect(money.split_between([1,2,2,5])).to eq [10,20,20,50].map{ |i| Money.new(i)}
38
+ expect(money.split_between([1,2])).to eq [33,67].map{ |i| Money.new(i)}
39
+
40
+ money_negative = Money.new(-100)
41
+ expect(money_negative.split_between(3)).to eq [-32,-34,-34].map{ |i| Money.new(i)}
42
+ expect(money_negative.split_between([1,2,2,5])).to eq [-10,-20,-20,-50].map{ |i| Money.new(i)}
43
+ expect(money_negative.split_between([1,2])).to eq [-33,-67].map{ |i| Money.new(i)}
44
+
45
+ money_zero = Money.new(0)
46
+ expect(money_zero.split_between([1,2])).to eq Array.new(2,money_zero)
47
+ end
48
+
49
+ it "should round split amounts" do
50
+ expect(Money.new(200).split_between([81, 40, 40])).to eq [100, 50, 50].map{ |i| Money.new(i) }
51
+ end
52
+
53
+ it "should assign rounding to max absolute" do
54
+ # the -8 is the largest ABSOLUTE number...
55
+ # -8 / 3 == -2.66666 = 2.67
56
+ # it receives the rounding of +1c
57
+ expected_money = [33, -266, 233, 100].map{ |i| Money.new(i) }
58
+ expect(Money.new(100).split_between([1, -8, 7, 3])).to eq expected_money
59
+ end
60
+
61
+ it "should return a nice, Big Decimal if so converted" do
62
+ money = Money.new(1428)
63
+ bigdecimal = BigDecimal.new("14.28")
64
+ expect(money.to_d).to eq bigdecimal
65
+ end
66
+
67
+ it "should be createable from strings and numbers" do
68
+ money = Money.new(100)
69
+ expect("1".to_money.cents).to eq money.cents
70
+ expect(BigDecimal.new('1').to_money.cents).to eq money.cents
71
+ expect(100.total_money.cents).to eq money.cents
72
+ end
73
+
74
+ let(:money_positive) { Money.new(100) }
75
+ let(:money_negative) { Money.new(-100) }
76
+ let(:money_zero) { Money.new(0) }
77
+
78
+ it "should know positives, negatives, and absolutes" do
79
+ expect(money_positive.positive?).to eq true
80
+ expect(money_positive.negative?).to eq false
81
+
82
+ expect(money_negative.positive?).to eq false
83
+ expect(money_negative.negative?).to eq true
84
+
85
+ expect(money_zero.positive?).to eq false
86
+ expect(money_zero.negative?).to eq false
87
+
88
+ expect(money_positive.abs).to eq money_positive
89
+ expect(money_negative.abs).to eq money_positive
90
+ expect(money_zero.abs).to eq money_zero
91
+ end
92
+
93
+ it "should format the output correctly" do
94
+ expect(money_positive.format).to eq "<span class=\"money positive\">$1.00</span>"
95
+ expect(money_negative.format).to eq "<span class=\"money negative\">-$1.00</span>"
96
+ expect(money_zero.format).to eq "<span class=\"money zero\">$0.00</span>"
97
+
98
+ expect(money_positive.format(:html)).to eq "<span class=\"money positive\">$1.00</span>"
99
+ expect(money_negative.format(:html)).to eq "<span class=\"money negative\">-$1.00</span>"
100
+ expect(money_zero.format(:html)).to eq "<span class=\"money zero\">$0.00</span>"
101
+
102
+ expect(money_positive.format(:signed)).to eq "+$1.00"
103
+ expect(money_negative.format(:signed)).to eq "-$1.00"
104
+ expect(money_zero.format(:signed)).to eq "$0.00"
105
+
106
+ expect("1.50".to_money.format(:separator => '~')).to eq "$1~50"
107
+ end
108
+
109
+ it 'should format cents where appropriate' do
110
+ expect('1.50'.to_money.format(:no_cents)).to eq '$1'
111
+ expect('1.00'.to_money.format(:no_cents)).to eq '$1'
112
+
113
+ expect('1.50'.to_money.format(:hide_zero_cents)).to eq '$1.50'
114
+ expect('1.00'.to_money.format(:hide_zero_cents)).to eq '$1'
115
+ end
116
+
117
+ end
data/spec/schema.rb ADDED
@@ -0,0 +1,5 @@
1
+ ActiveRecord::Schema.define(:version => 1) do
2
+ create_table :models do |t|
3
+ t.integer :price_in_cents, :cost_in_cents
4
+ end
5
+ end
@@ -0,0 +1,19 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper.rb"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+
8
+ require 'rubygems'
9
+ require 'bundler/setup'
10
+
11
+ require 'support/coverage_loader'
12
+
13
+ require 'active_record'
14
+ require 'money_extensions'
15
+
16
+ RSpec.configure do |config|
17
+ config.run_all_when_everything_filtered = true
18
+ config.filter_run :focus
19
+ end
@@ -0,0 +1,26 @@
1
+ MINIMUM_COVERAGE = 80
2
+
3
+ unless ENV['COVERAGE'] == 'off'
4
+ require 'simplecov'
5
+ require 'simplecov-rcov'
6
+ require 'coveralls'
7
+ Coveralls.wear!
8
+
9
+ SimpleCov.formatters = [
10
+ SimpleCov::Formatter::RcovFormatter,
11
+ Coveralls::SimpleCov::Formatter
12
+ ]
13
+ SimpleCov.start do
14
+ add_filter '/vendor/'
15
+ add_filter '/spec/'
16
+ add_group 'lib', 'lib'
17
+ end
18
+ SimpleCov.at_exit do
19
+ SimpleCov.result.format!
20
+ percent = SimpleCov.result.covered_percent
21
+ unless percent >= MINIMUM_COVERAGE
22
+ puts "Coverage must be above #{MINIMUM_COVERAGE}%. It is #{"%.2f" % percent}%"
23
+ Kernel.exit(1)
24
+ end
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,210 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: money_extensions
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Michael Noack
8
+ - Alessandro Berardi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2016-02-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: money
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 3.0.0
21
+ - - "<"
22
+ - !ruby/object:Gem::Version
23
+ version: 6.0.0
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: 3.0.0
31
+ - - "<"
32
+ - !ruby/object:Gem::Version
33
+ version: 6.0.0
34
+ - !ruby/object:Gem::Dependency
35
+ name: rake
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ type: :development
42
+ prerelease: false
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ - !ruby/object:Gem::Dependency
49
+ name: rspec
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: simplecov
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ type: :development
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ - !ruby/object:Gem::Dependency
77
+ name: simplecov-rcov
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ - !ruby/object:Gem::Dependency
91
+ name: coveralls
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ type: :development
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ - !ruby/object:Gem::Dependency
105
+ name: sqlite3
106
+ requirement: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ type: :development
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ - !ruby/object:Gem::Dependency
119
+ name: activerecord
120
+ requirement: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ type: :development
126
+ prerelease: false
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ - !ruby/object:Gem::Dependency
133
+ name: travis
134
+ requirement: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ type: :development
140
+ prerelease: false
141
+ version_requirements: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ description: These are extensions from the money/currency classes.
147
+ email: support@travellink.com.au
148
+ executables: []
149
+ extensions: []
150
+ extra_rdoc_files: []
151
+ files:
152
+ - ".gitignore"
153
+ - ".rspec"
154
+ - ".travis.yml"
155
+ - Gemfile
156
+ - LICENSE
157
+ - README.md
158
+ - Rakefile
159
+ - gemfiles/rails2.gemfile
160
+ - gemfiles/rails3.gemfile
161
+ - gemfiles/rails4.gemfile
162
+ - lib/money_extensions.rb
163
+ - lib/money_extensions/active_record/extensions.rb
164
+ - lib/money_extensions/array.rb
165
+ - lib/money_extensions/big_decimal.rb
166
+ - lib/money_extensions/extensions.rb
167
+ - lib/money_extensions/fixnum.rb
168
+ - lib/money_extensions/money_field.rb
169
+ - lib/money_extensions/numeric.rb
170
+ - lib/money_extensions/string.rb
171
+ - lib/money_extensions/version.rb
172
+ - money_extensions.gemspec
173
+ - rails/init.rb
174
+ - spec/big_decimal_spec.rb
175
+ - spec/money_field_spec.rb
176
+ - spec/money_spec.rb
177
+ - spec/schema.rb
178
+ - spec/spec_helper.rb
179
+ - spec/support/coverage_loader.rb
180
+ homepage: http://github.com/sealink/money_extensions
181
+ licenses:
182
+ - MIT
183
+ metadata: {}
184
+ post_install_message:
185
+ rdoc_options: []
186
+ require_paths:
187
+ - lib
188
+ required_ruby_version: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: '0'
193
+ required_rubygems_version: !ruby/object:Gem::Requirement
194
+ requirements:
195
+ - - ">="
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ requirements: []
199
+ rubyforge_project:
200
+ rubygems_version: 2.4.8
201
+ signing_key:
202
+ specification_version: 4
203
+ summary: Set of extensions to the money gem used by TravelLink Technology.
204
+ test_files:
205
+ - spec/big_decimal_spec.rb
206
+ - spec/money_field_spec.rb
207
+ - spec/money_spec.rb
208
+ - spec/schema.rb
209
+ - spec/spec_helper.rb
210
+ - spec/support/coverage_loader.rb