money_extensions 0.0.2

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.
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