money_extensions 0.0.2 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/release.yml +59 -0
- data/.github/workflows/ruby.yml +24 -0
- data/.gitignore +2 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +19 -0
- data/README.md +17 -4
- data/gemfiles/{rails4.gemfile → rails60.gemfile} +2 -3
- data/gemfiles/rails61.gemfile +7 -0
- data/gemfiles/rails70.gemfile +7 -0
- data/lib/money_extensions/active_record/extensions.rb +9 -18
- data/lib/money_extensions/array.rb +3 -1
- data/lib/money_extensions/html_format.rb +37 -0
- data/lib/money_extensions/integer.rb +8 -0
- data/lib/money_extensions/money_field.rb +4 -7
- data/lib/money_extensions/numeric.rb +4 -2
- data/lib/money_extensions/split_between.rb +54 -0
- data/lib/money_extensions/string.rb +4 -2
- data/lib/money_extensions/version.rb +3 -1
- data/lib/money_extensions.rb +3 -4
- data/money_extensions.gemspec +6 -5
- data/spec/big_decimal_spec.rb +6 -6
- data/spec/html_format_spec.rb +48 -0
- data/spec/money_spec.rb +3 -45
- data/spec/support/coverage_loader.rb +2 -25
- metadata +25 -26
- data/.travis.yml +0 -30
- data/gemfiles/rails2.gemfile +0 -8
- data/gemfiles/rails3.gemfile +0 -8
- data/lib/money_extensions/big_decimal.rb +0 -12
- data/lib/money_extensions/extensions.rb +0 -189
- data/lib/money_extensions/fixnum.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e3f8f70cfcaef8f46dcb30b9f80f2116b0d45210d043fa21c4e7fbbb8c3a4915
|
4
|
+
data.tar.gz: 949f3cee9063fc0a604087d06925739a8beacf6482cae52e3cb2f6ae3fb7126a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc5c87edadd0d2b03e45810f399037ea32b86f1d9aac412c7a79022b2202846e7b6a8f4be352d5c3705562b225123dbff71eb33c4aac3bb88fe1cc649d6f9300
|
7
|
+
data.tar.gz: c8caeb5dd952872c50805478ca22e04a8cd0caf63b934896133d30ea0d569e23e2f186bf9ce3726a04d8ed518ed0ea866a8ae99b8971969f09b25d92318b1f40
|
@@ -0,0 +1,59 @@
|
|
1
|
+
name: Release
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
tags:
|
6
|
+
- "v*"
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
build:
|
10
|
+
name: Build
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
steps:
|
13
|
+
- name: Checkout
|
14
|
+
uses: actions/checkout@v2
|
15
|
+
- uses: ruby/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
bundler-cache: true
|
18
|
+
- run: bundle exec rake
|
19
|
+
|
20
|
+
release:
|
21
|
+
needs: build
|
22
|
+
name: Release
|
23
|
+
runs-on: ubuntu-latest
|
24
|
+
steps:
|
25
|
+
- name: Checkout
|
26
|
+
uses: actions/checkout@v2
|
27
|
+
|
28
|
+
- name: Generate Changelog
|
29
|
+
run: |
|
30
|
+
# Get version from github ref (remove 'refs/tags/' and prefix 'v')
|
31
|
+
version="${GITHUB_REF#refs/tags/v}"
|
32
|
+
npx changelog-parser CHANGELOG.md | jq -cr ".versions | .[] | select(.version == \"$version\") | .body" > ${{ github.workflow }}-CHANGELOG.txt
|
33
|
+
|
34
|
+
- name: Release
|
35
|
+
uses: softprops/action-gh-release@v1
|
36
|
+
with:
|
37
|
+
body_path: ${{ github.workflow }}-CHANGELOG.txt
|
38
|
+
env:
|
39
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
40
|
+
|
41
|
+
publish:
|
42
|
+
needs: [build, release]
|
43
|
+
name: Publish
|
44
|
+
runs-on: ubuntu-latest
|
45
|
+
|
46
|
+
steps:
|
47
|
+
- uses: actions/checkout@v2
|
48
|
+
- uses: ruby/setup-ruby@v1
|
49
|
+
|
50
|
+
- name: Publish to RubyGems
|
51
|
+
run: |
|
52
|
+
mkdir -p $HOME/.gem
|
53
|
+
touch $HOME/.gem/credentials
|
54
|
+
chmod 0600 $HOME/.gem/credentials
|
55
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
56
|
+
gem build *.gemspec
|
57
|
+
gem push *.gem
|
58
|
+
env:
|
59
|
+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
name: Build and Test
|
2
|
+
on: [push, pull_request]
|
3
|
+
jobs:
|
4
|
+
test:
|
5
|
+
strategy:
|
6
|
+
fail-fast: false
|
7
|
+
matrix:
|
8
|
+
gemfile: [rails60, rails61, rails70]
|
9
|
+
ruby: ["2.7", "3.0", "3.1"]
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
env:
|
12
|
+
BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile
|
13
|
+
steps:
|
14
|
+
- uses: actions/checkout@v2
|
15
|
+
- uses: ruby/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
ruby-version: ${{ matrix.ruby }}
|
18
|
+
bundler-cache: true
|
19
|
+
- run: bundle exec rake
|
20
|
+
- name: Coveralls
|
21
|
+
uses: coverallsapp/github-action@master
|
22
|
+
with:
|
23
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
24
|
+
path-to-lcov: coverage/lcov.info
|
data/.gitignore
CHANGED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.1.0
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## 1.2.0
|
4
|
+
|
5
|
+
- [PLAT-183] Build with Ruby 3.1 / Rails 7 and publish coverage with github action
|
6
|
+
|
7
|
+
## 1.1.0
|
8
|
+
|
9
|
+
- [TT-8625] Update to build with github actions / ruby 3.0 / rails 6.1
|
10
|
+
|
11
|
+
## 1.0.0
|
12
|
+
|
13
|
+
* [TT-5845] Update to support money gems >= 6
|
14
|
+
|
15
|
+
## 0.1.0
|
16
|
+
|
17
|
+
* Using Fixnum is deprecated
|
18
|
+
* Drop rails 2 and old ruby support
|
19
|
+
* Use coverage kit to enforce maximum coverage
|
data/README.md
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
Money Extensions
|
2
|
-
================
|
1
|
+
# Money Extensions
|
3
2
|
|
4
|
-
[![
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/money_extensions.svg)](http://badge.fury.io/rb/money_extensions)
|
4
|
+
[![Build Status](https://github.com/sealink/money_extensions/workflows/Build%20and%20Test/badge.svg?branch=master)](https://github.com/sealink/money_extensions/actions)
|
5
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
6
|
[![Code Climate](https://codeclimate.com/github/sealink/money_extensions.png)](https://codeclimate.com/github/sealink/money_extensions)
|
8
7
|
|
9
8
|
# DESCRIPTION
|
@@ -11,7 +10,10 @@ Money Extensions
|
|
11
10
|
Extends the money classes with helpful functions and currency functions
|
12
11
|
|
13
12
|
e.g. in model
|
13
|
+
|
14
|
+
```ruby
|
14
15
|
money_fields :total, :discount
|
16
|
+
```
|
15
17
|
|
16
18
|
This will by default map a money object "total" to a database column total_in_cents and the same for discount.
|
17
19
|
|
@@ -24,3 +26,14 @@ See spec directory for examples.
|
|
24
26
|
Add to your Gemfile:
|
25
27
|
gem 'money_extensions'
|
26
28
|
|
29
|
+
# RELEASE
|
30
|
+
|
31
|
+
To publish a new version of this gem the following steps must be taken.
|
32
|
+
|
33
|
+
- Update the version in the following files
|
34
|
+
```
|
35
|
+
CHANGELOG.md
|
36
|
+
lib/quick_travel/version.rb
|
37
|
+
```
|
38
|
+
- Create a tag using the format v0.1.0
|
39
|
+
- Follow build progress in GitHub actions
|
@@ -1,6 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Extensions
|
3
|
-
|
4
5
|
def self.included(base)
|
5
6
|
base.extend(ClassMethods)
|
6
7
|
end
|
@@ -18,20 +19,14 @@ module ActiveRecord
|
|
18
19
|
|
19
20
|
field_name = "#{attr_name}_iso_code"
|
20
21
|
|
21
|
-
composed_of attr_name, :
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
composed_of attr_name, class_name: 'Money::Currency',
|
23
|
+
mapping: [field_name, 'iso_code'],
|
24
|
+
allow_nil: true,
|
25
|
+
constructor: proc { |value| Money::Currency.new(value) unless value.blank? }
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
where(:currency_iso_code => currency.iso_code)
|
27
|
+
scope :for_currency, lambda { |currency|
|
28
|
+
where(currency_iso_code: currency.iso_code)
|
29
29
|
}
|
30
|
-
else
|
31
|
-
named_scope :for_currency, lambda{ |currency|
|
32
|
-
{:conditions => {:currency_iso_code => currency.iso_code}}
|
33
|
-
}
|
34
|
-
end
|
35
30
|
|
36
31
|
if options[:default]
|
37
32
|
before_validation :set_default_currency
|
@@ -43,13 +38,9 @@ module ActiveRecord
|
|
43
38
|
METHOD
|
44
39
|
end
|
45
40
|
|
46
|
-
if options[:required]
|
47
|
-
validates_presence_of :currency_iso_code
|
48
|
-
end
|
41
|
+
validates_presence_of :currency_iso_code if options[:required]
|
49
42
|
end
|
50
|
-
|
51
43
|
end
|
52
|
-
|
53
44
|
end
|
54
45
|
end
|
55
46
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/string/output_safety'
|
4
|
+
|
5
|
+
module HtmlFormat
|
6
|
+
def format(**rules)
|
7
|
+
html_wrap = rules.delete(:html_wrap)
|
8
|
+
return html_wrap(super(**rules)) if html_wrap
|
9
|
+
|
10
|
+
super(**rules)
|
11
|
+
end
|
12
|
+
|
13
|
+
def as_html(**rules)
|
14
|
+
rules[:html_wrap] = true
|
15
|
+
format(**rules).html_safe
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def html_wrap(str)
|
21
|
+
"<span class=\"money #{direction_class}\">#{str}</span>"
|
22
|
+
end
|
23
|
+
|
24
|
+
def direction_class
|
25
|
+
if cents > 0
|
26
|
+
'positive'
|
27
|
+
elsif cents < 0
|
28
|
+
'negative'
|
29
|
+
else
|
30
|
+
'zero'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Money
|
36
|
+
prepend HtmlFormat
|
37
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module MoneyField
|
3
4
|
def self.included(base)
|
4
5
|
base.extend(ClassMethods)
|
5
6
|
end
|
@@ -23,12 +24,10 @@ module MoneyField
|
|
23
24
|
METHOD
|
24
25
|
end
|
25
26
|
|
26
|
-
|
27
27
|
def money_fields(*attributes)
|
28
|
-
attributes.each {|a|
|
28
|
+
attributes.each { |a| money_field(a) }
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
31
|
def money(*fields)
|
33
32
|
fields.each do |field|
|
34
33
|
class_eval <<-METHOD
|
@@ -38,7 +37,5 @@ module MoneyField
|
|
38
37
|
METHOD
|
39
38
|
end
|
40
39
|
end
|
41
|
-
|
42
40
|
end
|
43
|
-
|
44
|
-
end
|
41
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Numeric
|
2
4
|
# Converts this numeric to a Money object in the default currency. It
|
3
5
|
# multiplies the numeric value by 100 and treats that as cents.
|
@@ -11,6 +13,6 @@ class Numeric
|
|
11
13
|
# require 'bigdecimal'
|
12
14
|
# BigDecimal.new('100').to_money => #<Money @cents=10000>
|
13
15
|
def to_money(currency = nil)
|
14
|
-
::Money.new((self * 100).round, currency)
|
16
|
+
::Money.new((self * 100).round, currency || Money.default_currency)
|
15
17
|
end
|
16
|
-
end
|
18
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SplitBetween
|
4
|
+
# Override division -- should not do it, but use split_between
|
5
|
+
def /(*_params)
|
6
|
+
raise "Division of money NOT allowed - use 'split_between' to avoid rounding errors"
|
7
|
+
end
|
8
|
+
|
9
|
+
# Split the money between the specified number - and return an array of money
|
10
|
+
# Remainder in splits are given to the highest value e.g.
|
11
|
+
# $2.00 splits [40, 81, 40] into [49, 102, 49]
|
12
|
+
# $1.01 splits [ 1, 1, 1] into [35, 33, 33]
|
13
|
+
def split_between(params)
|
14
|
+
# if just a number is passed in, then money is split equally
|
15
|
+
if params.is_a?(Integer)
|
16
|
+
divisor = params
|
17
|
+
raise ArgumentError, 'Can only split up over a positive number' if divisor < 1
|
18
|
+
|
19
|
+
rounded_split = lossy_divide(divisor)
|
20
|
+
results = Array.new(divisor, rounded_split) # Create with 'divisor' num elements
|
21
|
+
|
22
|
+
# if an array of monies is passed in, then split in proportions
|
23
|
+
elsif params.is_a?(Array)
|
24
|
+
|
25
|
+
raise ArgumentError, 'Can only split up over at least one ration' if params.empty? && !zero?
|
26
|
+
|
27
|
+
results = params.map { |p| p.is_a?(::Money) ? p.cents : p }
|
28
|
+
|
29
|
+
total = results.inject(:+)
|
30
|
+
if total.zero?
|
31
|
+
return Array.new(results.size, ::Money.new(0)) if zero?
|
32
|
+
|
33
|
+
raise ArgumentError, "Total of ratios should not be zero! You sent in: #{params.inspect}"
|
34
|
+
end
|
35
|
+
|
36
|
+
results.map! do |ratio|
|
37
|
+
::Money.new((cents * (ratio.to_f / total)).round)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
raise 'Either a Integer or array has to be passed in for splitting money'
|
41
|
+
end
|
42
|
+
|
43
|
+
# Distribute rounding to max absolute to avoid a $0 amount getting the rounding
|
44
|
+
biggest_value_index = results.index(results.max_by(&:abs))
|
45
|
+
results[biggest_value_index] += self - results.total_money
|
46
|
+
|
47
|
+
results
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Money
|
52
|
+
alias lossy_divide /
|
53
|
+
include SplitBetween
|
54
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class String
|
2
4
|
# Converts this string to a float and then to a Money object in the default currency.
|
3
5
|
# It multiplies the converted numeric value by 100 and treats that as cents.
|
@@ -9,6 +11,6 @@ class String
|
|
9
11
|
# '100'.to_money => #<Money @cents=10000>
|
10
12
|
# '100.37'.to_money => #<Money @cents=10037>
|
11
13
|
def to_money(currency = nil)
|
12
|
-
Money.new((
|
14
|
+
Money.new((to_f * 100).round, currency)
|
13
15
|
end
|
14
|
-
end
|
16
|
+
end
|
data/lib/money_extensions.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module MoneyExtensions
|
2
2
|
require 'money'
|
3
3
|
require 'money_extensions/array'
|
4
|
-
require 'money_extensions/
|
5
|
-
require 'money_extensions/
|
6
|
-
require 'money_extensions/
|
4
|
+
require 'money_extensions/integer'
|
5
|
+
require 'money_extensions/split_between'
|
6
|
+
require 'money_extensions/html_format'
|
7
7
|
require 'money_extensions/money_field'
|
8
8
|
require 'money_extensions/numeric'
|
9
9
|
require 'money_extensions/string'
|
@@ -14,4 +14,3 @@ module MoneyExtensions
|
|
14
14
|
end
|
15
15
|
|
16
16
|
end
|
17
|
-
|
data/money_extensions.gemspec
CHANGED
@@ -19,13 +19,14 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_dependency 'money', '>=
|
22
|
+
spec.add_dependency 'money', '>= 6.0.0'
|
23
|
+
spec.add_dependency 'activesupport', '>= 6'
|
24
|
+
|
23
25
|
spec.add_development_dependency 'rake'
|
26
|
+
spec.add_development_dependency 'rubocop'
|
24
27
|
spec.add_development_dependency 'rspec'
|
25
|
-
spec.add_development_dependency '
|
26
|
-
spec.add_development_dependency 'simplecov-rcov'
|
27
|
-
spec.add_development_dependency 'coveralls'
|
28
|
+
spec.add_development_dependency 'coverage-kit'
|
28
29
|
spec.add_development_dependency 'sqlite3'
|
29
30
|
spec.add_development_dependency 'activerecord'
|
30
|
-
spec.add_development_dependency '
|
31
|
+
spec.add_development_dependency 'pry-byebug'
|
31
32
|
end
|
data/spec/big_decimal_spec.rb
CHANGED
@@ -3,15 +3,15 @@ require 'spec_helper'
|
|
3
3
|
describe BigDecimal do
|
4
4
|
|
5
5
|
it "should allow verifying if it's a positive value" do
|
6
|
-
expect(BigDecimal
|
7
|
-
expect(BigDecimal
|
8
|
-
expect(BigDecimal
|
6
|
+
expect(BigDecimal('-1')).to_not be_positive
|
7
|
+
expect(BigDecimal('0')).to_not be_positive
|
8
|
+
expect(BigDecimal('1')).to be_positive
|
9
9
|
end
|
10
10
|
|
11
11
|
it "should allow verifying if it's a negative value" do
|
12
|
-
expect(BigDecimal
|
13
|
-
expect(BigDecimal
|
14
|
-
expect(BigDecimal
|
12
|
+
expect(BigDecimal('-1')).to be_negative
|
13
|
+
expect(BigDecimal('0')).to_not be_negative
|
14
|
+
expect(BigDecimal('1')).to_not be_negative
|
15
15
|
end
|
16
16
|
|
17
17
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'money'
|
2
|
+
require 'money_extensions/string'
|
3
|
+
require 'money_extensions/html_format'
|
4
|
+
|
5
|
+
RSpec.describe Money do
|
6
|
+
before { Money.locale_backend = nil }
|
7
|
+
after { Money.locale_backend = :legacy }
|
8
|
+
|
9
|
+
let(:money_positive) { Money.new(100) }
|
10
|
+
let(:money_negative) { Money.new(-100) }
|
11
|
+
let(:money_zero) { Money.new(0) }
|
12
|
+
|
13
|
+
subject { money_positive.format(*rules) }
|
14
|
+
|
15
|
+
it "should format the output correctly" do
|
16
|
+
expect(money_negative.format(html_wrap: true, sign_before_symbol: true)).to eq "<span class=\"money negative\">-$1.00</span>"
|
17
|
+
expect(money_positive.format(html_wrap: true)).to eq "<span class=\"money positive\">$1.00</span>"
|
18
|
+
expect(money_zero.format(html_wrap: true)).to eq "<span class=\"money zero\">$0.00</span>"
|
19
|
+
|
20
|
+
expect(money_positive.format(html_wrap: true)).to eq "<span class=\"money positive\">$1.00</span>"
|
21
|
+
expect(money_negative.format(html_wrap: true, sign_before_symbol: true)).to eq "<span class=\"money negative\">-$1.00</span>"
|
22
|
+
expect(money_zero.format(html_wrap: true)).to eq "<span class=\"money zero\">$0.00</span>"
|
23
|
+
|
24
|
+
expect(money_positive.format(signed: true, sign_positive: true, sign_before_symbol: true)).to eq "+$1.00"
|
25
|
+
expect(money_negative.format(signed: true, sign_before_symbol: true)).to eq "-$1.00"
|
26
|
+
expect(money_zero.format(signed: true)).to eq "$0.00"
|
27
|
+
|
28
|
+
expect(money_positive.format(separator: '~')).to eq "$1~00"
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should work with as_html' do
|
32
|
+
expect(money_negative.as_html(sign_before_symbol: true)).to eq "<span class=\"money negative\">-$1.00</span>"
|
33
|
+
expect(money_positive.as_html).to eq "<span class=\"money positive\">$1.00</span>"
|
34
|
+
expect(money_zero.as_html).to eq "<span class=\"money zero\">$0.00</span>"
|
35
|
+
|
36
|
+
expect(money_positive.as_html).to eq "<span class=\"money positive\">$1.00</span>"
|
37
|
+
expect(money_negative.as_html(sign_before_symbol: true)).to eq "<span class=\"money negative\">-$1.00</span>"
|
38
|
+
expect(money_zero.as_html).to eq "<span class=\"money zero\">$0.00</span>"
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should format cents where appropriate' do
|
42
|
+
expect('1.50'.to_money.format(no_cents: true)).to eq '$1'
|
43
|
+
expect('1.00'.to_money.format(no_cents: true)).to eq '$1'
|
44
|
+
|
45
|
+
expect('1.50'.to_money.format(no_cents_if_whole: true)).to eq '$1.50'
|
46
|
+
expect('1.00'.to_money.format(no_cents_if_whole: true)).to eq '$1'
|
47
|
+
end
|
48
|
+
end
|
data/spec/money_spec.rb
CHANGED
@@ -1,23 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
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
4
|
it "should deny division of money (to prevent rounding errors)" do
|
22
5
|
expect { Money.new(50)/10 }.to raise_error(RuntimeError)
|
23
6
|
end
|
@@ -38,7 +21,7 @@ describe Money do
|
|
38
21
|
expect(money.split_between([1,2])).to eq [33,67].map{ |i| Money.new(i)}
|
39
22
|
|
40
23
|
money_negative = Money.new(-100)
|
41
|
-
expect(money_negative.split_between(3)).to eq [-
|
24
|
+
expect(money_negative.split_between(3)).to eq [-34,-33,-33].map{ |i| Money.new(i)}
|
42
25
|
expect(money_negative.split_between([1,2,2,5])).to eq [-10,-20,-20,-50].map{ |i| Money.new(i)}
|
43
26
|
expect(money_negative.split_between([1,2])).to eq [-33,-67].map{ |i| Money.new(i)}
|
44
27
|
|
@@ -60,14 +43,14 @@ describe Money do
|
|
60
43
|
|
61
44
|
it "should return a nice, Big Decimal if so converted" do
|
62
45
|
money = Money.new(1428)
|
63
|
-
bigdecimal = BigDecimal
|
46
|
+
bigdecimal = BigDecimal("14.28")
|
64
47
|
expect(money.to_d).to eq bigdecimal
|
65
48
|
end
|
66
49
|
|
67
50
|
it "should be createable from strings and numbers" do
|
68
51
|
money = Money.new(100)
|
69
52
|
expect("1".to_money.cents).to eq money.cents
|
70
|
-
expect(BigDecimal
|
53
|
+
expect(BigDecimal('1').to_money.cents).to eq money.cents
|
71
54
|
expect(100.total_money.cents).to eq money.cents
|
72
55
|
end
|
73
56
|
|
@@ -89,29 +72,4 @@ describe Money do
|
|
89
72
|
expect(money_negative.abs).to eq money_positive
|
90
73
|
expect(money_zero.abs).to eq money_zero
|
91
74
|
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
75
|
end
|
@@ -1,26 +1,3 @@
|
|
1
|
-
|
1
|
+
require 'coverage/kit'
|
2
2
|
|
3
|
-
|
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
|
3
|
+
Coverage::Kit.setup(minimum_coverage: 86.6)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: money_extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Noack
|
@@ -9,16 +9,13 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-01-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: money
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
18
|
- - ">="
|
19
|
-
- !ruby/object:Gem::Version
|
20
|
-
version: 3.0.0
|
21
|
-
- - "<"
|
22
19
|
- !ruby/object:Gem::Version
|
23
20
|
version: 6.0.0
|
24
21
|
type: :runtime
|
@@ -26,27 +23,24 @@ dependencies:
|
|
26
23
|
version_requirements: !ruby/object:Gem::Requirement
|
27
24
|
requirements:
|
28
25
|
- - ">="
|
29
|
-
- !ruby/object:Gem::Version
|
30
|
-
version: 3.0.0
|
31
|
-
- - "<"
|
32
26
|
- !ruby/object:Gem::Version
|
33
27
|
version: 6.0.0
|
34
28
|
- !ruby/object:Gem::Dependency
|
35
|
-
name:
|
29
|
+
name: activesupport
|
36
30
|
requirement: !ruby/object:Gem::Requirement
|
37
31
|
requirements:
|
38
32
|
- - ">="
|
39
33
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
41
|
-
type: :
|
34
|
+
version: '6'
|
35
|
+
type: :runtime
|
42
36
|
prerelease: false
|
43
37
|
version_requirements: !ruby/object:Gem::Requirement
|
44
38
|
requirements:
|
45
39
|
- - ">="
|
46
40
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
41
|
+
version: '6'
|
48
42
|
- !ruby/object:Gem::Dependency
|
49
|
-
name:
|
43
|
+
name: rake
|
50
44
|
requirement: !ruby/object:Gem::Requirement
|
51
45
|
requirements:
|
52
46
|
- - ">="
|
@@ -60,7 +54,7 @@ dependencies:
|
|
60
54
|
- !ruby/object:Gem::Version
|
61
55
|
version: '0'
|
62
56
|
- !ruby/object:Gem::Dependency
|
63
|
-
name:
|
57
|
+
name: rubocop
|
64
58
|
requirement: !ruby/object:Gem::Requirement
|
65
59
|
requirements:
|
66
60
|
- - ">="
|
@@ -74,7 +68,7 @@ dependencies:
|
|
74
68
|
- !ruby/object:Gem::Version
|
75
69
|
version: '0'
|
76
70
|
- !ruby/object:Gem::Dependency
|
77
|
-
name:
|
71
|
+
name: rspec
|
78
72
|
requirement: !ruby/object:Gem::Requirement
|
79
73
|
requirements:
|
80
74
|
- - ">="
|
@@ -88,7 +82,7 @@ dependencies:
|
|
88
82
|
- !ruby/object:Gem::Version
|
89
83
|
version: '0'
|
90
84
|
- !ruby/object:Gem::Dependency
|
91
|
-
name:
|
85
|
+
name: coverage-kit
|
92
86
|
requirement: !ruby/object:Gem::Requirement
|
93
87
|
requirements:
|
94
88
|
- - ">="
|
@@ -130,7 +124,7 @@ dependencies:
|
|
130
124
|
- !ruby/object:Gem::Version
|
131
125
|
version: '0'
|
132
126
|
- !ruby/object:Gem::Dependency
|
133
|
-
name:
|
127
|
+
name: pry-byebug
|
134
128
|
requirement: !ruby/object:Gem::Requirement
|
135
129
|
requirements:
|
136
130
|
- - ">="
|
@@ -149,29 +143,34 @@ executables: []
|
|
149
143
|
extensions: []
|
150
144
|
extra_rdoc_files: []
|
151
145
|
files:
|
146
|
+
- ".github/dependabot.yml"
|
147
|
+
- ".github/workflows/release.yml"
|
148
|
+
- ".github/workflows/ruby.yml"
|
152
149
|
- ".gitignore"
|
153
150
|
- ".rspec"
|
154
|
-
- ".
|
151
|
+
- ".ruby-version"
|
152
|
+
- CHANGELOG.md
|
155
153
|
- Gemfile
|
156
154
|
- LICENSE
|
157
155
|
- README.md
|
158
156
|
- Rakefile
|
159
|
-
- gemfiles/
|
160
|
-
- gemfiles/
|
161
|
-
- gemfiles/
|
157
|
+
- gemfiles/rails60.gemfile
|
158
|
+
- gemfiles/rails61.gemfile
|
159
|
+
- gemfiles/rails70.gemfile
|
162
160
|
- lib/money_extensions.rb
|
163
161
|
- lib/money_extensions/active_record/extensions.rb
|
164
162
|
- lib/money_extensions/array.rb
|
165
|
-
- lib/money_extensions/
|
166
|
-
- lib/money_extensions/
|
167
|
-
- lib/money_extensions/fixnum.rb
|
163
|
+
- lib/money_extensions/html_format.rb
|
164
|
+
- lib/money_extensions/integer.rb
|
168
165
|
- lib/money_extensions/money_field.rb
|
169
166
|
- lib/money_extensions/numeric.rb
|
167
|
+
- lib/money_extensions/split_between.rb
|
170
168
|
- lib/money_extensions/string.rb
|
171
169
|
- lib/money_extensions/version.rb
|
172
170
|
- money_extensions.gemspec
|
173
171
|
- rails/init.rb
|
174
172
|
- spec/big_decimal_spec.rb
|
173
|
+
- spec/html_format_spec.rb
|
175
174
|
- spec/money_field_spec.rb
|
176
175
|
- spec/money_spec.rb
|
177
176
|
- spec/schema.rb
|
@@ -196,13 +195,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
196
195
|
- !ruby/object:Gem::Version
|
197
196
|
version: '0'
|
198
197
|
requirements: []
|
199
|
-
|
200
|
-
rubygems_version: 2.4.8
|
198
|
+
rubygems_version: 3.3.3
|
201
199
|
signing_key:
|
202
200
|
specification_version: 4
|
203
201
|
summary: Set of extensions to the money gem used by TravelLink Technology.
|
204
202
|
test_files:
|
205
203
|
- spec/big_decimal_spec.rb
|
204
|
+
- spec/html_format_spec.rb
|
206
205
|
- spec/money_field_spec.rb
|
207
206
|
- spec/money_spec.rb
|
208
207
|
- spec/schema.rb
|
data/.travis.yml
DELETED
@@ -1,30 +0,0 @@
|
|
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/gemfiles/rails2.gemfile
DELETED
data/gemfiles/rails3.gemfile
DELETED
@@ -1,189 +0,0 @@
|
|
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)
|