dotiw 5.3.2 → 5.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5bbd17f6b2f3af46a5255fa9eb900aa7bb734ead661088a2936739aa2f2bef63
4
- data.tar.gz: 2870eadd1e540d51e6ff73210024fbcb87de8c01cca2549d7fb80e30b4390d93
3
+ metadata.gz: 32864f97b3b06dc77f8c793e11a5b17b1d2af506bdfac0c559354f3e4eef7998
4
+ data.tar.gz: 20efed42fb0802a836f078f2791d78e7b863005b73780243ad4867bbf653103e
5
5
  SHA512:
6
- metadata.gz: a89bfcad7275d64474969ac9200f43d6b5b3fb922c6cfa9e4bc05550361f43dc3d1eb37c27771116dc51cfba6adfa844232fbc062592dfa2874ff0e355797c3b
7
- data.tar.gz: 8379db832851f15302fa364103671c3d805c0f367d8c8c0be4d746e81a473ab4b7d49c29a248fd353410a14e22ed04db79a1bfb05b83f4fd52403781460712fa
6
+ metadata.gz: 6bb97a0d7bc860b57432dc81cb3d8a7921c6364c5271db648d30984164f26d6f4003e21de28252f958294960df1b0270cc4fbf83ae7602be23096829404ee2fe
7
+ data.tar.gz: e2d4f70428937b7e5c409666612dd7aefbe63e71d884a3738b240c222fc2f36a8df7a66b83fa4d52fef96cd27e7edd3a9fdecb15e497fa46853e9b3922f83663
@@ -1,12 +1,6 @@
1
1
  name: Ruby
2
2
 
3
- on:
4
- push:
5
- branches:
6
- - master
7
- pull_request:
8
- branches:
9
- - master
3
+ on: [push, pull_request]
10
4
 
11
5
  jobs:
12
6
  build:
@@ -15,19 +9,26 @@ jobs:
15
9
  fail-fast: false
16
10
  matrix:
17
11
  include:
18
- - ruby-version: 2.5
19
- - ruby-version: 2.6
20
- - ruby-version: 2.4
21
- bundler-version: 1.17.3
22
- gemfile: gemfiles/rails_4.gemfile
23
- - ruby-version: 2.6
12
+ - ruby-version: 3.4
13
+ - ruby-version: 3.3
14
+ - ruby-version: 3.2
15
+ - ruby-version: 3.1
16
+ - ruby-version: 3.0
17
+ - ruby-version: 2.7
18
+ - ruby-version: 2.7
24
19
  gemfile: gemfiles/rails_5.0.gemfile
25
- - ruby-version: 2.6
20
+ - ruby-version: 2.7
26
21
  gemfile: gemfiles/rails_5.1.gemfile
27
- - ruby-version: 2.6
22
+ - ruby-version: 2.7
28
23
  gemfile: gemfiles/rails_5.2.gemfile
29
- - ruby-version: 2.6
24
+ - ruby-version: 3.4
30
25
  gemfile: gemfiles/rails_6.0.gemfile
26
+ - ruby-version: 3.4
27
+ gemfile: gemfiles/rails_7.0.gemfile
28
+ - ruby-version: 3.4
29
+ gemfile: gemfiles/rails_7.1.gemfile
30
+ - ruby-version: 3.4
31
+ gemfile: gemfiles/rails_7.2.gemfile
31
32
  steps:
32
33
  - uses: actions/checkout@v2
33
34
  - name: Set up Ruby
data/Appraisals CHANGED
@@ -1,7 +1,3 @@
1
- appraise 'rails_4' do
2
- gem 'rails', '~> 4.0'
3
- end
4
-
5
1
  appraise 'rails_5.0' do
6
2
  gem 'rails', '~> 5.0.0'
7
3
  end
@@ -17,3 +13,15 @@ end
17
13
  appraise 'rails_6.0' do
18
14
  gem 'rails', '~> 6.0.0'
19
15
  end
