lare_round 0.0.2 → 1.0.0
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 +5 -5
- data/.github/workflows/ci.yml +37 -0
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +38 -0
- data/.ruby-version +1 -0
- data/Gemfile +8 -0
- data/README.md +32 -13
- data/Rakefile +2 -0
- data/bin/console +15 -0
- data/lare_round.gemspec +11 -12
- data/lib/lare_round/version.rb +3 -1
- data/lib/lare_round.rb +82 -84
- data/test/lare_round_test.rb +15 -18
- data/test/test_helper.rb +2 -1
- metadata +16 -86
- data/.travis.yml +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 75cbed7590d38dbbddd0ddb51341cc2fd9b3b55c614ebc347cad64ac8b675d6c
|
4
|
+
data.tar.gz: 99c9fa456289a7c6e384b90922b770532ce937874a226dedbae6a1e83cc32b49
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5d356f37278ac48cad5f49cba5a88587cc35c57e68a650126c9d066f86d101442cd97c17522c0fd2f0db88d9127cfe6dcb2aa755403883fe4006e1c9c98dba4
|
7
|
+
data.tar.gz: f1f3193af4b5acd7a09a43b9e3c5aee28ceb460730e0ab19a4d6d45d2d20cc0649a6616e6dd77ceee117b1a4e4903c60db909f74e4087fca0372003d1b46368b
|
@@ -0,0 +1,37 @@
|
|
1
|
+
name: Continuous Integration
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
branches: [ master ]
|
5
|
+
pull_request:
|
6
|
+
branches: [ master ]
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
test:
|
10
|
+
name: Rspec and Rubocop
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
|
13
|
+
strategy:
|
14
|
+
matrix:
|
15
|
+
ruby-version: ['3.0.4', '3.1.2','3.2.0']
|
16
|
+
|
17
|
+
steps:
|
18
|
+
- name: Check out code
|
19
|
+
uses: actions/checkout@v4
|
20
|
+
|
21
|
+
- name: Set up Ruby
|
22
|
+
uses: ruby/setup-ruby@v1.215.0
|
23
|
+
with:
|
24
|
+
ruby-version: ${{ matrix.ruby-version }}
|
25
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
26
|
+
|
27
|
+
- name: Rubocop
|
28
|
+
env:
|
29
|
+
RAILS_ENV: test
|
30
|
+
run: |
|
31
|
+
bundle exec rubocop
|
32
|
+
|
33
|
+
- name: Minitest
|
34
|
+
env:
|
35
|
+
RAILS_ENV: test
|
36
|
+
run: |
|
37
|
+
bundle exec rake test
|
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2023-09-11 13:29:52 UTC using RuboCop version 1.56.3.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 3
|
10
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
11
|
+
Metrics/AbcSize:
|
12
|
+
Max: 25
|
13
|
+
|
14
|
+
# Offense count: 3
|
15
|
+
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
16
|
+
# AllowedMethods: refine
|
17
|
+
Metrics/BlockLength:
|
18
|
+
Max: 78
|
19
|
+
|
20
|
+
# Offense count: 1
|
21
|
+
# Configuration parameters: CountComments, CountAsOne.
|
22
|
+
Metrics/ClassLength:
|
23
|
+
Max: 130
|
24
|
+
|
25
|
+
# Offense count: 1
|
26
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
27
|
+
Metrics/CyclomaticComplexity:
|
28
|
+
Max: 10
|
29
|
+
|
30
|
+
# Offense count: 2
|
31
|
+
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
32
|
+
Metrics/MethodLength:
|
33
|
+
Max: 18
|
34
|
+
|
35
|
+
# Offense count: 1
|
36
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
37
|
+
Metrics/PerceivedComplexity:
|
38
|
+
Max: 10
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.2.0
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -3,26 +3,27 @@
|
|
3
3
|
A collection of BigDecimal items e.g. invoice items can be rounded for displaying them in views. Rounding may apply a rounding error to the items such as the summed up rounded items will show deviation towards an invoice total with summed unrounded items. Which might cause confusion for customers and finance departments alike. Application of the largest remainder method can help to preserve the total sum for rounded parts thus eliminating this confusion.
|
4
4
|
|
5
5
|
## Build status
|
6
|
-
[](https://github.com/jethroo/lare_round/actions/workflows/ci.yml)
|
7
|
+
[](http://badge.fury.io/rb/lare_round)
|
8
|
+
<a href="https://codeclimate.com/github/jethroo/lare_round/maintainability"><img src="https://api.codeclimate.com/v1/badges/9d97f5a7c54707277efc/maintainability" /></a>
|
8
9
|
|
9
10
|
## Used in production
|
10
11
|
|
11
|
-
since 4th of
|
12
|
+
since 4th of September 2013
|
12
13
|
|
13
14
|
## Example
|
14
15
|
|
15
16
|
say we have an array of 3 invoice items which are stored in the database and your invoice calculations are precise to the 4th position after the decimal point:
|
16
17
|
|
17
18
|
```ruby
|
18
|
-
Array.new(3){BigDecimal
|
19
|
+
Array.new(3){ BigDecimal('0.3334') }
|
19
20
|
# => [#<BigDecimal:c75a38,'0.3334E0',9(18)>, #<BigDecimal:c759c0,'0.3334E0',9(18)>, #<BigDecimal:c75920,'0.3334E0',9(18)>]
|
20
21
|
```
|
21
22
|
say you have an invoice which is rendered as pdf which only needs to display the total you are fine, because you only
|
22
23
|
have to round once for displaying a customer friendly price:
|
23
24
|
|
24
25
|
```ruby
|
25
|
-
Array.new(3){BigDecimal
|
26
|
+
Array.new(3){ BigDecimal('0.3334') }.reduce(:+).round(2).to_f
|
26
27
|
# => 1.0
|
27
28
|
```
|
28
29
|
|
@@ -39,7 +40,7 @@ Item | Price
|
|
39
40
|
So the most likely aproach is to simply round each item by itself, so the customer isn't bothered with 34/10000 €-Cents. Simple at it is its not quite what you want:
|
40
41
|
|
41
42
|
```ruby
|
42
|
-
Array.new(3){BigDecimal
|
43
|
+
Array.new(3){ BigDecimal('0.3334') }.map{|i| i.round(2)}.reduce(:+).to_f
|
43
44
|
# => 0.99
|
44
45
|
```
|
45
46
|
|
@@ -54,7 +55,7 @@ Now you have the customer bothering about why there is a difference between the
|
|
54
55
|
|
55
56
|
This gem helps to distribute the rounding error amongst the items to preserve the total:
|
56
57
|
```ruby
|
57
|
-
a = Array.new(3){BigDecimal
|
58
|
+
a = Array.new(3){ BigDecimal('0.3334') }
|
58
59
|
# => [#<BigDecimal:887b6c8,'0.3334E0',9(18)>, #<BigDecimal:887b600,'0.3334E0',9(18)>, #<BigDecimal:887b4c0,'0.3334E0',9(18)>]
|
59
60
|
a = LareRound.round(a,2)
|
60
61
|
# => [#<BigDecimal:8867330,'0.34E0',9(36)>, #<BigDecimal:8867290,'0.33E0',9(36)>, #<BigDecimal:88671f0,'0.33E0',9(36)>]
|
@@ -75,7 +76,7 @@ Item | Price
|
|
75
76
|
LareRound supports *Array* and *Hash* as collection types. As usage of array was shown above here comes the hash example:
|
76
77
|
|
77
78
|
```ruby
|
78
|
-
hash = Hash[(1..3).map.with_index{|x,i|[x,BigDecimal
|
79
|
+
hash = Hash[(1..3).map.with_index{ |x,i| [x,BigDecimal('0.3334')] }]
|
79
80
|
# => {1=>#<BigDecimal:26ac7d0,'0.3334E0',9(18)>, 2=>#<BigDecimal:26ac730,'0.3334E0',9(18)>, 3=>#<BigDecimal:26ac690,'0.3334E0',9(18)>}
|
80
81
|
LareRound.round(hash,2)
|
81
82
|
# => {1=>#<BigDecimal:26b9318,'0.34E0',9(36)>, 2=>#<BigDecimal:26b9250,'0.33E0',9(36)>, 3=>#<BigDecimal:26b91b0,'0.33E0',9(36)>}
|
@@ -93,17 +94,34 @@ Item (unrounded)| Price (unrounded) | LareRound | Financial
|
|
93
94
|
tax ( 8.23%) | 0.823 | 0.82 | 0.83
|
94
95
|
**Total** | **10.823** | **10.82** | **10.83**
|
95
96
|
|
96
|
-
* refactoring ^^
|
97
|
-
|
98
97
|
## Installation
|
99
98
|
|
100
99
|
Add this line to your application's Gemfile:
|
101
100
|
|
102
|
-
|
103
|
-
|
101
|
+
```bash
|
102
|
+
gem 'lare_round', '~> 1.0.0'
|
103
|
+
```
|
104
104
|
And then execute:
|
105
105
|
|
106
|
-
|
106
|
+
```bash
|
107
|
+
bundle install
|
108
|
+
```
|
109
|
+
|
110
|
+
## Test
|
111
|
+
|
112
|
+
The tests can be run after bundling with:
|
113
|
+
|
114
|
+
```bash
|
115
|
+
rake test
|
116
|
+
```
|
117
|
+
|
118
|
+
## Console
|
119
|
+
|
120
|
+
To start the interactive ruby shell (irb) you can run:
|
121
|
+
|
122
|
+
```bash
|
123
|
+
bin/console
|
124
|
+
```
|
107
125
|
|
108
126
|
## Contributing
|
109
127
|
|
@@ -114,6 +132,7 @@ And then execute:
|
|
114
132
|
5. Create new Pull Request
|
115
133
|
|
116
134
|
## License
|
135
|
+
|
117
136
|
Hereby released under MIT license.
|
118
137
|
|
119
138
|
## Authors/Contributors
|
data/Rakefile
CHANGED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'lare_round'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/lare_round.gemspec
CHANGED
@@ -1,13 +1,17 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'English'
|
4
|
+
lib = File.expand_path('lib', __dir__)
|
3
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
6
|
require 'lare_round/version'
|
5
7
|
|
6
8
|
Gem::Specification.new do |spec|
|
9
|
+
spec.required_ruby_version = '>= 3.0.4'
|
10
|
+
|
7
11
|
spec.name = 'lare_round'
|
8
12
|
spec.version = LareRound::VERSION
|
9
13
|
spec.authors = ['Carsten Wirth']
|
10
|
-
spec.email = ['
|
14
|
+
spec.email = ['cwirth79@web.de']
|
11
15
|
|
12
16
|
spec.description = <<-DESCRIPTION
|
13
17
|
A collection of BigDecimal items e.g. invoice items can be rounded for
|
@@ -24,14 +28,9 @@ Gem::Specification.new do |spec|
|
|
24
28
|
spec.homepage = ''
|
25
29
|
spec.license = 'MIT'
|
26
30
|
|
27
|
-
spec.
|
28
|
-
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
|
29
|
-
spec.test_files = spec.files.grep(/^(test|spec|features)\//)
|
30
|
-
spec.require_paths = ['lib']
|
31
|
+
spec.metadata = { 'rubygems_mfa_required' => 'true' }
|
31
32
|
|
32
|
-
spec.
|
33
|
-
spec.
|
34
|
-
spec.
|
35
|
-
spec.add_development_dependency 'minitest'
|
36
|
-
spec.add_development_dependency 'simplecov'
|
33
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
34
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
35
|
+
spec.require_paths = ['lib']
|
37
36
|
end
|
data/lib/lare_round/version.rb
CHANGED
data/lib/lare_round.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bigdecimal'
|
2
4
|
|
5
|
+
# module providing the entry point to the rounding logic
|
3
6
|
module LareRound
|
4
7
|
def self.round(values, precision)
|
5
8
|
# although it is the senders responsibility to ensure that correct messages
|
@@ -15,111 +18,106 @@ module LareRound
|
|
15
18
|
# StandardError for dealing with application level errors
|
16
19
|
class LareRoundError < StandardError; end
|
17
20
|
|
18
|
-
|
21
|
+
class << self
|
22
|
+
private
|
19
23
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
24
|
+
def process(values, precision)
|
25
|
+
if values.is_a? Hash
|
26
|
+
process_hash(values, precision)
|
27
|
+
else
|
28
|
+
round_array_of_values(values, precision)
|
29
|
+
end
|
25
30
|
end
|
26
|
-
end
|
27
31
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
32
|
+
def process_hash(values, precision)
|
33
|
+
rounded_values = round_array_of_values(values.values, precision)
|
34
|
+
values.tap do |hash|
|
35
|
+
hash.keys.each_with_index do |key, index|
|
36
|
+
hash[key] = rounded_values[index]
|
37
|
+
end
|
33
38
|
end
|
34
39
|
end
|
35
|
-
end
|
36
40
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
41
|
+
def handle_value_errors(values)
|
42
|
+
raise LareRoundError, 'values must not be nil' if values.nil?
|
43
|
+
raise LareRoundError, 'values must not be empty' if values.empty?
|
44
|
+
raise LareRoundError, 'values must be an array' unless values.is_a? Array
|
45
|
+
|
46
|
+
numbers_invalid = values.map { |i| i.is_a? Numeric }
|
47
|
+
.reject { |i| i == true }.size
|
48
|
+
if numbers_invalid.positive?
|
49
|
+
error = <<-ERROR.strip.gsub(/\s+/, ' ')
|
50
|
+
values contains not numeric values (#{numbers_invalid})
|
51
|
+
ERROR
|
52
|
+
raise LareRoundError, error
|
53
|
+
end
|
54
|
+
|
55
|
+
return if values.map { |i| i.is_a? BigDecimal }.reject { |i| i == true }.empty?
|
50
56
|
|
51
|
-
|
52
|
-
warning = <<-WARNING.strip.gsub(/\s+/, ' ')
|
57
|
+
warn <<-WARNING.strip.gsub(/\s+/, ' ')
|
53
58
|
values contains non decimal values,
|
54
59
|
you might loose precision or even get wrong rounding results
|
55
60
|
WARNING
|
56
|
-
warn warning
|
57
61
|
end
|
58
|
-
end
|
59
62
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
fail LareRoundError, 'precision must be a number'
|
63
|
+
def handle_precision_errors(precision)
|
64
|
+
raise LareRoundError, 'precision must not be nil' if precision.nil?
|
65
|
+
raise LareRoundError, 'precision must be a number' unless precision.is_a?(Numeric)
|
66
|
+
raise LareRoundError, 'precision must be greater or equal to 0' if precision.negative?
|
65
67
|
end
|
66
68
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
Struct.new(
|
70
|
+
'IntermediaryResults',
|
71
|
+
:decimal_shift,
|
72
|
+
:rounded_total,
|
73
|
+
:array_of_values,
|
74
|
+
:unrounded_values,
|
75
|
+
:precision,
|
76
|
+
:rounded_values
|
77
|
+
)
|
78
|
+
|
79
|
+
def round_array_of_values(array_of_values, precision)
|
80
|
+
mrc = Struct::IntermediaryResults.new
|
81
|
+
mrc.precision = precision
|
82
|
+
mrc.decimal_shift = BigDecimal(10**precision.to_i)
|
83
|
+
mrc.rounded_total = array_of_values.reduce(:+)
|
84
|
+
.round(precision) * mrc.decimal_shift
|
85
|
+
mrc.array_of_values = array_of_values.map do |v|
|
86
|
+
v.is_a?(BigDecimal) ? v : BigDecimal(v.to_s)
|
87
|
+
end
|
88
|
+
mrc.unrounded_values = array_of_values.map { |v| v * mrc.decimal_shift }
|
89
|
+
|
90
|
+
largest_remainder_method(mrc)
|
71
91
|
|
72
|
-
|
73
|
-
'IntermediaryResults',
|
74
|
-
:decimal_shift,
|
75
|
-
:rounded_total,
|
76
|
-
:array_of_values,
|
77
|
-
:unrounded_values,
|
78
|
-
:precision,
|
79
|
-
:rounded_values
|
80
|
-
)
|
81
|
-
|
82
|
-
def self.round_array_of_values(array_of_values, precision)
|
83
|
-
mrc = Struct::IntermediaryResults.new
|
84
|
-
mrc.precision = precision
|
85
|
-
mrc.decimal_shift = BigDecimal.new(10**precision.to_i)
|
86
|
-
mrc.rounded_total = array_of_values.reduce(:+)
|
87
|
-
.round(precision) * mrc.decimal_shift
|
88
|
-
mrc.array_of_values = array_of_values.map do |v|
|
89
|
-
((v.is_a? BigDecimal) ? v : BigDecimal.new(v.to_s))
|
92
|
+
mrc.rounded_values
|
90
93
|
end
|
91
|
-
mrc.unrounded_values = array_of_values.map { |v| v * mrc.decimal_shift }
|
92
94
|
|
93
|
-
largest_remainder_method(mrc)
|
95
|
+
def largest_remainder_method(mrc)
|
96
|
+
mrc.rounded_values = mrc.array_of_values.map do |v|
|
97
|
+
largest_remainder_round(v, mrc)
|
98
|
+
end
|
94
99
|
|
95
|
-
|
96
|
-
|
100
|
+
until mrc.rounded_values.reduce(:+) >= mrc.rounded_total
|
101
|
+
fractions = mrc.unrounded_values.zip(mrc.rounded_values).map do |x, y|
|
102
|
+
x - y
|
103
|
+
end
|
104
|
+
mrc.rounded_values[fractions.index(fractions.max)] += 1
|
105
|
+
end
|
97
106
|
|
98
|
-
|
99
|
-
mrc.rounded_values = mrc.array_of_values.map do |v|
|
100
|
-
largest_remainder_round(v, mrc)
|
107
|
+
mrc.rounded_values.map! { |v| v / mrc.decimal_shift }
|
101
108
|
end
|
102
109
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
110
|
+
def largest_remainder_round(value, mrc)
|
111
|
+
# items needed to be rounded down if positiv:
|
112
|
+
# 0.7 + 0.7 + 0.7 = ( 2.1 ).round(0) = 2
|
113
|
+
# (0.7).round(0) + (0.7).round(0) + (0.7).round(0) = 1 + 1 + 1 = 3
|
114
|
+
# elsewise if negative
|
115
|
+
rounding_strategy = if value.negative?
|
116
|
+
BigDecimal::ROUND_UP
|
117
|
+
else
|
118
|
+
BigDecimal::ROUND_DOWN
|
119
|
+
end
|
120
|
+
value.round(mrc.precision, rounding_strategy) * mrc.decimal_shift
|
108
121
|
end
|
109
|
-
|
110
|
-
mrc.rounded_values.map! { |v| v / mrc.decimal_shift }
|
111
|
-
end
|
112
|
-
|
113
|
-
def self.largest_remainder_round(v, mrc)
|
114
|
-
# items needed to be rounded down if positiv:
|
115
|
-
# 0.7 + 0.7 + 0.7 = ( 2.1 ).round(0) = 2
|
116
|
-
# (0.7).round(0) + (0.7).round(0) + (0.7).round(0) = 1 + 1 + 1 = 3
|
117
|
-
# elsewise if negative
|
118
|
-
rounding_strategy = if v < 0
|
119
|
-
BigDecimal::ROUND_UP
|
120
|
-
else
|
121
|
-
BigDecimal::ROUND_DOWN
|
122
|
-
end
|
123
|
-
v.round(mrc.precision, rounding_strategy) * mrc.decimal_shift
|
124
122
|
end
|
125
123
|
end
|
data/test/lare_round_test.rb
CHANGED
@@ -1,27 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'test_helper'
|
2
4
|
require 'bigdecimal'
|
3
5
|
require 'securerandom'
|
4
6
|
|
5
|
-
class LareRoundTest <
|
7
|
+
class LareRoundTest < Minitest::Spec
|
6
8
|
def test_has_static_method_round
|
7
9
|
assert_equal(true, LareRound.respond_to?(:round))
|
8
10
|
end
|
9
11
|
|
10
12
|
def create_big_decimal(precision, digit)
|
11
|
-
BigDecimal
|
13
|
+
BigDecimal("0.#{'3' * precision}#{digit}")
|
12
14
|
end
|
13
15
|
|
14
16
|
(1..9).each do |digit|
|
15
17
|
(1..23).each do |items|
|
16
18
|
(0..10).each do |precision|
|
17
|
-
|
18
19
|
method_name = <<-TESTMETHOD.strip.gsub(/\s+/, '_')
|
19
20
|
test #{items} items with last digit of #{digit}
|
20
21
|
should sum up to rounded total of BigDecimal items
|
21
22
|
with precision of #{precision} if passed as array
|
22
23
|
TESTMETHOD
|
23
24
|
define_method method_name do
|
24
|
-
arr =
|
25
|
+
arr = Array.new(items) { create_big_decimal(precision, digit) }
|
25
26
|
rounded_total = arr.reduce(:+).round(precision)
|
26
27
|
assert_equal(
|
27
28
|
rounded_total,
|
@@ -35,11 +36,9 @@ class LareRoundTest < MiniTest::Spec
|
|
35
36
|
with precision of #{precision} if passed as hash
|
36
37
|
TESTMETHOD
|
37
38
|
define_method method_name do
|
38
|
-
hash =
|
39
|
-
(
|
40
|
-
|
41
|
-
end
|
42
|
-
]
|
39
|
+
hash = (1..items).to_h do |x|
|
40
|
+
[x, create_big_decimal(precision, digit)]
|
41
|
+
end
|
43
42
|
rounded_total = hash.values.reduce(:+).round(precision)
|
44
43
|
assert_equal(
|
45
44
|
rounded_total,
|
@@ -53,14 +52,12 @@ class LareRoundTest < MiniTest::Spec
|
|
53
52
|
if passed as hash should not change order
|
54
53
|
TESTMETHOD
|
55
54
|
define_method method_name do
|
56
|
-
hash =
|
57
|
-
(
|
58
|
-
|
59
|
-
end
|
60
|
-
]
|
55
|
+
hash = (1..items).map.with_index do |x, i|
|
56
|
+
[x, create_big_decimal(precision, digit) + BigDecimal(i)]
|
57
|
+
end.to_h
|
61
58
|
rounded_hash = LareRound.round(hash.clone, precision)
|
62
|
-
hash.
|
63
|
-
assert((((hash[key] - rounded_hash[key]) * 10**precision).abs < 1))
|
59
|
+
hash.each_key do |key|
|
60
|
+
assert((((hash[key] - rounded_hash[key]) * (10**precision)).abs < 1))
|
64
61
|
end
|
65
62
|
end
|
66
63
|
|
@@ -71,7 +68,7 @@ class LareRoundTest < MiniTest::Spec
|
|
71
68
|
TESTMETHOD
|
72
69
|
define_method method_name do
|
73
70
|
arr = Array.new(items) do
|
74
|
-
BigDecimal
|
71
|
+
BigDecimal(-1 * create_big_decimal(precision, digit))
|
75
72
|
end
|
76
73
|
rounded_total = arr.reduce(:+).round(precision)
|
77
74
|
assert_equal(
|
@@ -98,7 +95,7 @@ class LareRoundTest < MiniTest::Spec
|
|
98
95
|
end
|
99
96
|
end
|
100
97
|
|
101
|
-
let(:default_array) { Array.new(3) { BigDecimal
|
98
|
+
let(:default_array) { Array.new(3) { BigDecimal('0.1234') } }
|
102
99
|
|
103
100
|
def test_should_raise_if_precision_is_nil
|
104
101
|
exception = assert_raises(LareRound::LareRoundError) do
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,85 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lare_round
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carsten Wirth
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.3'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.3'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rubocop
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: minitest
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: simplecov
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
10
|
+
date: 2025-01-31 00:00:00.000000000 Z
|
11
|
+
dependencies: []
|
83
12
|
description: |2
|
84
13
|
A collection of BigDecimal items e.g. invoice items can be rounded for
|
85
14
|
displaying them in views. Rounding may apply a rounding error to the
|
@@ -90,17 +19,22 @@ description: |2
|
|
90
19
|
Application of the largest remainder method can help to preserve the
|
91
20
|
total sum for fractionated parts thus eliminating this confusion.
|
92
21
|
email:
|
93
|
-
-
|
94
|
-
executables:
|
22
|
+
- cwirth79@web.de
|
23
|
+
executables:
|
24
|
+
- console
|
95
25
|
extensions: []
|
96
26
|
extra_rdoc_files: []
|
97
27
|
files:
|
28
|
+
- ".github/workflows/ci.yml"
|
98
29
|
- ".gitignore"
|
99
|
-
- ".
|
30
|
+
- ".rubocop.yml"
|
31
|
+
- ".rubocop_todo.yml"
|
32
|
+
- ".ruby-version"
|
100
33
|
- Gemfile
|
101
34
|
- LICENSE.txt
|
102
35
|
- README.md
|
103
36
|
- Rakefile
|
37
|
+
- bin/console
|
104
38
|
- lare_round.gemspec
|
105
39
|
- lib/lare_round.rb
|
106
40
|
- lib/lare_round/version.rb
|
@@ -109,8 +43,8 @@ files:
|
|
109
43
|
homepage: ''
|
110
44
|
licenses:
|
111
45
|
- MIT
|
112
|
-
metadata:
|
113
|
-
|
46
|
+
metadata:
|
47
|
+
rubygems_mfa_required: 'true'
|
114
48
|
rdoc_options: []
|
115
49
|
require_paths:
|
116
50
|
- lib
|
@@ -118,18 +52,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
118
52
|
requirements:
|
119
53
|
- - ">="
|
120
54
|
- !ruby/object:Gem::Version
|
121
|
-
version:
|
55
|
+
version: 3.0.4
|
122
56
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
57
|
requirements:
|
124
58
|
- - ">="
|
125
59
|
- !ruby/object:Gem::Version
|
126
60
|
version: '0'
|
127
61
|
requirements: []
|
128
|
-
|
129
|
-
rubygems_version: 2.2.2
|
130
|
-
signing_key:
|
62
|
+
rubygems_version: 3.6.3
|
131
63
|
specification_version: 4
|
132
64
|
summary: gem for rounding BigDecimal items by preserving its sum
|
133
|
-
test_files:
|
134
|
-
- test/lare_round_test.rb
|
135
|
-
- test/test_helper.rb
|
65
|
+
test_files: []
|