money_with_date 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6a911f2070a7b2f664aa55b706b25c60cb2bca163edae6d486fe2453f4195c0b
4
+ data.tar.gz: f1622c8bba8fa245391d495172df0fad151670abc1067ae95408078dbc8c7bcf
5
+ SHA512:
6
+ metadata.gz: bf1e0503e40b67256656889b317f0199fafac3c8486b0764656afd618e063b7de66e91006e40a9e097e17b09e20f3021431d72410d31f25953106ad2f38d317f
7
+ data.tar.gz: f511459facf2b7670b6040e0d87a8cea0aa5d6fdf899d7f0e2e89720400ae540a2f7c720a6c023563d9eabc6b3533d69ba11a7807f179355aa4af16a2b17826d
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --force-color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,28 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+ NewCops: enable
4
+ Exclude:
5
+ - 'spec_money/**/*'
6
+ - 'spec_money_rails/**/*'
7
+ - 'vendor/**/*'
8
+
9
+ Style/StringLiterals:
10
+ Enabled: true
11
+ EnforcedStyle: double_quotes
12
+
13
+ Style/StringLiteralsInInterpolation:
14
+ Enabled: true
15
+ EnforcedStyle: double_quotes
16
+
17
+ Style/Documentation:
18
+ Enabled: false
19
+
20
+ Style/ConditionalAssignment:
21
+ Enabled: false
22
+
23
+ Metrics/BlockLength:
24
+ Exclude:
25
+ - 'spec/**/*'
26
+
27
+ Layout/LineLength:
28
+ Max: 120
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-02-18
4
+
5
+ - Initial release
@@ -0,0 +1,84 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
+
9
+ ## Our Standards
10
+
11
+ Examples of behavior that contributes to a positive environment for our community include:
12
+
13
+ * Demonstrating empathy and kindness toward other people
14
+ * Being respectful of differing opinions, viewpoints, and experiences
15
+ * Giving and gracefully accepting constructive feedback
16
+ * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17
+ * Focusing on what is best not just for us as individuals, but for the overall community
18
+
19
+ Examples of unacceptable behavior include:
20
+
21
+ * The use of sexualized language or imagery, and sexual attention or
22
+ advances of any kind
23
+ * Trolling, insulting or derogatory comments, and personal or political attacks
24
+ * Public or private harassment
25
+ * Publishing others' private information, such as a physical or email
26
+ address, without their explicit permission
27
+ * Other conduct which could reasonably be considered inappropriate in a
28
+ professional setting
29
+
30
+ ## Enforcement Responsibilities
31
+
32
+ Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
33
+
34
+ Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
35
+
36
+ ## Scope
37
+
38
+ This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
39
+
40
+ ## Enforcement
41
+
42
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at lovro.bikic@gmail.com. All complaints will be reviewed and investigated promptly and fairly.
43
+
44
+ All community leaders are obligated to respect the privacy and security of the reporter of any incident.
45
+
46
+ ## Enforcement Guidelines
47
+
48
+ Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
49
+
50
+ ### 1. Correction
51
+
52
+ **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
53
+
54
+ **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
55
+
56
+ ### 2. Warning
57
+
58
+ **Community Impact**: A violation through a single incident or series of actions.
59
+
60
+ **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
61
+
62
+ ### 3. Temporary Ban
63
+
64
+ **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
65
+
66
+ **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
67
+
68
+ ### 4. Permanent Ban
69
+
70
+ **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
71
+
72
+ **Consequence**: A permanent ban from any sort of public interaction within the community.
73
+
74
+ ## Attribution
75
+
76
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
77
+ available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
78
+
79
+ Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
80
+
81
+ [homepage]: https://www.contributor-covenant.org
82
+
83
+ For answers to common questions about this code of conduct, see the FAQ at
84
+ https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,73 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ money_with_date (0.1.0)
5
+ money (>= 6.14.0, <= 6.16.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.2)
11
+ coderay (1.1.3)
12
+ concurrent-ruby (1.1.9)
13
+ diff-lcs (1.5.0)
14
+ i18n (1.10.0)
15
+ concurrent-ruby (~> 1.0)
16
+ method_source (1.0.0)
17
+ money (6.16.0)
18
+ i18n (>= 0.6.4, <= 2)
19
+ parallel (1.21.0)
20
+ parser (3.1.0.0)
21
+ ast (~> 2.4.1)
22
+ pry (0.14.1)
23
+ coderay (~> 1.1)
24
+ method_source (~> 1.0)
25
+ rainbow (3.1.1)
26
+ rake (13.0.6)
27
+ regexp_parser (2.2.1)
28
+ rexml (3.2.5)
29
+ rspec (3.11.0)
30
+ rspec-core (~> 3.11.0)
31
+ rspec-expectations (~> 3.11.0)
32
+ rspec-mocks (~> 3.11.0)
33
+ rspec-core (3.11.0)
34
+ rspec-support (~> 3.11.0)
35
+ rspec-expectations (3.11.0)
36
+ diff-lcs (>= 1.2.0, < 2.0)
37
+ rspec-support (~> 3.11.0)
38
+ rspec-mocks (3.11.0)
39
+ diff-lcs (>= 1.2.0, < 2.0)
40
+ rspec-support (~> 3.11.0)
41
+ rspec-support (3.11.0)
42
+ rubocop (1.25.1)
43
+ parallel (~> 1.10)
44
+ parser (>= 3.1.0.0)
45
+ rainbow (>= 2.2.2, < 4.0)
46
+ regexp_parser (>= 1.8, < 3.0)
47
+ rexml
48
+ rubocop-ast (>= 1.15.1, < 2.0)
49
+ ruby-progressbar (~> 1.7)
50
+ unicode-display_width (>= 1.4.0, < 3.0)
51
+ rubocop-ast (1.15.2)
52
+ parser (>= 3.0.1.1)
53
+ rubocop-rake (0.6.0)
54
+ rubocop (~> 1.0)
55
+ rubocop-rspec (2.8.0)
56
+ rubocop (~> 1.19)
57
+ ruby-progressbar (1.11.0)
58
+ unicode-display_width (2.1.0)
59
+
60
+ PLATFORMS
61
+ ruby
62
+
63
+ DEPENDENCIES
64
+ money_with_date!
65
+ pry
66
+ rake
67
+ rspec
68
+ rubocop
69
+ rubocop-rake
70
+ rubocop-rspec
71
+
72
+ BUNDLED WITH
73
+ 2.3.7
data/Gemfile_test ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "money", ENV["MONEY_VERSION"]
8
+ gem "money-rails", ENV["MONEY_RAILS_VERSION"]
9
+ gem "activerecord", ENV["RAILS_VERSION"]
10
+ gem "sqlite3"
11
+ gem "rubyzip"
12
+ gem "simplecov", require: false
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Infinum
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,399 @@
1
+ # money_with_date
2
+
3
+ A Ruby library which extends the popular [money](https://github.com/RubyMoney/money) and [money-rails](https://github.com/RubyMoney/money-rails) gems with support for dated Money objects.
4
+
5
+ Dated Money objects are useful in situations where you have to exchange money between currencies based on historical exchange rates, and you'd like to keep date information on the Money object itself.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'money_with_date'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself:
20
+
21
+ $ gem install money_with_date
22
+
23
+ That's it! All your Money objects now have a date attribute.
24
+
25
+ ## Usage
26
+
27
+ ```ruby
28
+ Money.default_bank = MoneyWithDate::Bank::VariableExchange.new(MoneyWithDate::RatesStore::Memory.new)
29
+ # the gem provides subclasses of Money::Bank::VariableExchange and Money::RatesStore::Memory
30
+ # which can save historical exchange rates
31
+
32
+ Money.add_rate "USD", "EUR", 0.9, "2020-01-01"
33
+ Money.add_rate "CHF", "EUR", 0.96, Date.today
34
+
35
+ Money.new(100, "EUR", date: Date.today) + Money.new(100, "USD", date: "2020-01-01")
36
+ # => Money.new(190, "EUR", date: Date.today)
37
+ # the second Money object is exchanged to EUR with the exchange rate on 1/1/2020
38
+
39
+ Money.default_date = -> { Date.today }
40
+ # new Money objects use the default date if "date" parameter is omitted in Money.new
41
+
42
+ transactions = [
43
+ Money.from_amount(2, "CHF"),
44
+ Money.from_amount(1, "USD", date: "2020-01-01"),
45
+ Money.from_amount(2, "EUR")
46
+ ]
47
+
48
+ transactions.map { |money| money.exchange_to(:eur) }.sum
49
+ # => Money.from_amount(4,82, "EUR")
50
+ # 2 CHF == 1,92 EUR on today's date
51
+ # 1 USD = 0,9 EUR on 1/1/2020
52
+ # 1,92 + 0,9 + 2 = 4,82
53
+
54
+ # In Rails, a date column can be used to control the date of the Money object:
55
+ class Product < ActiveRecord::Base
56
+ monetize :amount_cents, with_model_date: :published_on
57
+ end
58
+
59
+ product = Product.new(amount_cents: 100, published_on: "2020-01-01")
60
+
61
+ product.amount.date
62
+ # => "2020-01-01"
63
+ ```
64
+
65
+ When you install and require the gem, all Money objects will automatically have a date attribute.
66
+
67
+ The default date for a Money object is read from the configuration option `Money.default_date` (more about that [here](#default_date)).
68
+
69
+ You can also override the default date when creating a Money object:
70
+ ```ruby
71
+ money = Money.new(100, :usd, date: "2020-01-01")
72
+
73
+ money.date # => "2020-01-01"
74
+ ```
75
+
76
+ The type of the `date` param should either be a Date object or something which can be coerced into Date (e.g. Time object, a string which can be parsed as date). The following examples are all valid ways of setting a date:
77
+ ```ruby
78
+ Money.new(100, :usd, date: Date.today)
79
+
80
+ Money.new(100, :usd, date: Time.now) # converted to Date with the `#to_date` method
81
+
82
+ Money.new(100, :usd, date: "2022-02-15") # converted to Date with `Date.parse`
83
+ ```
84
+
85
+ `date` argument which cannot be coerced into a Date will result in an `ArgumentError`.
86
+
87
+ It is also possible to copy Money objects and change their date with `Money#with_date`:
88
+ ```ruby
89
+ money = Money.from_cents(100, :eur, date: "2022-02-15")
90
+
91
+ new_money = money.with_date("2020-01-01")
92
+
93
+ new_money.cents # => 100
94
+ new_money.date # => "2020-01-01"
95
+ money.date # => "2022-02-15" (the original object is unchanged)
96
+ ```
97
+
98
+ In addition to `Money.new`, the `date` param can also be passed to the following methods:
99
+ ```ruby
100
+ Money.from_amount(10.00, :usd, date: Date.today)
101
+
102
+ Money.from_cents(1000, :usd, date: Date.today)
103
+ ```
104
+
105
+ ### Currency Exchange
106
+
107
+ This gem supports all existing bank implementations which do not support historical exchange rates. By default, when you require `money_with_date` in your project, existing currency exchange logic won't be affected.
108
+
109
+ To enable historical currency exchanges, the bank and rates store objects you use must support an additional `date` param in order to associate an exchange rate with a specific date.
110
+
111
+ This gem provides subclasses of money's default bank and rates store (variable exchange, in-memory store), which have been extended to support historical exchange rates:
112
+ ```ruby
113
+ Money.default_bank = MoneyWithDate::Bank::VariableExchange.new(MoneyWithDate::RatesStore::Memory.new)
114
+ ```
115
+
116
+ To save a historical exchange rate, use `Money.add_rate` and supply a date param:
117
+ ```ruby
118
+ Money.add_rate "EUR", "USD", 1.1, Date.today
119
+ ```
120
+
121
+ Currency exchange then works like this:
122
+ ```ruby
123
+ Money.add_rate "EUR", "USD", 1.05, Date.today - 1
124
+ Money.add_rate "EUR", "USD", 1.1, Date.today
125
+ Money.add_rate "EUR", "USD", 1.2, Date.today + 1
126
+
127
+ money = Money.new(100, "EUR", date: Date.today + 1)
128
+
129
+ money.exchange_to("USD").cents == 120 # => true
130
+ ```
131
+
132
+ To retrieve a historical exchange rate from a bank, invoke `Bank#get_rate` with a date param:
133
+ ```ruby
134
+ Money.default_bank.get_rate("EUR", "USD", Date.today - 1) # => 1.05
135
+ ```
136
+
137
+ The same rules that apply to the date param when initializing a Money object also apply here: it can be a Date object or anything that can be coerced into a Date object (Time, String).
138
+
139
+ ### Exchange rate stores
140
+
141
+ This gem provides an in-memory store for historical exchange rates: `MoneyWithDate::RatesStore::Memory`. This class is very similar to `Money::RatesStore::Memory`, only an additional `date` parameter has been added to methods where necessary.
142
+
143
+ You can also implement your own store, but it has to follow this interface:
144
+ ```ruby
145
+ # Add new exchange rate.
146
+ # @param [String] iso_from Currency ISO code. ex. 'USD'
147
+ # @param [String] iso_to Currency ISO code. ex. 'CAD'
148
+ # @param [Numeric] rate Exchange rate. ex. 0.0016
149
+ # @param [Date] date Exchange rate date. ex. Date.today
150
+ #
151
+ # @return [Numeric] rate.
152
+ def add_rate(iso_from, iso_to, rate, date); end
153
+
154
+ # Get rate. Must be idempotent. i.e. adding the same rate must not produce duplicates.
155
+ # @param [String] iso_from Currency ISO code. ex. 'USD'
156
+ # @param [String] iso_to Currency ISO code. ex. 'CAD'
157
+ # @param [Date] date Exchange rate date. ex. Date.today
158
+ #
159
+ # @return [Numeric] rate.
160
+ def get_rate(iso_from, iso_to, date); end
161
+
162
+ # Iterate over rate tuples (iso_from, iso_to, rate)
163
+ #
164
+ # @yieldparam iso_from [String] Currency ISO string.
165
+ # @yieldparam iso_to [String] Currency ISO string.
166
+ # @yieldparam rate [Numeric] Exchange rate.
167
+ # @yieldparam date [Date] Exchange rate date.
168
+ #
169
+ # @return [Enumerator]
170
+ #
171
+ # @example
172
+ # store.each_rate do |iso_from, iso_to, rate, date|
173
+ # puts [iso_from, iso_to, rate, date].join
174
+ # end
175
+ def each_rate(&block); end
176
+
177
+ # Wrap store operations in a thread-safe transaction
178
+ # (or IO or Database transaction, depending on your implementation)
179
+ #
180
+ # @yield [n] Block that will be wrapped in transaction.
181
+ #
182
+ # @example
183
+ # store.transaction do
184
+ # store.add_rate('USD', 'CAD', 0.9, Date.today)
185
+ # store.add_rate('USD', 'CLP', 0.0016, Date.today)
186
+ # end
187
+ def transaction(&block); end
188
+
189
+ # Serialize store and its content to make Marshal.dump work.
190
+ #
191
+ # Returns an array with store class and any arguments needed to initialize the store in the current state.
192
+
193
+ # @return [Array] [class, arg1, arg2]
194
+ def marshal_dump; end
195
+ ```
196
+
197
+ ### Usage With Rails
198
+
199
+ In case you're using `money-rails`, its `monetize` helper will also be extended to provide options for setting the date on monetized attributes. There are three ways you can set the date on a monetized attribute:
200
+
201
+ #### `with_model_date` option
202
+
203
+ Use this option when you want to use a table column to set the date on a monetized attribute. For example, if you have a table:
204
+ ```ruby
205
+ # Table name: products
206
+ #
207
+ # id :integer not null, primary key
208
+ # amount_cents :integer not null
209
+ # published_on :date
210
+ # created_at :timestamp not null
211
+ #
212
+ ```
213
+ and you want to use `published_on` column to set the date on the monetized `amount_cents`, do this:
214
+ ```ruby
215
+ class Product < ActiveRecord::Base
216
+ monetize :amount_cents, with_model_date: :published_on
217
+ end
218
+ ```
219
+ which will cause the following:
220
+ ```ruby
221
+ product = Product.new(amount_cents: 100, published_on: "2020-01-01")
222
+
223
+ product.amount.date # => "2020-01-01"
224
+ ```
225
+
226
+ If the value of the `with_model_date` column is `nil`, the date on the Money object will fall back to `Money.default_date`.
227
+
228
+ #### `with_date` option
229
+
230
+ This option can be used when you want to either hard-code a date for a monetized attribute, or you want to set the date dynamically. If you want to hard-code the date, pass a concrete value:
231
+ ```ruby
232
+ class Product < ActiveRecord::Base
233
+ monetize :amount_cents, with_date: "2020-01-01"
234
+ end
235
+ ```
236
+ All `Product#amount` Money objects will now have the same date: 1/1/2020.
237
+
238
+ You can also set the date on the Money object dynamically by using a callable:
239
+ ```ruby
240
+ class Product < ActiveRecord::Base
241
+ monetize :amount_cents, with_date: ->(product) { product.published_on || product.created_at }
242
+ end
243
+ ```
244
+ The callable should accept a single param: the ActiveRecord object.
245
+
246
+ In case the callable resolves to nil, the date on the Money object will fall back to `Money.default_date`.
247
+
248
+ #### `Money.default_date_column`
249
+
250
+ If you don't supply either of the above options to `monetize`, the gem will search for a default column to set the date on a monetized attribute. By default, this column is `created_at`. That means that if your class looks like this:
251
+ ```ruby
252
+ class Product < ActiveRecord::Base
253
+ monetize :amount_cents
254
+ end
255
+ ```
256
+ the following will happen:
257
+ ```ruby
258
+ product = Product.new(amount_cents: 100, created_at: "2020-01-01")
259
+
260
+ product.amount.date # => "2020-01-01"
261
+ ```
262
+
263
+ In case the default column doesn't exist, or its value is nil, the date on the Money object will fall back to `Money.default_date`.
264
+
265
+ You can also override the default column, or even disable it, which you can find out how to do [here](#default_date_column).
266
+
267
+ ## Configuration
268
+
269
+ The gem provides a couple of configuration options.
270
+
271
+ ### default_date
272
+
273
+ With the `money_with_date` gem installed, _all_ Money objects have a date. If you don't supply a date when creating a Money object, a default date will be assigned.
274
+
275
+ You can override the default date:
276
+ ```ruby
277
+ Money.default_date = -> { Date.today }
278
+ ```
279
+ and fetch it like this:
280
+ ```ruby
281
+ Money.default_date # outputs today's date
282
+ ```
283
+
284
+ The default date can be either a callable or a concrete value.
285
+ If you set a callable, `Money.default_date` will call the callable and return its value.
286
+
287
+ By default, `Money.default_date` is set to:
288
+ ```ruby
289
+ Money.default_date = -> { Date.current }
290
+ ```
291
+ if `Date.current` exists (which is the case in Rails projects). If it doesn't exist, the default date is set to:
292
+ ```ruby
293
+ Money.default_date = -> { Date.today }
294
+ ```
295
+
296
+ ### date_determines_equality
297
+
298
+ Based on your use case, you might or might not want the `date` attribute to affect whether two Money objects are equal. For example, in some scenarios it makes sense that:
299
+ ```ruby
300
+ Money.new(100, :usd, date: "2010-12-31") == Money.new(100, :usd, date: "2020-01-01")
301
+ ```
302
+ returns `true` (e.g. if you care only about money amounts), while in others it makes sense that it returns `false`.
303
+
304
+ By default, the date on the Money object **doesn't** affect the equality of the object to other Money objects (in the above scenario, the expression would return `true`).
305
+
306
+ However, if you need to, you can tell the gem to look at the date when comparing Money objects, so that the above expression would return `false`. You can achieve that by setting:
307
+ ```ruby
308
+ Money.date_determines_equality = true
309
+ ```
310
+
311
+ Setting this will affect the following Money methods: `#hash`, `#eql?` and `#<=>`.
312
+
313
+ ### default_date_column
314
+
315
+ If you use `money-rails` and `monetize`, but don't supply either `with_date` or `with_model_date` options, the gem will try to find a default column to set the date on the monetized attribute.
316
+
317
+ The default table column for setting the date on Money is `created_at`.
318
+
319
+ If you'd like to use a different default column, set it with:
320
+ ```ruby
321
+ # config/initializers/money.rb
322
+ Money.default_date_column = :my_date_column
323
+ ```
324
+
325
+ If the column value is nil, or the column doesn't exist on the table, the date will fall back to `Money.default_date`.
326
+
327
+ If you don't want the gem to use a default column for setting the date, set this:
328
+ ```ruby
329
+ Money.default_date_column = nil
330
+ ```
331
+
332
+ ## Supported Versions
333
+
334
+ `money_with_date` has the following version requirements:
335
+ - Ruby: **>= 2.6.0**
336
+ - money: **>= 6.14.0** and **<= 6.16.0**
337
+ - money-rails: **1.15.0**
338
+
339
+ The gem has been tested against all possible combinations of supported Ruby, Rails, money, and money-rails versions:
340
+ - Ruby: `2.6`, `2.7`, `3.0`, and `3.1`
341
+ - Rails: `5.2.6.2`, `6.0.4.6`, `6.1.4.6`, and `7.0.2.2`
342
+ - money: `6.14.0`, `6.14.1`, and `6.16.0`
343
+ - money-rails: `1.15.0`
344
+
345
+ Note: the gem hasn't been tested on Rails `5.2.6.2` with Rubies `3.0` and `3.1`, and on Rails `7.0.2.2` with Ruby `2.6` as those combinations of versions aren't compatible.
346
+
347
+ In addition to running its own test suite, the CI for this gem also runs [money's](https://github.com/RubyMoney/money/tree/main/spec) and [money-rails's](https://github.com/RubyMoney/money-rails/tree/main/spec) test suites with this gem loaded, to prevent regressions. This has been achieved by cloning their test suites from GitHub and requiring this gem in their spec files. For technical information, check the CI [workflow](.github/workflows/ci.yml).
348
+
349
+ ## Compatibility
350
+
351
+ This gem overrides money's and money-rails's public and _private_ APIs. As such, the gem can break with any new release of either of those gems if their API changes. To ensure breakages don't happen, the gem has been locked only to those versions of money and money-rails which have been fully tested for regressions.
352
+
353
+ The minimum supported versions are 6.14.0 for money and 1.15.0 for money-rails. Versions older than those cannot be supported as the API in older versions of both gems cannot be extended to provide the functionality which this gem provides.
354
+
355
+ ### Positional bank parameter
356
+
357
+ Up until version 6.14.0 of money, Money constructor accepted only three positional arguments (amount, currency, and bank):
358
+ ```ruby
359
+ Money.new(1000, :usd, Money.default_bank)
360
+ ```
361
+
362
+ From version 6.14.0, the constructor accepts two positional arguments and optional keyword arguments which can be used to override the default bank:
363
+ ```ruby
364
+ Money.new(1000, :usd, bank: Money.default_bank)
365
+ ```
366
+
367
+ For backwards-compatibility reasons, in version 6.14.0 and above, you can use either of those approaches to override the default bank.
368
+
369
+ However, the old approach isn't compatible with this gem because it doesn't allow us to provide a date argument without modifying the constructor.
370
+
371
+ So, if you want to use this gem, but are currently overriding the default bank with a positional argument, you'll have to refactor your code to use the new approach.
372
+
373
+ Note that, even if you don't refactor the code, the gem will still work, but all Money objects created that way will be assigned the default date.
374
+
375
+ ## Development
376
+
377
+ After checking out the repo, run `bin/setup` to install dependencies. Run `bin/console` for an interactive prompt that will allow you to experiment.
378
+
379
+ To run RuboCop, execute `bundle exec rake rubocop`.
380
+
381
+ To run unit tests, execute `bundle exec rake spec:unit`. Unit tests can be run with different versions of money, money-rails, and Rails, which you can specify as Rake task arguments. For example, if you want to run unit tests on money 6.14.0, money-rails 1.15.0, and Rails 6.1.4.6, execute: `bundle exec rake "spec:unit[6.14.0, 1.15.0, 6.1.4.6]"`.
382
+
383
+ To run money regression tests, execute `bundle exec rake spec:money`. You can also run them with a specific version of money: `bundle exec rake "spec:money[6.14.1]"`.
384
+
385
+ To run money-rails regression tests, execute `bundle exec rake spec:money_rails`. The task also accepts a money-rails version argument: `bundle exec rake "spec:money_rails[1.15.0]"`.
386
+
387
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
388
+
389
+ ## Contributing
390
+
391
+ Bug reports and pull requests are welcome on GitHub at https://github.com/infinum/money_with_date. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/infinum/money_with_date/blob/master/CODE_OF_CONDUCT.md).
392
+
393
+ ## License
394
+
395
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
396
+
397
+ ## Code of Conduct
398
+
399
+ Everyone interacting in the MoneyWithDate project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/infinum/money_with_date/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec:unit rubocop]
13
+
14
+ namespace :spec do # rubocop:disable Metrics/BlockLength
15
+ task :unit, [:money, :money_rails, :rails] do |_, args|
16
+ args.with_defaults(money: "6.16.0", money_rails: "1.15.0", rails: "7.0.2.2")
17
+
18
+ ENV["MONEY_VERSION"] = args.money
19
+ ENV["MONEY_RAILS_VERSION"] = args.money_rails
20
+ ENV["RAILS_VERSION"] = args.rails
21
+
22
+ sh 'echo "Running unit tests on money $MONEY_VERSION, money-rails $MONEY_RAILS_VERSION, rails $RAILS_VERSION"',
23
+ verbose: false
24
+ Rake::Task["prepare_env"].invoke
25
+ sh "bundle exec rspec", verbose: false
26
+ end
27
+
28
+ task :money, [:money] do |_, args|
29
+ args.with_defaults(money: "6.16.0")
30
+
31
+ ENV["MONEY_VERSION"] = args.money
32
+
33
+ sh 'echo "Running money regressions tests on money $MONEY_VERSION"', verbose: false
34
+ Rake::Task["prepare_env"].invoke
35
+ sh "bin/prepare_money_specs", verbose: false
36
+ sh "bundle exec rspec --default-path spec_money", verbose: false
37
+ end
38
+
39
+ task :money_rails, [:money_rails] do |_, args|
40
+ args.with_defaults(money_rails: "1.15.0")
41
+
42
+ ENV["MONEY_RAILS_VERSION"] = args.money_rails
43
+
44
+ sh 'echo "Running money-rails regressions tests on money-rails $MONEY_RAILS_VERSION"', verbose: false
45
+ Rake::Task["prepare_env"].invoke
46
+ sh "bin/prepare_money_rails_specs", verbose: false
47
+
48
+ Bundler.with_original_env do
49
+ Dir.chdir("spec_money_rails") do
50
+ sh "BUNDLE_GEMFILE='' bundle install", verbose: false
51
+ sh "BUNDLE_GEMFILE='' bundle exec rake spec:all", verbose: false
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ task :prepare_env do
58
+ sh "export BUNDLE_GEMFILE=Gemfile_test", verbose: false
59
+ sh "rm -f Gemfile_test.lock", verbose: false
60
+ sh "bundle install", verbose: false
61
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MoneyWithDate
4
+ module ActiveRecord
5
+ module ClassMethods
6
+ attr_accessor :default_date_column
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MoneyWithDate
4
+ module ActiveRecord
5
+ module Monetizable
6
+ def read_monetized(name, subunit_name, options = {}, *args)
7
+ money = super(name, subunit_name, options, *args)
8
+ date = find_date_for(options[:with_model_date], options[:with_date])
9
+
10
+ if money&.date == date
11
+ money
12
+ else
13
+ instance_variable_set("@#{name}", money&.with_date(date))
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def find_date_for(instance_date_name, field_date_name) # rubocop:disable Metrics/MethodLength
20
+ if instance_date_name && respond_to?(instance_date_name)
21
+ public_send(instance_date_name)
22
+ elsif field_date_name.respond_to?(:call)
23
+ field_date_name.call(self)
24
+ elsif field_date_name
25
+ field_date_name
26
+ elsif ::Money.default_date_column && respond_to?(::Money.default_date_column)
27
+ public_send(::Money.default_date_column)
28
+ else
29
+ ::Money.default_date
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MoneyWithDate
4
+ module Bank
5
+ class VariableExchange < ::Money::Bank::VariableExchange
6
+ def initialize(store = MoneyWithDate::RatesStore::Memory.new, &block)
7
+ super
8
+ end
9
+
10
+ # rubocop:disable all
11
+ def exchange_with(from, to_currency, &block)
12
+ to_currency = ::Money::Currency.wrap(to_currency)
13
+ if from.currency == to_currency
14
+ from
15
+ else
16
+ if (rate = get_rate(from.currency, to_currency, from.date))
17
+ fractional = calculate_fractional(from, to_currency)
18
+ from.dup_with(
19
+ fractional: exchange(fractional, rate, &block),
20
+ currency: to_currency,
21
+ bank: self
22
+ )
23
+ else
24
+ raise ::Money::Bank::UnknownRate, "No conversion rate known for '#{from.currency.iso_code}' -> '#{to_currency}' on date #{from.date}"
25
+ end
26
+ end
27
+ end
28
+ # rubocop:enable all
29
+
30
+ def add_rate(from, to, rate, date)
31
+ set_rate(from, to, rate, date)
32
+ end
33
+
34
+ # rubocop:disable Metrics/AbcSize
35
+ def set_rate(from, to, rate, date)
36
+ if store.method(:add_rate).parameters.size == 3
37
+ store.add_rate(::Money::Currency.wrap(from).iso_code, ::Money::Currency.wrap(to).iso_code, rate)
38
+ else
39
+ store.add_rate(::Money::Currency.wrap(from).iso_code, ::Money::Currency.wrap(to).iso_code, rate,
40
+ ::Money.parse_date(date))
41
+ end
42
+ end
43
+
44
+ def get_rate(from, to, date)
45
+ if store.method(:get_rate).parameters.size == 2
46
+ store.get_rate(::Money::Currency.wrap(from).iso_code, ::Money::Currency.wrap(to).iso_code)
47
+ else
48
+ store.get_rate(::Money::Currency.wrap(from).iso_code, ::Money::Currency.wrap(to).iso_code,
49
+ ::Money.parse_date(date))
50
+ end
51
+ end
52
+ # rubocop:enable Metrics/AbcSize
53
+
54
+ def rates
55
+ return super if store.method(:get_rate).parameters.size == 2
56
+
57
+ store.each_rate.each_with_object({}) do |(from, to, rate, date), hash|
58
+ hash[date.to_s] ||= {}
59
+ hash[date.to_s][[from, to].join(SERIALIZER_SEPARATOR)] = rate
60
+ end
61
+ end
62
+
63
+ def import_rates(format, s, opts = {}) # rubocop:disable all
64
+ return super if store.method(:add_rate).parameters.size == 3
65
+
66
+ raise Money::Bank::UnknownRateFormat unless RATE_FORMATS.include?(format)
67
+
68
+ if format == :ruby
69
+ warn "[WARNING] Using :ruby format when importing rates is potentially unsafe and " \
70
+ "might lead to remote code execution via Marshal.load deserializer. Consider using " \
71
+ "safe alternatives such as :json and :yaml."
72
+ end
73
+
74
+ store.transaction do
75
+ data = FORMAT_SERIALIZERS[format].load(s)
76
+
77
+ data.each do |date, rates|
78
+ rates.each do |key, rate|
79
+ from, to = key.split(SERIALIZER_SEPARATOR)
80
+ add_rate(from, to, rate, date)
81
+ end
82
+ end
83
+ end
84
+
85
+ self
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MoneyWithDate
4
+ module ClassMethods
5
+ attr_accessor :date_determines_equality
6
+
7
+ attr_writer :default_date
8
+
9
+ def default_date
10
+ if @default_date.respond_to?(:call)
11
+ @default_date.call
12
+ else
13
+ @default_date
14
+ end
15
+ end
16
+
17
+ def add_rate(from_currency, to_currency, rate, date = ::Money.default_date)
18
+ if ::Money.default_bank.method(:add_rate).parameters.size == 3
19
+ ::Money.default_bank.add_rate(from_currency, to_currency, rate)
20
+ else
21
+ ::Money.default_bank.add_rate(from_currency, to_currency, rate, date)
22
+ end
23
+ end
24
+
25
+ def parse_date(date)
26
+ return ::Money.default_date unless date
27
+ return date.to_date if date.respond_to?(:to_date)
28
+
29
+ ::Date.parse(date)
30
+ rescue ArgumentError, TypeError, ::Date::Error # rubocop:disable Lint/ShadowedException
31
+ raise ArgumentError, "#{date.inspect} cannot be parsed as Date"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MoneyWithDate
4
+ class Hooks
5
+ def self.init
6
+ ::ActiveSupport.on_load(:active_record) do
7
+ require "money_with_date/active_record/monetizable"
8
+ require "money_with_date/active_record/class_methods"
9
+
10
+ ::ActiveRecord::Base.prepend(::MoneyWithDate::ActiveRecord::Monetizable)
11
+ ::Money.singleton_class.prepend(::MoneyWithDate::ActiveRecord::ClassMethods)
12
+
13
+ ::Money.default_date_column = :created_at
14
+ end
15
+ end
16
+
17
+ def self.init?
18
+ money_rails_version = ::Gem::Version.new(::MoneyRails::VERSION)
19
+
20
+ money_rails_version >= ::Gem::Version.new(::MoneyWithDate::MINIMUM_MONEY_RAILS_VERSION) &&
21
+ money_rails_version <= ::Gem::Version.new(::MoneyWithDate::MAXIMUM_MONEY_RAILS_VERSION)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MoneyWithDate
4
+ module InstanceMethods
5
+ attr_reader :date
6
+
7
+ def initialize(obj, currency = ::Money.default_currency, options = {})
8
+ if options.is_a?(::Hash)
9
+ @date = self.class.parse_date(options[:date])
10
+ else
11
+ @date = ::Money.default_date
12
+ end
13
+
14
+ raise ArgumentError, "#{@date.inspect} is not an instance of Date" unless @date.is_a?(::Date)
15
+
16
+ super
17
+ end
18
+
19
+ def hash
20
+ return super unless ::Money.date_determines_equality
21
+
22
+ [fractional.hash, currency.hash, date.hash].hash
23
+ end
24
+
25
+ def inspect
26
+ "#<#{self.class.name} fractional:#{fractional} currency:#{currency} date:#{date}>"
27
+ end
28
+
29
+ def with_date(new_date)
30
+ new_date = self.class.parse_date(new_date)
31
+
32
+ if date == new_date
33
+ self
34
+ else
35
+ dup_with(date: new_date)
36
+ end
37
+ end
38
+
39
+ def dup_with(options = {})
40
+ self.class.new(
41
+ options[:fractional] || fractional,
42
+ options[:currency] || currency,
43
+ bank: options[:bank] || bank,
44
+ date: options[:date] || date
45
+ )
46
+ end
47
+
48
+ def eql?(other)
49
+ return super unless ::Money.date_determines_equality
50
+
51
+ if other.is_a?(::Money)
52
+ (fractional == other.fractional && currency == other.currency && date == other.date) ||
53
+ (fractional.zero? && other.fractional.zero?)
54
+ else
55
+ false
56
+ end
57
+ end
58
+
59
+ def <=>(other) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
60
+ return super unless ::Money.date_determines_equality
61
+
62
+ unless other.is_a?(::Money)
63
+ return unless other.respond_to?(:zero?) && other.zero?
64
+
65
+ return other.is_a?(::Money::Arithmetic::CoercedNumeric) ? 0 <=> fractional : fractional <=> 0
66
+ end
67
+
68
+ return fractional <=> other.fractional if zero? || other.zero?
69
+
70
+ other = other.exchange_to(currency)
71
+ [fractional, date] <=> [other.fractional, other.date]
72
+ rescue ::Money::Bank::UnknownRate
73
+ nil
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "money_with_date/hooks"
4
+
5
+ module MoneyWithDate
6
+ class Railtie < ::Rails::Railtie
7
+ initializer "money_with_date.initialize", after: "moneyrails.initialize" do
8
+ MoneyWithDate::Hooks.init if MoneyWithDate::Hooks.init?
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MoneyWithDate
4
+ module RatesStore
5
+ class Memory < ::Money::RatesStore::Memory
6
+ def add_rate(currency_iso_from, currency_iso_to, rate, date)
7
+ guard.synchronize do
8
+ date_key = date_key_for(date)
9
+ rates[date_key] ||= {}
10
+ rates[date_key][rate_key_for(currency_iso_from, currency_iso_to)] = rate
11
+ end
12
+ end
13
+
14
+ def get_rate(currency_iso_from, currency_iso_to, date)
15
+ guard.synchronize do
16
+ rates.dig(date_key_for(date), rate_key_for(currency_iso_from, currency_iso_to))
17
+ end
18
+ end
19
+
20
+ def each_rate
21
+ return to_enum(:each_rate) unless block_given?
22
+
23
+ guard.synchronize do
24
+ rates.each do |date, date_rates|
25
+ date_rates.each do |key, rate|
26
+ iso_from, iso_to = key.split(INDEX_KEY_SEPARATOR)
27
+ yield iso_from, iso_to, rate, date_for_key(date)
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def date_key_for(date)
36
+ date.to_date.to_s
37
+ end
38
+
39
+ def date_for_key(key)
40
+ ::Date.parse(key)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MoneyWithDate
4
+ VERSION = "0.1.0"
5
+
6
+ MINIMUM_MONEY_VERSION = "6.14.0"
7
+ MINIMUM_MONEY_RAILS_VERSION = "1.15.0"
8
+
9
+ MAXIMUM_MONEY_VERSION = "6.16.0"
10
+ MAXIMUM_MONEY_RAILS_VERSION = "1.15.0"
11
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "money"
5
+
6
+ require_relative "money_with_date/version"
7
+ require_relative "money_with_date/class_methods"
8
+ require_relative "money_with_date/instance_methods"
9
+ require_relative "money_with_date/bank/variable_exchange"
10
+ require_relative "money_with_date/rates_store/memory"
11
+
12
+ ::Money.prepend(::MoneyWithDate::InstanceMethods)
13
+ ::Money.singleton_class.prepend(::MoneyWithDate::ClassMethods)
14
+
15
+ # :nocov:
16
+ ::Money.date_determines_equality = false
17
+ ::Money.default_date = ::Date.respond_to?(:current) ? -> { ::Date.current } : -> { ::Date.today }
18
+
19
+ require "money_with_date/railtie" if defined?(::Rails::Railtie)
20
+ # :nocov:
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/money_with_date/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "money_with_date"
7
+ spec.version = MoneyWithDate::VERSION
8
+ spec.authors = ["Lovro Bikić"]
9
+ spec.email = ["lovro.bikic@gmail.com"]
10
+
11
+ spec.summary = "Extension for the money gem which adds dates to Money objects"
12
+ spec.homepage = "https://github.com/infinum/money_with_date"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = spec.homepage
18
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
19
+
20
+ spec.metadata["rubygems_mfa_required"] = "true"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
27
+ end
28
+ end
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_dependency "money", ">= #{MoneyWithDate::MINIMUM_MONEY_VERSION}", "<= #{MoneyWithDate::MAXIMUM_MONEY_VERSION}" # rubocop:disable Layout/LineLength
32
+
33
+ spec.add_development_dependency "pry"
34
+ spec.add_development_dependency "rake"
35
+ spec.add_development_dependency "rspec"
36
+ spec.add_development_dependency "rubocop"
37
+ spec.add_development_dependency "rubocop-rake"
38
+ spec.add_development_dependency "rubocop-rspec"
39
+ end
metadata ADDED
@@ -0,0 +1,172 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: money_with_date
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Lovro Bikić
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-02-18 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.14.0
20
+ - - "<="
21
+ - !ruby/object:Gem::Version
22
+ version: 6.16.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 6.14.0
30
+ - - "<="
31
+ - !ruby/object:Gem::Version
32
+ version: 6.16.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: pry
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rubocop
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rubocop-rake
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: rubocop-rspec
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ description:
118
+ email:
119
+ - lovro.bikic@gmail.com
120
+ executables: []
121
+ extensions: []
122
+ extra_rdoc_files: []
123
+ files:
124
+ - ".rspec"
125
+ - ".rubocop.yml"
126
+ - CHANGELOG.md
127
+ - CODE_OF_CONDUCT.md
128
+ - Gemfile
129
+ - Gemfile.lock
130
+ - Gemfile_test
131
+ - LICENSE
132
+ - README.md
133
+ - Rakefile
134
+ - lib/money_with_date.rb
135
+ - lib/money_with_date/active_record/class_methods.rb
136
+ - lib/money_with_date/active_record/monetizable.rb
137
+ - lib/money_with_date/bank/variable_exchange.rb
138
+ - lib/money_with_date/class_methods.rb
139
+ - lib/money_with_date/hooks.rb
140
+ - lib/money_with_date/instance_methods.rb
141
+ - lib/money_with_date/railtie.rb
142
+ - lib/money_with_date/rates_store/memory.rb
143
+ - lib/money_with_date/version.rb
144
+ - money_with_date.gemspec
145
+ homepage: https://github.com/infinum/money_with_date
146
+ licenses:
147
+ - MIT
148
+ metadata:
149
+ homepage_uri: https://github.com/infinum/money_with_date
150
+ source_code_uri: https://github.com/infinum/money_with_date
151
+ changelog_uri: https://github.com/infinum/money_with_date/blob/master/CHANGELOG.md
152
+ rubygems_mfa_required: 'true'
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: 2.6.0
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubygems_version: 3.2.3
169
+ signing_key:
170
+ specification_version: 4
171
+ summary: Extension for the money gem which adds dates to Money objects
172
+ test_files: []