money-collection 0.0.1
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 +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +56 -0
- data/Rakefile +9 -0
- data/benchmark/helper.rb +17 -0
- data/benchmark/max.rb +47 -0
- data/benchmark/sum.rb +47 -0
- data/lib/money/collection.rb +125 -0
- data/lib/money/collection/version.rb +5 -0
- data/money-collection.gemspec +25 -0
- data/spec/helper.rb +21 -0
- data/spec/money/collection_spec.rb +211 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 31d48e73a143672af436d2128462573a8f9b7d70
|
4
|
+
data.tar.gz: 2c8528a4f6163392762d25ddea387eb6649d860a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9027fda3114e23db23061ab4a88d31e071a89d690a11c4de994c0c8b6517d6a41f5e28aa1620dff27285be12aac433612e95871ff3609d6bc457aeb82d358603
|
7
|
+
data.tar.gz: 85632bb6ae43b0eac67779762a35909c1d00894ba67c51cb727413f78f977538d3349d1af189c1094e5f2822c55390dfefe098bf2f0dd3b34eedf5bdb3bb3313
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 lulalala
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# Money::Collection
|
2
|
+
|
3
|
+
Calculating sum/min/max on a bunch of Money objects, with __accuracy__ and __efficiency__.
|
4
|
+
|
5
|
+
This is achieved by minimizing number of currency conversions and object creations.
|
6
|
+
|
7
|
+
For large collections, sum/min/max can perform up to 4 times as fast. For small collections (n<10), it can perform at least 2 times as fast.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'money-collection'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install money-collection
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
Here is the demo of the Money::Collection library:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
require 'money'
|
31
|
+
require 'money/collection'
|
32
|
+
|
33
|
+
c = Money::Collection.new( [money1, money2] )
|
34
|
+
c << money3
|
35
|
+
c.concat [money4, money5]
|
36
|
+
|
37
|
+
c.sum
|
38
|
+
c.sum('TWD') # returns sum in TWD currency.
|
39
|
+
c.max
|
40
|
+
c.min
|
41
|
+
```
|
42
|
+
|
43
|
+
For more, please check the API documentation.
|
44
|
+
|
45
|
+
## Contributing
|
46
|
+
|
47
|
+
The followings areas can use some of your expertise:
|
48
|
+
|
49
|
+
* Benchmarking, including comparison between commits.
|
50
|
+
* Accuracy improvement
|
51
|
+
* Optimization
|
52
|
+
|
53
|
+
## License
|
54
|
+
|
55
|
+
MIT License (See LICENSE.txt)
|
56
|
+
|
data/Rakefile
ADDED
data/benchmark/helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'bundler'
|
5
|
+
rescue LoadError => e
|
6
|
+
STDERR.puts e.message
|
7
|
+
STDERR.puts "Run `gem install bundler` to install Bundler."
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
|
11
|
+
begin
|
12
|
+
Bundler.setup(:default, :development, :test)
|
13
|
+
rescue Bundler::BundlerError => e
|
14
|
+
STDERR.puts e.message
|
15
|
+
STDERR.puts "Run `bundle install` to install missing gems."
|
16
|
+
exit e.status_code
|
17
|
+
end
|
data/benchmark/max.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
require 'benchmark'
|
3
|
+
require 'money'
|
4
|
+
require 'money/collection'
|
5
|
+
|
6
|
+
Money.add_rate(:usd, :twd, 30.4071518)
|
7
|
+
Money.add_rate(:twd, :usd, 0.032887)
|
8
|
+
|
9
|
+
def generate_samples(size, possible_currencies = [:usd])
|
10
|
+
size.times.map do
|
11
|
+
Money.new(Random.rand(100000), possible_currencies.sample)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
benchmarking_sizes = [1,2,5,10,50,100,200,400,600,800,1000]
|
16
|
+
|
17
|
+
puts 'Single currency'
|
18
|
+
|
19
|
+
samples = generate_samples(benchmarking_sizes.max)
|
20
|
+
# rehearsal
|
21
|
+
100.times{ Money::Collection.new(samples).sum }
|
22
|
+
Benchmark.bm do |x|
|
23
|
+
benchmarking_sizes.each do |size|
|
24
|
+
x.report "sum #{size} items" do
|
25
|
+
sliced_samples = samples.first(size)
|
26
|
+
10000.times do
|
27
|
+
Money::Collection.new(sliced_samples).max
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
puts 'Multiple currencies'
|
34
|
+
|
35
|
+
samples = generate_samples(benchmarking_sizes.max, [:usd, :twd])
|
36
|
+
# rehearsal
|
37
|
+
100.times{ Money::Collection.new(samples).sum }
|
38
|
+
Benchmark.bm do |x|
|
39
|
+
benchmarking_sizes.each do |size|
|
40
|
+
x.report "sum #{size} items" do
|
41
|
+
sliced_samples = samples.first(size)
|
42
|
+
10000.times do
|
43
|
+
Money::Collection.new(sliced_samples).max
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/benchmark/sum.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
require 'benchmark'
|
3
|
+
require 'money'
|
4
|
+
require 'money/collection'
|
5
|
+
|
6
|
+
Money.add_rate(:usd, :twd, 30.4071518)
|
7
|
+
Money.add_rate(:twd, :usd, 0.032887)
|
8
|
+
|
9
|
+
def generate_samples(size, possible_currencies = [:usd])
|
10
|
+
size.times.map do
|
11
|
+
Money.new(Random.rand(100000), possible_currencies.sample)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
benchmarking_sizes = [1,2,5,10,50,100,200,400,600,800,1000]
|
16
|
+
|
17
|
+
puts 'Single currency'
|
18
|
+
|
19
|
+
samples = generate_samples(benchmarking_sizes.max)
|
20
|
+
# rehearsal
|
21
|
+
100.times{ Money::Collection.new(samples).sum }
|
22
|
+
Benchmark.bm do |x|
|
23
|
+
benchmarking_sizes.each do |size|
|
24
|
+
x.report "sum #{size} items" do
|
25
|
+
sliced_samples = samples.first(size)
|
26
|
+
10000.times do
|
27
|
+
Money::Collection.new(sliced_samples).sum
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
puts 'Multiple currencies'
|
34
|
+
|
35
|
+
samples = generate_samples(benchmarking_sizes.max, [:usd, :twd])
|
36
|
+
# rehearsal
|
37
|
+
100.times{ Money::Collection.new(samples).sum }
|
38
|
+
Benchmark.bm do |x|
|
39
|
+
benchmarking_sizes.each do |size|
|
40
|
+
x.report "sum #{size} items" do
|
41
|
+
sliced_samples = samples.first(size)
|
42
|
+
10000.times do
|
43
|
+
Money::Collection.new(sliced_samples).sum
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require "money/collection/version"
|
2
|
+
|
3
|
+
class Money
|
4
|
+
class Collection
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
# @parma array [#to_a] collection of Money objects
|
8
|
+
def initialize(array = nil)
|
9
|
+
@collection = array.to_a.dup
|
10
|
+
@group_by_currency = @collection.group_by(&:currency)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Sums up Money objects in collection.
|
14
|
+
# @param target_currency [Currency, String, Symbol] - currency of the returning money object.
|
15
|
+
# @return [Money] sum of Money collection.
|
16
|
+
def sum(target_currency = nil)
|
17
|
+
if @collection.empty?
|
18
|
+
return Money.new(0, target_currency)
|
19
|
+
end
|
20
|
+
|
21
|
+
if @group_by_currency.size == 1
|
22
|
+
sum = self.class.sum_single_currency(@collection)
|
23
|
+
else
|
24
|
+
sums_per_currency = @group_by_currency.values.map{|moneys|
|
25
|
+
self.class.sum_single_currency(moneys)
|
26
|
+
}
|
27
|
+
|
28
|
+
# If target_currency is specified, and is in collection,
|
29
|
+
# move it to the front so it has precedence over other currencies.
|
30
|
+
if target_currency
|
31
|
+
target_currency = self.class.convert_to_currency_object(target_currency)
|
32
|
+
if index = sums_per_currency.find_index{|money| money.currency == target_currency}
|
33
|
+
money = sums_per_currency.delete_at(index)
|
34
|
+
sums_per_currency.unshift money
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
sum = self.class.sum_basic(sums_per_currency)
|
39
|
+
end
|
40
|
+
|
41
|
+
if target_currency.nil?
|
42
|
+
return sum
|
43
|
+
else
|
44
|
+
return sum.exchange_to(target_currency)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def max
|
49
|
+
@group_by_currency.values.map{|moneys|
|
50
|
+
moneys.max_by{|money| money.fractional}
|
51
|
+
}.max
|
52
|
+
end
|
53
|
+
|
54
|
+
def min
|
55
|
+
@group_by_currency.values.map{|moneys|
|
56
|
+
moneys.min_by{|money| money.fractional}
|
57
|
+
}.min
|
58
|
+
end
|
59
|
+
|
60
|
+
#### delegations
|
61
|
+
|
62
|
+
def each
|
63
|
+
if block_given?
|
64
|
+
@collection.each{|x| yield(x)}
|
65
|
+
return self
|
66
|
+
else
|
67
|
+
return @collection.each
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def <<(obj)
|
72
|
+
@collection << obj
|
73
|
+
if @group_by_currency[obj.currency].nil?
|
74
|
+
@group_by_currency[obj.currency] = [obj]
|
75
|
+
else
|
76
|
+
@group_by_currency[obj.currency] << obj
|
77
|
+
end
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def concat(other_ary)
|
82
|
+
@collection.concat(other_ary)
|
83
|
+
|
84
|
+
other_ary.group_by(&:currency).each do |currency, ary|
|
85
|
+
if @group_by_currency[currency].nil?
|
86
|
+
@group_by_currency[currency] = ary
|
87
|
+
else
|
88
|
+
@group_by_currency[currency].concat(ary)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
def size
|
95
|
+
@collection.size
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# Sums up Money objects using built-in :+ method.
|
101
|
+
# @param moneys [Enumerable<Money>] list of Moneys.
|
102
|
+
# @return [Money] sum of Money collection.
|
103
|
+
def self.sum_basic(moneys)
|
104
|
+
moneys.reduce{|total, money| total + money }
|
105
|
+
end
|
106
|
+
|
107
|
+
# Sums up Money objects of the same currency.
|
108
|
+
# Number of object creation is minimized.
|
109
|
+
# @param moneys [Enumerable<Money>] list of Moneys. There is no validation so caller must ensure all Moneys belong to the same currency.
|
110
|
+
# @return [Money] sum of Money collection.
|
111
|
+
def self.sum_single_currency(moneys)
|
112
|
+
total_fractional = moneys.reduce(0){|fractional, money| fractional += money.fractional }
|
113
|
+
Money.new(total_fractional, moneys[0].currency)
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.convert_to_currency_object(currency)
|
117
|
+
case currency
|
118
|
+
when String, Symbol
|
119
|
+
::Money::Currency.new(currency)
|
120
|
+
when ::Money::Currency
|
121
|
+
currency
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'money/collection/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "money-collection"
|
8
|
+
spec.version = Money::Collection::VERSION
|
9
|
+
spec.authors = ["lulalala"]
|
10
|
+
spec.email = ["mark@goodlife.tw"]
|
11
|
+
spec.summary = %q{Optimized operation on collection of Money objects}
|
12
|
+
spec.description = spec.summary
|
13
|
+
spec.homepage = "https://github.com/lulalala/money-collection"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "money", "~> 6.3"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
end
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'bundler'
|
5
|
+
rescue LoadError => e
|
6
|
+
STDERR.puts e.message
|
7
|
+
STDERR.puts "Run `gem install bundler` to install Bundler."
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
|
11
|
+
begin
|
12
|
+
Bundler.setup(:default, :development, :test)
|
13
|
+
rescue Bundler::BundlerError => e
|
14
|
+
STDERR.puts e.message
|
15
|
+
STDERR.puts "Run `bundle install` to install missing gems."
|
16
|
+
exit e.status_code
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'minitest/spec'
|
20
|
+
require 'minitest/autorun'
|
21
|
+
|
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'money'
|
3
|
+
require 'money/collection'
|
4
|
+
|
5
|
+
describe Money::Collection do
|
6
|
+
before do
|
7
|
+
foo = {
|
8
|
+
:iso_code => "FOO",
|
9
|
+
:subunit_to_unit => 100,
|
10
|
+
}
|
11
|
+
Money::Currency.register(foo)
|
12
|
+
|
13
|
+
Money.add_rate("FOO", "USD", 0.5)
|
14
|
+
Money.add_rate("USD", "FOO", 2)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'has version' do
|
18
|
+
Money::Collection.const_get('VERSION').wont_be_empty
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#sum' do
|
22
|
+
|
23
|
+
it 'sums with no element' do
|
24
|
+
c = Money::Collection.new
|
25
|
+
c.sum.must_equal Money.new(0)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'sums with single element' do
|
29
|
+
c = Money::Collection.new
|
30
|
+
c << Money.new(10,:usd)
|
31
|
+
c.sum.must_equal Money.new(10,:usd)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'sums same currency' do
|
35
|
+
ary = [
|
36
|
+
Money.new(10,:usd),
|
37
|
+
Money.new(20,:usd),
|
38
|
+
Money.new(30,:usd),
|
39
|
+
]
|
40
|
+
|
41
|
+
c = Money::Collection.new(ary)
|
42
|
+
|
43
|
+
c.sum.must_equal Money.new(60,:usd)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'sums different currencies' do
|
47
|
+
ary = [
|
48
|
+
Money.new(100,:usd),
|
49
|
+
Money.new(10,:foo),
|
50
|
+
]
|
51
|
+
|
52
|
+
c = Money::Collection.new(ary)
|
53
|
+
|
54
|
+
c.sum.must_equal normal_sum(ary)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'sums correctly, avoiding rounding down error' do
|
58
|
+
ary = [
|
59
|
+
Money.new(10,:usd),
|
60
|
+
Money.new(1,:foo),
|
61
|
+
Money.new(1,:foo),
|
62
|
+
]
|
63
|
+
|
64
|
+
c = Money::Collection.new(ary)
|
65
|
+
|
66
|
+
c.sum.must_equal Money.new(11,:usd)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'sums correctly, avoiding rounding twice if possible' do
|
70
|
+
ary = [
|
71
|
+
Money.new(10,:usd),
|
72
|
+
Money.new(1,:foo),
|
73
|
+
]
|
74
|
+
|
75
|
+
c = Money::Collection.new(ary)
|
76
|
+
|
77
|
+
c.sum(:foo).must_equal Money.new(21,:foo)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'returns sum in the specified currency' do
|
81
|
+
ary = [
|
82
|
+
Money.new(10,:usd),
|
83
|
+
Money.new(2,:foo),
|
84
|
+
]
|
85
|
+
|
86
|
+
c = Money::Collection.new(ary)
|
87
|
+
|
88
|
+
c.sum('foo').must_equal Money.new(22,:foo)
|
89
|
+
c.sum('usd').must_equal Money.new(11,:usd)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'returns sum large number of Money' do
|
93
|
+
10.times do
|
94
|
+
# force first bunch to be FOO, the a bunch of USD,
|
95
|
+
# so there won't be currency conversion error even for native sum method
|
96
|
+
ary = 1000.times.map do
|
97
|
+
Money.new(Random.rand(100000), :foo)
|
98
|
+
end
|
99
|
+
ary.concat(
|
100
|
+
1000.times.map do
|
101
|
+
Money.new(Random.rand(100000), :usd)
|
102
|
+
end
|
103
|
+
)
|
104
|
+
|
105
|
+
c = Money::Collection.new(ary)
|
106
|
+
c.sum('foo').must_equal normal_sum(ary)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '#min' do
|
112
|
+
it 'returns accurate smallest Money object' do
|
113
|
+
ary = [
|
114
|
+
Money.new(30,:foo),
|
115
|
+
Money.new(1,:usd),
|
116
|
+
Money.new(3,:foo),
|
117
|
+
]
|
118
|
+
|
119
|
+
c = Money::Collection.new(ary)
|
120
|
+
c.min.must_equal Money.new(1,:usd)
|
121
|
+
end
|
122
|
+
it 'returns smallest Money objects (from many items)' do
|
123
|
+
ary = 1000.times.map do
|
124
|
+
Money.new(Random.rand(100000) + 10, :foo)
|
125
|
+
end
|
126
|
+
c = Money::Collection.new(ary)
|
127
|
+
min = Money.new(1, :usd)
|
128
|
+
c << min
|
129
|
+
c.min.must_equal min
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '#max' do
|
134
|
+
it 'returns accurate biggest Money object' do
|
135
|
+
ary = [
|
136
|
+
Money.new(30,:foo),
|
137
|
+
Money.new(1,:usd),
|
138
|
+
Money.new(3,:foo),
|
139
|
+
]
|
140
|
+
|
141
|
+
c = Money::Collection.new(ary)
|
142
|
+
c.max.must_equal Money.new(30,:foo)
|
143
|
+
end
|
144
|
+
it 'returns biggest Money objects (from many items)' do
|
145
|
+
ary = 1000.times.map do
|
146
|
+
Money.new(Random.rand(100000), :foo)
|
147
|
+
end
|
148
|
+
c = Money::Collection.new(ary)
|
149
|
+
max = Money.new(100001, :usd)
|
150
|
+
c << max
|
151
|
+
c.max.must_equal max
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe '#concat' do
|
156
|
+
it 'concats Money objects to collection' do
|
157
|
+
ary = [
|
158
|
+
Money.new(10,:usd),
|
159
|
+
Money.new(2,:foo),
|
160
|
+
]
|
161
|
+
|
162
|
+
c = Money::Collection.new
|
163
|
+
c.concat ary
|
164
|
+
|
165
|
+
c.size.must_equal 2
|
166
|
+
c.sum('foo').must_equal Money.new(22,:foo)
|
167
|
+
end
|
168
|
+
it 'concats Money objects to collection multiple times' do
|
169
|
+
ary = [
|
170
|
+
Money.new(10,:usd),
|
171
|
+
Money.new(2,:foo),
|
172
|
+
]
|
173
|
+
|
174
|
+
c = Money::Collection.new
|
175
|
+
c.concat ary
|
176
|
+
c.concat ary
|
177
|
+
|
178
|
+
c.size.must_equal 4
|
179
|
+
c.sum('foo').must_equal Money.new(44,:foo)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe '#<<' do
|
184
|
+
it 'concats Money object to collection multiple times' do
|
185
|
+
c = Money::Collection.new
|
186
|
+
c << Money.new(10,:usd)
|
187
|
+
c << Money.new(10,:foo)
|
188
|
+
c << Money.new(10,:usd)
|
189
|
+
|
190
|
+
c.sum.must_equal Money.new(25,:usd)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe 'no side effect' do
|
195
|
+
it 'does not change original array that is passed in the initialize method' do
|
196
|
+
m1 = Money.new(10,:usd)
|
197
|
+
ary = [m1]
|
198
|
+
|
199
|
+
c = Money::Collection.new(ary)
|
200
|
+
c << Money.new(1,:usd)
|
201
|
+
|
202
|
+
ary.size.must_equal 1
|
203
|
+
ary[0].must_equal m1
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Adds up array of Money one by one and returns the sum.
|
209
|
+
def normal_sum(array)
|
210
|
+
array.reduce{|total, money| total + money }
|
211
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: money-collection
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- lulalala
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: money
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
description: Optimized operation on collection of Money objects
|
56
|
+
email:
|
57
|
+
- mark@goodlife.tw
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE.txt
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- benchmark/helper.rb
|
68
|
+
- benchmark/max.rb
|
69
|
+
- benchmark/sum.rb
|
70
|
+
- lib/money/collection.rb
|
71
|
+
- lib/money/collection/version.rb
|
72
|
+
- money-collection.gemspec
|
73
|
+
- spec/helper.rb
|
74
|
+
- spec/money/collection_spec.rb
|
75
|
+
homepage: https://github.com/lulalala/money-collection
|
76
|
+
licenses:
|
77
|
+
- MIT
|
78
|
+
metadata: {}
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
requirements: []
|
94
|
+
rubyforge_project:
|
95
|
+
rubygems_version: 2.2.2
|
96
|
+
signing_key:
|
97
|
+
specification_version: 4
|
98
|
+
summary: Optimized operation on collection of Money objects
|
99
|
+
test_files:
|
100
|
+
- spec/helper.rb
|
101
|
+
- spec/money/collection_spec.rb
|
102
|
+
has_rdoc:
|