16
+
17
+ appraise 'rails_7.0' do
18
+ gem 'rails', '~> 7.0.0'
19
+ end
20
+
21
+ appraise 'rails_7.1' do
22
+ gem 'rails', '~> 7.1.0'
23
+ end
24
+
25
+ appraise 'rails_7.2' do
26
+ gem 'rails', '~> 7.2.0'
27
+ end
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## 5.4.0 (Next)
2
+
3
+ * [#131](https://github.com/radar/distance_of_time_in_words/pull/131): Deprecates `highest_measure_only`. Adds alternate form of `highest_measures` option to permit rounding up whatever part of the duration was previously silently discarded - [@seansfkelley](https://github.com/seansfkelley).
4
+ * [#133](https://github.com/radar/distance_of_time_in_words/pull/133): Test on Ruby 3.0 and 3.1 - [@dblock](https://github.com/dblock).
5
+ * [#134](https://github.com/radar/distance_of_time_in_words/pull/134): Add support for Dzongkha, the National language of Bhutan - [@KinWang-2013](https://github.com/KinWang-2013).
6
+ * [#135](https://github.com/radar/distance_of_time_in_words/pull/135): Add support for Ruby 2.7 and 3.2, removed support for ruby 2.4, 2.5, 2.6 and Rails 4 - [@KinWang-2013](https://github.com/KinWang-2013).
7
+ * [#136](https://github.com/radar/distance_of_time_in_words/pull/136): Add support for Rails 7 - [@KinWang-2013](https://github.com/KinWang-2013).
8
+ * [#139](https://github.com/radar/distance_of_time_in_words/pull/139): Fix bug with pluralization of Russian translations for numbers ending in 1 - [bitberry-dev](https://github.com/bitberry-dev).
9
+ * Your contribution here.
10
+
11
+ ## 5.3.3 (2022/04/25)
12
+
13
+ * [#128](https://github.com/radar/distance_of_time_in_words/pull/128): Adds support for numeric values for both the `from_time` and `to_time` arguments in the `#distance_of_time_in_words` helper - [@bjedrocha](https://github.com/bjedrocha).
14
+
1
15
  ## 5.3.2 (2021/11/08)
2
16
 
3
17
  * [#126](https://github.com/radar/distance_of_time_in_words/pull/126): Fixes `#distance_of_time_in_words_to_now` with `vague: true` when supplied without `include_seconds` argument - [@mozcomp](https://github.com/mozcomp).
data/CODEOWNERS ADDED
@@ -0,0 +1,2 @@
1
+ @radar
2
+ *.gemspec @radar
data/README.markdown CHANGED
@@ -58,6 +58,22 @@ Better than "about 1 year", am I right? Of course I am.
58
58
  => "1 second"
59
59
  ```
60
60
 
61
+ It also supports numeric arguments like the original Rails version:
62
+
63
+ ```ruby
64
+ >> distance_of_time_in_words(0, 150)
65
+ => "2 minutes and 30 seconds"
66
+ ```
67
+
68
+ as an alternative to:
69
+
70
+ ```ruby
71
+ >> distance_of_time_in_words(Time.now, Time.now + 2.5.minutes)
72
+ => "2 minutes and 30 seconds"
73
+ ```
74
+
75
+ This is useful if you're just interested in "stringifying" the length of time. Alternatively, you can use the `#distance_of_time` helper as described [below](#distance\_of\_time).
76
+
61
77
  The third argument for this method is whether or not to include seconds. By default this is `false` (because in Rails' `distance_of_time_in_words` it is), you can turn it on though by passing `true` as the third argument:
62
78
 
63
79
  ```ruby
@@ -148,6 +164,8 @@ Culling a whole group of measurements of time:
148
164
 
149
165
  #### :highest\_measure\_only
150
166
 
167
+ > **Deprecated**. Use `highest_measures: 1` instead.
168
+
151
169
  For times when Rails `distance_of_time_in_words` is not precise enough and `DOTIW` is too precise. For instance, if you only want to know the highest time part (measure) that elapsed between two dates.
152
170
 
153
171
  ```ruby
@@ -173,6 +191,24 @@ When you want variable precision from `DOTIW`:
173
191
  => "1 hour and 1 minute"
174
192
  ```
175
193
 
194
+ You can also specify what to do with the extra time with the `remainder` option:
195
+
196
+ ```ruby
197
+ >> distance_of_time_in_words(Time.now, Time.now + 1.hour + 1.minute + 1.second, true, highest_measures: { max: 2, remainder: :ceiling })
198
+ => "1 hour and 2 minutes"
199
+ ```
200
+
201
+ Valid options for `remainder` are `:floor` (default), `:ceiling` and `:round`. Note that `:round` is best-effort and makes some simplifying assumptions:
202
+
203
+ ```ruby
204
+ # Only the next-largest unit is examined, which can unexpectedly round down in some situations.
205
+ >> distance_of_time_in_words(Time.now, Time.now + 1.week + 3.days + 23.hours, true, highest_measures: { remainder: :round })
206
+ => "1 week"
207
+ # The variability of some measures (like months) is ignored and the shortest duration of that measure is used.
208
+ >> distance_of_time_in_words(Time.now, Time.now + 1.month + 14.days, true, highest_measures: { remainder: :round })
209
+ => "2 months"
210
+ ```
211
+
176
212
  #### :words_connector
177
213
 
178
214
  This is an option for `to_sentence`, defaults to ', '.
data/dotiw.gemspec CHANGED
@@ -27,6 +27,9 @@ Gem::Specification.new do |s|
27
27
  s.add_development_dependency 'rake'
28
28
  s.add_development_dependency 'rspec', '~> 3.0'
29
29
  s.add_development_dependency 'tzinfo', '~> 1.2.7'
30
+ s.add_development_dependency 'mutex_m'
31
+ s.add_development_dependency 'base64'
32
+ s.add_development_dependency 'bigdecimal'
30
33
 
31
34
  s.files = `git ls-files`.split("\n")
32
35
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -4,6 +4,7 @@
4
4
 
5
5
  source 'http://rubygems.org'
6
6
 
7
- gem 'rails', '~> 4.0'
7
+ gem 'rails', '~> 7.0.0'
8
+ gem 'tzinfo', '~> 2.0'
8
9
 
9
10
  gemspec path: '../'
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was generated by Appraisal
4
+
5
+ source 'http://rubygems.org'
6
+
7
+ gem 'rails', '~> 7.1.0'
8
+ gem 'tzinfo', '~> 2.0'
9
+
10
+
11
+ gemspec path: '../'
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was generated by Appraisal
4
+
5
+ source 'http://rubygems.org'
6
+
7
+ gem 'rails', '~> 7.2.0'
8
+ gem 'tzinfo', '~> 2.0'
9
+
10
+
11
+ gemspec path: '../'
@@ -0,0 +1,60 @@
1
+ dz:
2
+ datetime:
3
+ dotiw:
4
+ seconds:
5
+ one: སྐར་ཆ་ ༡
6
+ other: "སྐར་ཆ་ %{count}"
7
+ minutes:
8
+ one: སྐར་མ་ ༡
9
+ other: "སྐར་མ་ %{count}"
10
+ hours:
11
+ one: ཆུ་ཚོད་ ༡
12
+ other: "ཆུ་ཚོད་ %{count}"
13
+ days:
14
+ one: ཉིནམ་ ༡
15
+ other: "ཉིནམ་ %{count}"
16
+ weeks:
17
+ one: བདུན༌ཕྲག་ ༡
18
+ other: "བདུན༌ཕྲག་ %{count}"
19
+ months:
20
+ one: ཟླཝ་ ༡
21
+ other: "ཟླཝ་ %{count}"
22
+ years:
23
+ one: ལོ་ ༡
24
+ other: "ལོ་ %{count}"
25
+ less_than_x: "%{distance} ་ལས་ཉུངམ་"
26
+ dotiw_compact:
27
+ seconds:
28
+ one: སྐར་ཆ་ ༡
29
+ other: "སྐར་ཆ་ %{count}"
30
+ minutes:
31
+ one: སྐར་མ་ ༡
32
+ other: "སྐར་མ་ %{count}"
33
+ hours:
34
+ one: ཆུ་ཚོད་ ༡
35
+ other: "ཆུ་ཚོད་ %{count}"
36
+ days:
37
+ one: ཉིནམ་ ༡
38
+ other: "ཉིནམ་ %{count}"
39
+ weeks:
40
+ one: བདུན༌ཕྲག་ ༡
41
+ other: "བདུན༌ཕྲག་ %{count}"
42
+ months:
43
+ one: ཟླཝ་ ༡
44
+ other: "ཟླཝ་ %{count}"
45
+ years:
46
+ one: ལོ་ ༡
47
+ other: "ལོ་ %{count}"
48
+ less_than_x: "%{distance} ་ལས་ཉུངམ"
49
+ words_connector: ""
50
+ two_words_connector: " དང་ "
51
+ last_word_connector: " དང་ "
52
+ about_x_years:
53
+ one: ལོ་ ༡ ་དེ་ཅིག
54
+ any: "ལོ་ %{count} ་དེ་ཅིག"
55
+ over_x_years:
56
+ one: "ལོ་ ༡ ་ལས་ལྷགཔ་ཅིག"
57
+ other: "ལོ་ %{count} ་ལས་ལྷགཔ་ཅིག"
58
+ almost_x_years:
59
+ one: ལོ་ ༡ ་མ་ལྷགཔ་ཅིག
60
+ any: "ལོ་ %{count} ་མ་ལྷགཔ་ཅིག"
@@ -39,22 +39,22 @@ ru:
39
39
  less_than_x: "меньше, чем %{distance}"
40
40
  dotiw_compact:
41
41
  seconds:
42
- one: 1с
42
+ one: "%{count}с"
43
43
  other: "%{count}с"
44
44
  minutes:
45
- one: 1м
45
+ one: "%{count}м"
46
46
  other: "%{count}м"
47
47
  hours:
48
- one: 1ч
48
+ one: "%{count}ч"
49
49
  other: "%{count}ч"
50
50
  days:
51
- one: 1д
51
+ one: "%{count}д"
52
52
  other: "%{count}д"
53
53
  weeks:
54
- one: 1н
54
+ one: "%{count}н"
55
55
  other: "%{count}н"
56
56
  months:
57
- one: 1ме
57
+ one: "%{count}ме"
58
58
  other: "%{count}ме"
59
59
  years:
60
60
  one: "%{count}г"
@@ -66,11 +66,11 @@ ru:
66
66
  two_words_connector: ""
67
67
  last_word_connector: ""
68
68
  about_x_years:
69
- one: ~1г
69
+ one: "~%{count}г"
70
70
  any: "~%{count}г"
71
71
  over_x_years:
72
- one: ">1г"
72
+ one: ">%{count}г"
73
73
  other: ">%{count}г"
74
74
  almost_x_years:
75
- one: ~1г
75
+ one: "~%{count}г"
76
76
  any: "~%{count}г"
data/lib/dotiw/methods.rb CHANGED
@@ -20,7 +20,9 @@ module DOTIW
20
20
  end
21
21
 
22
22
  def distance_of_time_in_words(from_time, to_time = 0, include_seconds_or_options = {}, options = {})
23
- raise ArgumentError, "nil can't be converted to a Time value" if from_time.nil? || to_time.nil?
23
+ from_time = normalize_distance_of_time_argument_to_time(from_time)
24
+ to_time = normalize_distance_of_time_argument_to_time(to_time)
25
+ from_time, to_time = to_time, from_time if from_time > to_time
24
26
 
25
27
  if include_seconds_or_options.is_a?(Hash)
26
28
  options = include_seconds_or_options
@@ -28,7 +30,8 @@ module DOTIW
28
30
  options = options.dup
29
31
  options[:include_seconds] ||= !!include_seconds_or_options
30
32
  end
31
- return distance_of_time(from_time, options) if to_time == 0
33
+
34
+ return distance_of_time(to_time.to_i, options) if from_time.to_i == 0
32
35
 
33
36
  options = options_with_scope(options)
34
37
  hash = distance_of_time_in_words_hash(from_time, to_time, options)
@@ -41,6 +44,41 @@ module DOTIW
41
44
 
42
45
  private
43
46
 
47
+ # How many of each measure is necessary to round up to one of the next-largest measure. Note
48
+ # that for simplicity of implementation, we only check one measure when seeing if we should
49
+ # round up. This means that rounding up days to weeks, for instance, cannot draw the line at 3
50
+ # days and 12 hours, but instead either 3 or 4 (whole) days. Let's not even talk about weeks
51
+ # rounding up to months.
52
+ ROUNDING_THRESHOLDS = {
53
+ seconds: 30,
54
+ minutes: 30,
55
+ hours: 12,
56
+ days: 4,
57
+ weeks: 2,
58
+ months: 6,
59
+ years: Float::INFINITY,
60
+ }
61
+
62
+ ROLLUP_THRESHOLDS = {
63
+ seconds: 60,
64
+ minutes: 60,
65
+ hours: 24,
66
+ days: 7,
67
+ weeks: 4, # !!!
68
+ months: 12,
69
+ years: Float::INFINITY,
70
+ }
71
+
72
+ def normalize_distance_of_time_argument_to_time(value)
73
+ if value.is_a?(Numeric)
74
+ Time.at(value)
75
+ elsif value.respond_to?(:to_time)
76
+ value.to_time
77
+ else
78
+ raise ArgumentError, "#{value.inspect} can't be converted to a Time value"
79
+ end
80
+ end
81
+
44
82
  def options_with_scope(options)
45
83
  if options.key?(:compact)
46
84
  options.merge(scope: DOTIW::DEFAULT_I18N_SCOPE_COMPACT)
@@ -50,23 +88,29 @@ module DOTIW
50
88
  end
51
89
 
52
90
  def _display_time_in_words(hash, options = {})
91
+ hash = hash.dup
92
+
53
93
  options = options.reverse_merge(
54
94
  include_seconds: false
55
95
  ).symbolize_keys!
56
96
 
97
+ discarded_hash = {}
98
+
57
99
  include_seconds = options.delete(:include_seconds)
58
- hash.delete(:seconds) if !include_seconds && hash[:minutes]
100
+ discarded_hash[:seconds] = hash.delete(:seconds) if !include_seconds && hash[:minutes]
59
101
 
60
102
  options[:except] = Array.wrap(options[:except]).map!(&:to_sym) if options[:except]
61
103
  options[:only] = Array.wrap(options[:only]).map!(&:to_sym) if options[:only]
62
104
 
63
- # Remove all the values that are nil or excluded. Keep the required ones.
64
- hash.delete_if do |key, value|
65
- value.nil? || value.zero? ||
66
- options[:except]&.include?(key) ||
67
- (options[:only] && !options[:only].include?(key))
105
+ DOTIW::TimeHash::TIME_FRACTIONS.each do |fraction|
106
+ if options[:except]&.include?(fraction) || (options[:only] && !options[:only].include?(fraction))
107
+ discarded_hash[fraction] = hash.delete fraction
108
+ end
68
109
  end
69
110
 
111
+ hash.delete_if { |key, value| value.nil? || value.zero? }
112
+ discarded_hash.delete_if { |key, value| value.nil? || value.zero? }
113
+
70
114
  i18n_scope = options.delete(:scope) || DOTIW::DEFAULT_I18N_SCOPE
71
115
  if hash.empty?
72
116
  fractions = DOTIW::TimeHash::TIME_FRACTIONS
@@ -81,16 +125,22 @@ module DOTIW
81
125
  end
82
126
  end
83
127
 
84
- output = []
85
- I18n.with_options locale: options[:locale], scope: i18n_scope do |locale|
86
- output = hash.map { |key, value| locale.t(key, count: value) }
87
- end
88
-
89
128
  options.delete(:except)
90
129
  options.delete(:only)
91
- highest_measures = options.delete(:highest_measures)
92
- highest_measures = 1 if options.delete(:highest_measure_only)
93
- output = output[0...highest_measures] if highest_measures
130
+
131
+ highest_measures = _compute_highest_measures! options
132
+ if highest_measures
133
+ high_entries, low_entries = hash.to_a.partition.with_index { |_, index| index < highest_measures[:max] }
134
+ hash = high_entries.to_h
135
+ discarded_hash.merge! low_entries.to_h
136
+
137
+ _maybe_round! hash, discarded_hash, highest_measures[:remainder]
138
+ end
139
+
140
+ phrases = []
141
+ I18n.with_options locale: options[:locale], scope: i18n_scope do |locale|
142
+ phrases = hash.map { |key, value| locale.t(key, count: value) }
143
+ end
94
144
 
95
145
  options[:words_connector] ||= I18n.translate :"#{i18n_scope}.words_connector",
96
146
  default: :'support.array.words_connector',
@@ -102,7 +152,56 @@ module DOTIW
102
152
  default: :'support.array.last_word_connector',
103
153
  locale: options[:locale]
104
154
 
105
- output.to_sentence(options.except(:accumulate_on, :compact))
155
+ phrases.to_sentence(options.except(:accumulate_on, :compact))
156
+ end
157
+
158
+ def _compute_highest_measures!(options)
159
+ highest_measures = options.delete(:highest_measures)
160
+ highest_measures = 1 if options.delete(:highest_measure_only)
161
+ highest_measures = { max: highest_measures } if highest_measures.is_a?(Integer)
162
+ highest_measures = highest_measures.reverse_merge(max: 1, remainder: :floor) if highest_measures
163
+
164
+ highest_measures
165
+ end
166
+
167
+ def _maybe_round!(hash, discarded_hash, remainder)
168
+ smallest_measure_index = DOTIW::TimeHash::TIME_FRACTIONS.index hash.to_a.last[0]
169
+ smallest_measure = DOTIW::TimeHash::TIME_FRACTIONS[smallest_measure_index]
170
+
171
+ case remainder
172
+ when :floor
173
+ # Nothing to do.
174
+ when :ceiling
175
+ # We already filtered out zeroes, so non-empty also means non-zero.
176
+ if !discarded_hash.empty?
177
+ hash[smallest_measure] += 1
178
+ _rollup! hash, smallest_measure_index
179
+ end
180
+ when :round
181
+ # If our smallest measure is already the smallest possible measure, there is no next
182
+ # smallest measure to inspect to see if we need to round up.
183
+ return if smallest_measure_index == 0
184
+
185
+ next_smallest_measure = DOTIW::TimeHash::TIME_FRACTIONS[smallest_measure_index - 1]
186
+ if discarded_hash.fetch(next_smallest_measure, 0) >= ROUNDING_THRESHOLDS[next_smallest_measure]
187
+ hash[smallest_measure] += 1
188
+ _rollup! hash, smallest_measure_index
189
+ end
190
+ else
191
+ raise ArgumentError, "unrecognized remainder value #{remainder.inspect}"
192
+ end
193
+ end
194
+
195
+ def _rollup!(hash, smallest_measure_index)
196
+ DOTIW::TimeHash::TIME_FRACTIONS[smallest_measure_index..-1].each_with_index do |fraction, index|
197
+ if hash.fetch(fraction, 0) >= ROLLUP_THRESHOLDS[fraction]
198
+ hash.delete fraction
199
+ next_fraction = DOTIW::TimeHash::TIME_FRACTIONS[smallest_measure_index + index + 1]
200
+ hash[next_fraction] = hash.fetch(next_fraction, 0) + 1
201
+ else
202
+ break
203
+ end
204
+ end
106
205
  end
107
206
  end
108
207
  end
@@ -43,8 +43,6 @@ module DOTIW
43
43
  def build_time_hash
44
44
  if accumulate_on = options[:accumulate_on]
45
45
  accumulate_on = accumulate_on.to_sym
46
- return build_time_hash if accumulate_on == :years
47
-
48
46
  TIME_FRACTIONS.index(accumulate_on).downto(0) { |i| send("build_#{TIME_FRACTIONS[i]}") }
49
47
  else
50
48
  while distance > 0
@@ -91,11 +89,15 @@ module DOTIW
91
89
  def build_months
92
90
  build_years_months_weeks_days
93
91
 
94
- if (years = output.delete(:years)) > 0
92
+ if options[:accumulate_on]&.to_sym == :months && (years = output.delete(:years)) > 0
95
93
  output[:months] += (years * 12)
96
94
  end
97
95
  end
98
96
 
97
+ def build_years
98
+ build_years_months_weeks_days
99
+ end
100
+
99
101
  def build_years_months_weeks_days
100
102
  months = (largest.year - smallest.year) * 12 + (largest.month - smallest.month)
101
103
  years, months = months.divmod(12)
data/lib/dotiw/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DOTIW
4
- VERSION = '5.3.2'
4
+ VERSION = '5.5.0'
5
5
  end
data/lib/dotiw.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'i18n'
4
-
4
+ require 'logger'
5
5
  require 'active_support'
6
6
  require 'active_support/core_ext'
7
7
 
@@ -13,10 +13,12 @@ describe 'A better distance_of_time_in_words' do
13
13
  include DOTIW::Methods
14
14
  end
15
15
 
16
- START_TIME = '01-08-2009'.to_time
16
+ START_TIME = '01-08-2009'.to_time(:utc)
17
17
 
18
18
  before do
19
19
  I18n.locale = :en
20
+ ActiveSupport.to_time_preserves_timezone = :zone
21
+
20
22
  allow(Time).to receive(:now).and_return(START_TIME)
21
23
  allow(Time.zone).to receive(:now).and_return(START_TIME)
22
24
  end
@@ -101,11 +103,6 @@ describe 'A better distance_of_time_in_words' do
101
103
  expect(DOTIW.languages.map(&:to_s).sort).to eq languages.sort
102
104
  end
103
105
 
104
- it 'raises ArgumentError when nil is passed for time' do
105
- expect { distance_of_time_in_words(nil) }.to raise_error(ArgumentError)
106
- expect { distance_of_time_in_words(nil, nil) }.to raise_error(ArgumentError)
107
- end
108
-
109
106
  DOTIW.languages.each do |lang|
110
107
  context lang do
111
108
  YAML.safe_load(
@@ -118,12 +115,18 @@ describe 'A better distance_of_time_in_words' do
118
115
  context category do
119
116
  fixtures.each_pair do |k, v|
120
117
  it v do
118
+ options = {
119
+ locale: lang
120
+ }
121
+
122
+ options[:compact] = true if category.split.include?('compact')
123
+
121
124
  expect(
122
125
  distance_of_time_in_words(
123
126
  START_TIME,
124
127
  START_TIME + eval(k),
125
128
  true,
126
- locale: lang
129
+ options
127
130
  )
128
131
  ).to eq(v)
129
132
  end
@@ -170,7 +173,7 @@ describe 'A better distance_of_time_in_words' do
170
173
  [Time.zone.now, Time.zone.now + 15.days - 1.minute, '14 days, 23 hours, and 59 minutes'],
171
174
  [Time.zone.now, Time.zone.now + 29.days - 1.minute, '28 days, 23 hours, and 59 minutes'],
172
175
  [Time.zone.now, Time.zone.now + 30.days - 1.minute, '29 days, 23 hours, and 59 minutes'],
173
- [Time.zone.now, Time.zone.now + 31.days - 1.minute, '30 days, 23 hours, and 59 minutes'],
176
+ [Time.zone.now, Time.zone.now + 31.day - 1.minute, '30 days, 23 hours, and 59 minutes'],
174
177
  [Time.zone.now, Time.zone.now + 32.days - 1.minute, '31 days, 23 hours, and 59 minutes'],
175
178
  [Time.zone.now, Time.zone.now + 33.days - 1.minute, '32 days, 23 hours, and 59 minutes']
176
179
  ].each do |start, finish, output|
@@ -216,7 +219,8 @@ describe 'A better distance_of_time_in_words' do
216
219
  START_TIME + 2.day + 10_000.hour + 10.second,
217
220
  :months,
218
221
  '13 months, 3 weeks, 1 day, 16 hours, and 10 seconds'],
219
- ['2015-1-15'.to_time, '2016-3-15'.to_time, :months, '14 months']
222
+ ['2015-1-15'.to_time, '2016-3-15'.to_time, :months, '14 months'],
223
+ ['2015-1-15'.to_time, '2016-3-15'.to_time, :years, '1 year and 2 months']
220
224
  ].each do |start, finish, accumulator, output|
221
225
  it "should be #{output}" do
222
226
  expect(distance_of_time_in_words(start, finish, true, accumulate_on: accumulator)).to eq(output)
@@ -241,6 +245,38 @@ describe 'A better distance_of_time_in_words' do
241
245
  end
242
246
  end
243
247
  end
248
+
249
+ describe 'with mixed inputs' do
250
+ # Further backwards compatability with the original rails
251
+ # distance_of_time_in_words helper.
252
+ [
253
+ [0, 2.5.minutes.to_i, '2 minutes and 30 seconds'],
254
+ [10.minutes.to_i, 0, '10 minutes'],
255
+ [0, 24.weeks.to_i, '5 months, 2 weeks, and 1 day'],
256
+ [0, 0, 'less than 1 second'],
257
+ ].each do |start, finish, output|
258
+ it "should be #{output}" do
259
+ expect(distance_of_time_in_words(start, finish)).to eq(output)
260
+ end
261
+ end
262
+
263
+ context "of which one or both can't be converted to a Time value" do
264
+ let(:invalid_inputs) do
265
+ [
266
+ [nil, 5.minutes],
267
+ [nil, double(:does_not_respond_to_time)],
268
+ [nil],
269
+ [nil, nil],
270
+ ]
271
+ end
272
+
273
+ it "should raise an ArgumentError" do
274
+ invalid_inputs.each do |start, finish|
275
+ expect { distance_of_time_in_words(start, finish) }.to raise_error(ArgumentError)
276
+ end
277
+ end
278
+ end
279
+ end
244
280
  end
245
281
 
246
282
  describe 'with output options' do
@@ -302,7 +338,7 @@ describe 'A better distance_of_time_in_words' do
302
338
  { highest_measures: 3 },
303
339
  '1 year and 2 weeks'],
304
340
  [START_TIME,
305
- START_TIME + 1.days,
341
+ START_TIME + 1.day,
306
342
  { only: %i[years months] },
307
343
  'less than 1 month'],
308
344
  [START_TIME,
@@ -310,15 +346,55 @@ describe 'A better distance_of_time_in_words' do
310
346
  { except: %i[hours minutes seconds] },
311
347
  'less than 1 day'],
312
348
  [START_TIME,
313
- START_TIME + 1.days,
349
+ START_TIME + 1.day,
314
350
  { highest_measures: 1, only: %i[years months] },
315
- 'less than 1 month']
351
+ 'less than 1 month'],
352
+ [START_TIME,
353
+ START_TIME + 1.day + 1.hour,
354
+ { highest_measures: {} },
355
+ '1 day'],
356
+ [START_TIME,
357
+ START_TIME + 1.day + 1.hour,
358
+ { highest_measures: { remainder: :floor} },
359
+ '1 day'],
360
+ [START_TIME,
361
+ START_TIME + 1.year + 1.minute,
362
+ { highest_measures: { remainder: :ceiling } },
363
+ '2 years'],
364
+ [START_TIME,
365
+ START_TIME + 1.day + 2.hours + 30.minutes,
366
+ { highest_measures: { max: 2, remainder: :round } },
367
+ '1 day and 3 hours'],
368
+ [START_TIME,
369
+ START_TIME + 1.day + 23.hours + 59.minutes + 59.seconds,
370
+ { highest_measures: { max: 3, remainder: :round } },
371
+ '2 days'],
372
+ [START_TIME,
373
+ START_TIME + 1.day,
374
+ { highest_measures: { remainder: :ceiling }, only: :months },
375
+ 'less than 1 month'],
376
+ [START_TIME,
377
+ # Simplistic rounding: one would expect this to round up to a second week.
378
+ START_TIME + 1.week + 3.days + 23.hours,
379
+ { highest_measures: { remainder: :round } },
380
+ '1 week'],
381
+ [START_TIME,
382
+ # Simplistic rounding: in some months, 15 days is less than half, but we always round it up.
383
+ START_TIME + 1.month + 14.days,
384
+ { highest_measures: { remainder: :round } },
385
+ '2 months'],
316
386
  ].each do |start, finish, options, output|
317
387
  it "should be #{output}" do
318
388
  expect(distance_of_time_in_words(start, finish, true, options)).to eq(output)
319
389
  end
320
390
  end
321
391
 
392
+ it "raises ArgumentError when an unrecognized value is passed for highest_measure.remainder" do
393
+ expect do
394
+ distance_of_time_in_words(START_TIME, START_TIME + 1.day, true, { highest_measures: { remainder: :oops }})
395
+ end.to raise_error(ArgumentError)
396
+ end
397
+
322
398
  if defined?(ActionView)
323
399
  describe 'ActionView without include seconds argument' do
324
400
  [
@@ -370,7 +446,7 @@ describe 'A better distance_of_time_in_words' do
370
446
  expect(distance_of_time_in_words(start, finish, true, options)).to eq(output)
371
447
  end
372
448
  end
373
-
449
+
374
450
  context 'via ActionController::Base.helpers' do
375
451
  it '#distance_of_time_in_words' do
376
452
  end_time = START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds
@@ -0,0 +1,21 @@
1
+ seconds:
2
+ 1.second: སྐར་ཆ་ ༡
3
+ 3.seconds: སྐར་ཆ་ 3
4
+ minutes:
5
+ 1.minute: སྐར་མ་ ༡
6
+ 20.minutes: སྐར་མ་ 20
7
+ hours:
8
+ 1.hour: ཆུ་ཚོད་ ༡
9
+ 5.hours: ཆུ་ཚོད་ 5
10
+ days:
11
+ 1.day: ཉིནམ་ ༡
12
+ 6.days: ཉིནམ་ 6
13
+ weeks:
14
+ 1.week: བདུན༌ཕྲག་ ༡
15
+ 2.weeks: བདུན༌ཕྲག་ 2
16
+ months:
17
+ 1.month: ཟླཝ་ ༡
18
+ 9.months: ཟླཝ་ 9
19
+ years:
20
+ 1.year: ལོ་ ༡
21
+ 3.years: ལོ་ 3
data/spec/lib/i18n/ru.yml CHANGED
@@ -3,3 +3,21 @@ seconds:
3
3
  1.second: 1 секунда
4
4
  minutes:
5
5
  1.minute: 1 минута
6
+ compact seconds:
7
+ 1.second: 1с
8
+ 11.seconds: 11с
9
+ 21.seconds: 21с
10
+ compact minutes:
11
+ 1.minute: 1м
12
+ 11.minutes: 11м
13
+ 21.minutes: 21м
14
+ compact hours:
15
+ 1.hour: 1ч
16
+ 11.hours: 11ч
17
+ 21.hours: 21ч
18
+ compact days:
19
+ 1.day: 1д
20
+ compact years:
21
+ 1.year: 1г
22
+ 11.years: 11г
23
+ 21.years: 21г
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dotiw
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.3.2
4
+ version: 5.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Bigg
8
8
  - Lauran Jansen
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2021-11-08 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: activesupport
@@ -123,6 +122,48 @@ dependencies:
123
122
  - - "~>"
124
123
  - !ruby/object:Gem::Version
125
124
  version: 1.2.7
125
+ - !ruby/object:Gem::Dependency
126
+ name: mutex_m
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: base64
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: bigdecimal
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
126
167
  description: |-
127
168
  dotiw is a gem for Rails that overrides the
128
169
  default distance_of_time_in_words and provides
@@ -141,6 +182,7 @@ files:
141
182
  - ".rspec"
142
183
  - Appraisals
143
184
  - CHANGELOG.md
185
+ - CODEOWNERS
144
186
  - CONTRIBUTING.md
145
187
  - Gemfile
146
188
  - MIT-LICENSE
@@ -149,16 +191,19 @@ files:
149
191
  - benchmarks/time_hash_bench.rb
150
192
  - dotiw.gemspec
151
193
  - gemfiles/.bundle/config
152
- - gemfiles/rails_4.gemfile
153
194
  - gemfiles/rails_5.0.gemfile
154
195
  - gemfiles/rails_5.1.gemfile
155
196
  - gemfiles/rails_5.2.gemfile
156
197
  - gemfiles/rails_6.0.gemfile
198
+ - gemfiles/rails_7.0.gemfile
199
+ - gemfiles/rails_7.1.gemfile
200
+ - gemfiles/rails_7.2.gemfile
157
201
  - lib/dotiw.rb
158
202
  - lib/dotiw/action_view/helpers/date_helper.rb
159
203
  - lib/dotiw/locale/ar.yml
160
204
  - lib/dotiw/locale/da.yml
161
205
  - lib/dotiw/locale/de.yml
206
+ - lib/dotiw/locale/dz.yml
162
207
  - lib/dotiw/locale/en.yml
163
208
  - lib/dotiw/locale/es.yml
164
209
  - lib/dotiw/locale/fr.yml
@@ -182,6 +227,7 @@ files:
182
227
  - spec/lib/i18n/ar.yml
183
228
  - spec/lib/i18n/da.yml
184
229
  - spec/lib/i18n/de.yml
230
+ - spec/lib/i18n/dz.yml
185
231
  - spec/lib/i18n/en.yml
186
232
  - spec/lib/i18n/es.yml
187
233
  - spec/lib/i18n/fr.yml
@@ -203,7 +249,6 @@ homepage: https://github.com/radar/distance_of_time_in_words
203
249
  licenses:
204
250
  - MIT
205
251
  metadata: {}
206
- post_install_message:
207
252
  rdoc_options: []
208
253
  require_paths:
209
254
  - lib
@@ -218,8 +263,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
218
263
  - !ruby/object:Gem::Version
219
264
  version: '0'
220
265
  requirements: []
221
- rubygems_version: 3.1.3
222
- signing_key:
266
+ rubygems_version: 3.6.9
223
267
  specification_version: 4
224
268
  summary: Better distance_of_time_in_words for Rails
225
269
  test_files:
@@ -227,6 +271,7 @@ test_files:
227
271
  - spec/lib/i18n/ar.yml
228
272
  - spec/lib/i18n/da.yml
229
273
  - spec/lib/i18n/de.yml
274
+ - spec/lib/i18n/dz.yml
230
275
  - spec/lib/i18n/en.yml
231
276
  - spec/lib/i18n/es.yml
232
277
  - spec/lib/i18n/fr.yml