gitlab_chronic_duration 0.10.6.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2a08e26fec03acc92f7690fe833452920d67d6c28bda9b5d106e020fc3fef390
4
+ data.tar.gz: bcd9abbbb3e510283748ebc7566705c32cd97f18f0c052b92f3b262fcccf7d94
5
+ SHA512:
6
+ metadata.gz: c86d746140760a7785907f2e19d263bbfe53bbc2a10987246990035a0f391ec2995f534f9ab7c377ca6dccea41178063b600e65e54e0e26831c1bfd6141d60ea
7
+ data.tar.gz: 60f11fe463617d2853a37a3268af643e28785aad1d689f78b49ae718fc7d1db1e35cf230e240e18fbe367e571b58a51ad2b05a34e90adb8dcc89efe8f6dd27f7
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,12 @@
1
+ image: "ruby:2.5"
2
+
3
+ before_script:
4
+ - ruby -v
5
+ - which ruby
6
+ - gem install bundler
7
+ - bundle install --jobs $(nproc) "${FLAGS[@]}"
8
+
9
+ rspec:
10
+ stage: test
11
+ script:
12
+ - bundle exec rspec
@@ -0,0 +1,4 @@
1
+ ## v0.10.6.1
2
+
3
+ - Allow to pass `days_per_month`, `hours_per_day` as part of `opts={}` for `.parse` and `.output`. For internal calculations, use `days_per_month` to calculate `days_per_week`. Replace `days_per_week` getter/setter with `days_per_month` !2
4
+ - Configure GitLab CI !1
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) Henry Poydar
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,81 @@
1
+ [![Build Status](https://travis-ci.org/hpoydar/chronic_duration.png?branch=master)](https://travis-ci.org/hpoydar/chronic_duration)
2
+
3
+ # Chronic Duration
4
+
5
+ A simple Ruby natural language parser for elapsed time. (For example, 4 hours and 30 minutes, 6 minutes 4 seconds, 3 days, etc.) Returns all results in seconds. Will return an integer unless you get tricky and need a float. (4 minutes and 13.47 seconds, for example.)
6
+
7
+ The reverse can also be accomplished with the output method. So pass in seconds and you can get strings like 4 mins 31.51 secs (default format), 4h 3m 30s, or 4:01:29.
8
+
9
+ ## Usage
10
+
11
+ >> require 'chronic_duration'
12
+ => true
13
+ >> ChronicDuration.parse('4 minutes and 30 seconds')
14
+ => 270
15
+ >> ChronicDuration.parse('0 seconds')
16
+ => nil
17
+ >> ChronicDuration.parse('0 seconds', :keep_zero => true)
18
+ => 0
19
+ >> ChronicDuration.output(270)
20
+ => 4 mins 30 secs
21
+ >> ChronicDuration.output(0)
22
+ => nil
23
+ >> ChronicDuration.output(0, :keep_zero => true)
24
+ => 0 secs
25
+ >> ChronicDuration.output(270, :format => :short)
26
+ => 4m 30s
27
+ >> ChronicDuration.output(270, :format => :long)
28
+ => 4 minutes 30 seconds
29
+ >> ChronicDuration.output(270, :format => :chrono)
30
+ => 4:30
31
+ >> ChronicDuration.output(1299600, :weeks => true)
32
+ => 2 wks 1 day 1 hr
33
+ >> ChronicDuration.output(1299600, :weeks => true, :units => 2)
34
+ => 2 wks 1 day
35
+ >> ChronicDuration.output(45*24*60*60 + 15*60, :limit_to_hours => true)
36
+ => 1080 hrs 15 mins
37
+ >> ChronicDuration.output(1299600, :weeks => true, :units => 2, :joiner => ', ')
38
+ => 2 wks, 1 day
39
+ >> ChronicDuration.output(1296000)
40
+ => 15 days
41
+
42
+ Nil is returned if the string can't be parsed
43
+
44
+ Examples of parse-able strings:
45
+
46
+ * '12.4 secs'
47
+ * '1:20'
48
+ * '1:20.51'
49
+ * '4:01:01'
50
+ * '3 mins 4 sec'
51
+ * '2 hrs 20 min'
52
+ * '2h20min'
53
+ * '6 mos 1 day'
54
+ * '47 yrs 6 mos and 4d'
55
+ * 'two hours and twenty minutes'
56
+ * '3 weeks and 2 days'
57
+
58
+ ChronicDuration.raise_exceptions can be set to true to raise exceptions when the string can't be parsed.
59
+
60
+ >> ChronicDuration.raise_exceptions = true
61
+ => true
62
+ >> ChronicDuration.parse('4 elephants and 3 Astroids')
63
+ ChronicDuration::DurationParseError: An invalid word "elephants" was used in the string to be parsed.
64
+
65
+ ## Contributing
66
+
67
+ Fork and pull request after your specs are green. Add your handle to the list below.
68
+ Also looking for additional maintainers.
69
+
70
+ ## Contributors
71
+
72
+ errm,pdf, brianjlandau, jduff, olauzon, roboman, ianlevesque, bolandrm
73
+
74
+ ## TODO
75
+
76
+ * Benchmark, optimize
77
+ * Context specific matching (E.g., for '4m30s', assume 'm' is minutes not months)
78
+ * Smartly parse vacation-like durations (E.g., '4 days and 3 nights')
79
+ * :chrono output option should probably change to something like 4 days 4:00:12 instead of 4:04:00:12
80
+ * Other locale support
81
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+ task :default => :spec
6
+
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'chronic_duration/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+
8
+ gem.name = "gitlab_chronic_duration"
9
+ gem.version = ChronicDuration::VERSION
10
+ gem.authors = ["hpoydar"]
11
+ gem.email = ["henry@poydar.com"]
12
+ gem.description = %q{A simple Ruby natural language parser for elapsed time. (For example, 4 hours and 30 minutes, 6 minutes 4 seconds, 3 days, etc.) Returns all results in seconds. Will return an integer unless you get tricky and need a float. (4 minutes and 13.47 seconds, for example.) The reverse can also be performed via the output method.}
13
+ gem.summary = %q{A simple Ruby natural language parser for elapsed time}
14
+ gem.homepage = "https://gitlab.com/gitlab-org/gitlab-chronic-duration"
15
+ gem.license = "MIT"
16
+
17
+ gem.files = `git ls-files`.split($/)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
+ gem.require_paths = ["lib"]
21
+
22
+ gem.add_runtime_dependency "numerizer", "~> 0.1.1"
23
+
24
+ gem.add_development_dependency "rake", "~> 10.0.3"
25
+ gem.add_development_dependency "rspec", "~> 2.12.0"
26
+
27
+
28
+ end
@@ -0,0 +1,5 @@
1
+ module ChronicDuration
2
+ # '0.10.6' is the versioning used in 'https://github.com/henrypoydar/chronic_duration',
3
+ # we are adding revision part to highlight GitLab changes, e.g. '0.10.6.N'
4
+ VERSION = '0.10.6.1'.freeze
5
+ end
@@ -0,0 +1,302 @@
1
+ require 'numerizer' unless defined?(Numerizer)
2
+
3
+ module ChronicDuration
4
+
5
+ extend self
6
+
7
+ class DurationParseError < StandardError
8
+ end
9
+
10
+ # On average, there's a little over 4 weeks in month.
11
+ FULL_WEEKS_PER_MONTH = 4
12
+
13
+ @@raise_exceptions = false
14
+ @@hours_per_day = 24
15
+ @@days_per_month = 30
16
+
17
+ def self.raise_exceptions
18
+ !!@@raise_exceptions
19
+ end
20
+
21
+ def self.raise_exceptions=(value)
22
+ @@raise_exceptions = !!value
23
+ end
24
+
25
+ def self.hours_per_day
26
+ @@hours_per_day
27
+ end
28
+
29
+ def self.hours_per_day=(value)
30
+ @@hours_per_day = value
31
+ end
32
+
33
+ def self.days_per_month
34
+ @@days_per_month
35
+ end
36
+
37
+ def self.days_per_month=(value)
38
+ @@days_per_month = value
39
+ end
40
+
41
+ # Given a string representation of elapsed time,
42
+ # return an integer (or float, if fractions of a
43
+ # second are input)
44
+ def parse(string, opts = {})
45
+ result = calculate_from_words(cleanup(string), opts)
46
+ (!opts[:keep_zero] and result == 0) ? nil : result
47
+ end
48
+
49
+ # Given an integer and an optional format,
50
+ # returns a formatted string representing elapsed time
51
+ def output(seconds, opts = {})
52
+ int = seconds.to_i
53
+ seconds = int if seconds - int == 0 # if seconds end with .0
54
+
55
+ opts[:format] ||= :default
56
+ opts[:keep_zero] ||= false
57
+
58
+ hours_per_day = opts[:hours_per_day] || ChronicDuration.hours_per_day
59
+ days_per_month = opts[:days_per_month] || ChronicDuration.days_per_month
60
+ days_per_week = days_per_month / FULL_WEEKS_PER_MONTH
61
+
62
+ years = months = weeks = days = hours = minutes = 0
63
+
64
+ decimal_places = seconds.to_s.split('.').last.length if seconds.is_a?(Float)
65
+
66
+ minute = 60
67
+ hour = 60 * minute
68
+ day = hours_per_day * hour
69
+ month = days_per_month * day
70
+ year = 31557600
71
+
72
+ if seconds >= 31557600 && seconds%year < seconds%month
73
+ years = seconds / year
74
+ months = seconds % year / month
75
+ days = seconds % year % month / day
76
+ hours = seconds % year % month % day / hour
77
+ minutes = seconds % year % month % day % hour / minute
78
+ seconds = seconds % year % month % day % hour % minute
79
+ elsif seconds >= 60
80
+ minutes = (seconds / 60).to_i
81
+ seconds = seconds % 60
82
+ if minutes >= 60
83
+ hours = (minutes / 60).to_i
84
+ minutes = (minutes % 60).to_i
85
+ if !opts[:limit_to_hours]
86
+ if hours >= hours_per_day
87
+ days = (hours / hours_per_day).to_i
88
+ hours = (hours % hours_per_day).to_i
89
+ if opts[:weeks]
90
+ if days >= days_per_week
91
+ weeks = (days / days_per_week).to_i
92
+ days = (days % days_per_week).to_i
93
+ if weeks >= FULL_WEEKS_PER_MONTH
94
+ months = (weeks / FULL_WEEKS_PER_MONTH).to_i
95
+ weeks = (weeks % FULL_WEEKS_PER_MONTH).to_i
96
+ end
97
+ end
98
+ else
99
+ if days >= days_per_month
100
+ months = (days / days_per_month).to_i
101
+ days = (days % days_per_month).to_i
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ joiner = opts.fetch(:joiner) { ' ' }
110
+ process = nil
111
+
112
+ case opts[:format]
113
+ when :micro
114
+ dividers = {
115
+ :years => 'y', :months => 'mo', :weeks => 'w', :days => 'd', :hours => 'h', :minutes => 'm', :seconds => 's' }
116
+ joiner = ''
117
+ when :short
118
+ dividers = {
119
+ :years => 'y', :months => 'mo', :weeks => 'w', :days => 'd', :hours => 'h', :minutes => 'm', :seconds => 's' }
120
+ when :default
121
+ dividers = {
122
+ :years => ' yr', :months => ' mo', :weeks => ' wk', :days => ' day', :hours => ' hr', :minutes => ' min', :seconds => ' sec',
123
+ :pluralize => true }
124
+ when :long
125
+ dividers = {
126
+ :years => ' year', :months => ' month', :weeks => ' week', :days => ' day', :hours => ' hour', :minutes => ' minute', :seconds => ' second',
127
+ :pluralize => true }
128
+ when :chrono
129
+ dividers = {
130
+ :years => ':', :months => ':', :weeks => ':', :days => ':', :hours => ':', :minutes => ':', :seconds => ':', :keep_zero => true }
131
+ process = lambda do |str|
132
+ # Pad zeros
133
+ # Get rid of lead off times if they are zero
134
+ # Get rid of lead off zero
135
+ # Get rid of trailing :
136
+ divider = ':'
137
+ str.split(divider).map { |n|
138
+ # add zeros only if n is an integer
139
+ n.include?('.') ? ("%04.#{decimal_places}f" % n) : ("%02d" % n)
140
+ }.join(divider).gsub(/^(00:)+/, '').gsub(/^0/, '').gsub(/:$/, '')
141
+ end
142
+ joiner = ''
143
+ end
144
+
145
+ result = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].map do |t|
146
+ next if t == :weeks && !opts[:weeks]
147
+ num = eval(t.to_s)
148
+ num = ("%.#{decimal_places}f" % num) if num.is_a?(Float) && t == :seconds
149
+ keep_zero = dividers[:keep_zero]
150
+ keep_zero ||= opts[:keep_zero] if t == :seconds
151
+ humanize_time_unit( num, dividers[t], dividers[:pluralize], keep_zero )
152
+ end.compact!
153
+
154
+ result = result[0...opts[:units]] if opts[:units]
155
+
156
+ result = result.join(joiner)
157
+
158
+ if process
159
+ result = process.call(result)
160
+ end
161
+
162
+ result.length == 0 ? nil : result
163
+
164
+ end
165
+
166
+ private
167
+
168
+ def humanize_time_unit(number, unit, pluralize, keep_zero)
169
+ return nil if number == 0 && !keep_zero
170
+ res = "#{number}#{unit}"
171
+ # A poor man's pluralizer
172
+ res << 's' if !(number == 1) && pluralize
173
+ res
174
+ end
175
+
176
+ def calculate_from_words(string, opts)
177
+ val = 0
178
+ words = string.split(' ')
179
+ words.each_with_index do |v, k|
180
+ if v =~ float_matcher
181
+ val += (convert_to_number(v) * duration_units_seconds_multiplier(words[k + 1] || (opts[:default_unit] || 'seconds'), opts))
182
+ end
183
+ end
184
+ val
185
+ end
186
+
187
+ def cleanup(string)
188
+ res = string.downcase
189
+ res = filter_by_type(Numerizer.numerize(res))
190
+ res = res.gsub(float_matcher) {|n| " #{n} "}.squeeze(' ').strip
191
+ res = filter_through_white_list(res)
192
+ end
193
+
194
+ def convert_to_number(string)
195
+ string.to_f % 1 > 0 ? string.to_f : string.to_i
196
+ end
197
+
198
+ def duration_units_list
199
+ %w(seconds minutes hours days weeks months years)
200
+ end
201
+
202
+ def duration_units_seconds_multiplier(unit, opts)
203
+ return 0 unless duration_units_list.include?(unit)
204
+
205
+ hours_per_day = opts[:hours_per_day] || ChronicDuration.hours_per_day
206
+ days_per_month = opts[:days_per_month] || ChronicDuration.days_per_month
207
+ days_per_week = days_per_month / FULL_WEEKS_PER_MONTH
208
+
209
+ case unit
210
+ when 'years'; 31557600
211
+ when 'months'; 3600 * hours_per_day * days_per_month
212
+ when 'weeks'; 3600 * hours_per_day * days_per_week
213
+ when 'days'; 3600 * hours_per_day
214
+ when 'hours'; 3600
215
+ when 'minutes'; 60
216
+ when 'seconds'; 1
217
+ end
218
+ end
219
+
220
+ # Parse 3:41:59 and return 3 hours 41 minutes 59 seconds
221
+ def filter_by_type(string)
222
+ chrono_units_list = duration_units_list.reject {|v| v == "weeks"}
223
+ if string.gsub(' ', '') =~ /#{float_matcher}(:#{float_matcher})+/
224
+ res = []
225
+ string.gsub(' ', '').split(':').reverse.each_with_index do |v,k|
226
+ return unless chrono_units_list[k]
227
+ res << "#{v} #{chrono_units_list[k]}"
228
+ end
229
+ res = res.reverse.join(' ')
230
+ else
231
+ res = string
232
+ end
233
+ res
234
+ end
235
+
236
+ def float_matcher
237
+ /[0-9]*\.?[0-9]+/
238
+ end
239
+
240
+ # Get rid of unknown words and map found
241
+ # words to defined time units
242
+ def filter_through_white_list(string)
243
+ res = []
244
+ string.split(' ').each do |word|
245
+ if word =~ float_matcher
246
+ res << word.strip
247
+ next
248
+ end
249
+ stripped_word = word.strip.gsub(/^,/, '').gsub(/,$/, '')
250
+ if mappings.has_key?(stripped_word)
251
+ res << mappings[stripped_word]
252
+ elsif !join_words.include?(stripped_word) and ChronicDuration.raise_exceptions
253
+ raise DurationParseError, "An invalid word #{word.inspect} was used in the string to be parsed."
254
+ end
255
+ end
256
+ # add '1' at front if string starts with something recognizable but not with a number, like 'day' or 'minute 30sec'
257
+ res.unshift(1) if res.length > 0 && mappings[res[0]]
258
+ res.join(' ')
259
+ end
260
+
261
+ def mappings
262
+ {
263
+ 'seconds' => 'seconds',
264
+ 'second' => 'seconds',
265
+ 'secs' => 'seconds',
266
+ 'sec' => 'seconds',
267
+ 's' => 'seconds',
268
+ 'minutes' => 'minutes',
269
+ 'minute' => 'minutes',
270
+ 'mins' => 'minutes',
271
+ 'min' => 'minutes',
272
+ 'm' => 'minutes',
273
+ 'hours' => 'hours',
274
+ 'hour' => 'hours',
275
+ 'hrs' => 'hours',
276
+ 'hr' => 'hours',
277
+ 'h' => 'hours',
278
+ 'days' => 'days',
279
+ 'day' => 'days',
280
+ 'dy' => 'days',
281
+ 'd' => 'days',
282
+ 'weeks' => 'weeks',
283
+ 'week' => 'weeks',
284
+ 'wks' => 'weeks',
285
+ 'wk' => 'weeks',
286
+ 'w' => 'weeks',
287
+ 'months' => 'months',
288
+ 'mo' => 'months',
289
+ 'mos' => 'months',
290
+ 'month' => 'months',
291
+ 'years' => 'years',
292
+ 'year' => 'years',
293
+ 'yrs' => 'years',
294
+ 'yr' => 'years',
295
+ 'y' => 'years'
296
+ }
297
+ end
298
+
299
+ def join_words
300
+ ['and', 'with', 'plus']
301
+ end
302
+ end
@@ -0,0 +1,331 @@
1
+ require 'spec_helper'
2
+
3
+ describe ChronicDuration do
4
+
5
+ describe ".parse" do
6
+
7
+ @exemplars = {
8
+ '1:20' => 60 + 20,
9
+ '1:20.51' => 60 + 20.51,
10
+ '4:01:01' => 4 * 3600 + 60 + 1,
11
+ '3 mins 4 sec' => 3 * 60 + 4,
12
+ '3 Mins 4 Sec' => 3 * 60 + 4,
13
+ 'three mins four sec' => 3 * 60 + 4,
14
+ '2 hrs 20 min' => 2 * 3600 + 20 * 60,
15
+ '2h20min' => 2 * 3600 + 20 * 60,
16
+ '6 mos 1 day' => 6 * 30 * 24 * 3600 + 24 * 3600,
17
+ '1 year 6 mos 1 day' => 1 * 31557600 + 6 * 30 * 24 * 3600 + 24 * 3600,
18
+ '2.5 hrs' => 2.5 * 3600,
19
+ '47 yrs 6 mos and 4.5d' => 47 * 31557600 + 6 * 30 * 24 * 3600 + 4.5 * 24 * 3600,
20
+ 'two hours and twenty minutes' => 2 * 3600 + 20 * 60,
21
+ 'four hours and forty minutes' => 4 * 3600 + 40 * 60,
22
+ 'four hours, and fourty minutes' => 4 * 3600 + 40 * 60,
23
+ '3 weeks and, 2 days' => 3600 * 24 * 7 * 3 + 3600 * 24 * 2,
24
+ '3 weeks, plus 2 days' => 3600 * 24 * 7 * 3 + 3600 * 24 * 2,
25
+ '3 weeks with 2 days' => 3600 * 24 * 7 * 3 + 3600 * 24 * 2,
26
+ '1 month' => 3600 * 24 * 30,
27
+ '2 months' => 3600 * 24 * 30 * 2,
28
+ '18 months' => 3600 * 24 * 30 * 18,
29
+ '1 year 6 months' => (3600 * 24 * (365.25 + 6 * 30)).to_i,
30
+ 'day' => 3600 * 24,
31
+ 'minute 30s' => 90
32
+ }
33
+
34
+ context "when string can't be parsed" do
35
+
36
+ it "returns nil" do
37
+ ChronicDuration.parse('gobblygoo').should be_nil
38
+ end
39
+
40
+ it "cannot parse zero" do
41
+ ChronicDuration.parse('0').should be_nil
42
+ end
43
+
44
+ context "when @@raise_exceptions set to true" do
45
+
46
+ it "raises with ChronicDuration::DurationParseError" do
47
+ ChronicDuration.raise_exceptions = true
48
+ expect { ChronicDuration.parse('23 gobblygoos') }.to raise_error(ChronicDuration::DurationParseError)
49
+ ChronicDuration.raise_exceptions = false
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
56
+ it "should return zero if the string parses as zero and the keep_zero option is true" do
57
+ ChronicDuration.parse('0', :keep_zero => true).should == 0
58
+ end
59
+
60
+ it "should return a float if seconds are in decimals" do
61
+ ChronicDuration.parse('12 mins 3.141 seconds').is_a?(Float).should be_true
62
+ end
63
+
64
+ it "should return an integer unless the seconds are in decimals" do
65
+ ChronicDuration.parse('12 mins 3 seconds').is_a?(Integer).should be_true
66
+ end
67
+
68
+ it "should be able to parse minutes by default" do
69
+ ChronicDuration.parse('5', :default_unit => "minutes").should == 300
70
+ end
71
+
72
+ @exemplars.each do |k, v|
73
+ it "parses a duration like #{k}" do
74
+ ChronicDuration.parse(k).should == v
75
+ end
76
+ end
77
+
78
+ context 'with :hours_per_day and :days_per_month params' do
79
+ it 'uses provided :hours_per_day' do
80
+ ChronicDuration.parse('1d', hours_per_day: 24).should == 24 * 60 * 60
81
+ ChronicDuration.parse('1d', hours_per_day: 8).should == 8 * 60 * 60
82
+ end
83
+
84
+ it 'uses provided :days_per_month' do
85
+ ChronicDuration.parse('1mo', days_per_month: 30).should == 30 * 24 * 60 * 60
86
+ ChronicDuration.parse('1mo', days_per_month: 20).should == 20 * 24 * 60 * 60
87
+
88
+ ChronicDuration.parse('1w', days_per_month: 30).should == 7 * 24 * 60 * 60
89
+ ChronicDuration.parse('1w', days_per_month: 20).should == 5 * 24 * 60 * 60
90
+ end
91
+
92
+ it 'uses provided both :hours_per_day and :days_per_month' do
93
+ ChronicDuration.parse('1mo', days_per_month: 30, hours_per_day: 24).should == 30 * 24 * 60 * 60
94
+ ChronicDuration.parse('1mo', days_per_month: 20, hours_per_day: 8).should == 20 * 8 * 60 * 60
95
+
96
+ ChronicDuration.parse('1w', days_per_month: 30, hours_per_day: 24).should == 7 * 24 * 60 * 60
97
+ ChronicDuration.parse('1w', days_per_month: 20, hours_per_day: 8).should == 5 * 8 * 60 * 60
98
+ end
99
+ end
100
+ end
101
+
102
+ describe '.output' do
103
+
104
+ @exemplars = {
105
+ (60 + 20) =>
106
+ {
107
+ :micro => '1m20s',
108
+ :short => '1m 20s',
109
+ :default => '1 min 20 secs',
110
+ :long => '1 minute 20 seconds',
111
+ :chrono => '1:20'
112
+ },
113
+ (60 + 20.51) =>
114
+ {
115
+ :micro => '1m20.51s',
116
+ :short => '1m 20.51s',
117
+ :default => '1 min 20.51 secs',
118
+ :long => '1 minute 20.51 seconds',
119
+ :chrono => '1:20.51'
120
+ },
121
+ (60 + 20.51928) =>
122
+ {
123
+ :micro => '1m20.51928s',
124
+ :short => '1m 20.51928s',
125
+ :default => '1 min 20.51928 secs',
126
+ :long => '1 minute 20.51928 seconds',
127
+ :chrono => '1:20.51928'
128
+ },
129
+ (4 * 3600 + 60 + 1) =>
130
+ {
131
+ :micro => '4h1m1s',
132
+ :short => '4h 1m 1s',
133
+ :default => '4 hrs 1 min 1 sec',
134
+ :long => '4 hours 1 minute 1 second',
135
+ :chrono => '4:01:01'
136
+ },
137
+ (2 * 3600 + 20 * 60) =>
138
+ {
139
+ :micro => '2h20m',
140
+ :short => '2h 20m',
141
+ :default => '2 hrs 20 mins',
142
+ :long => '2 hours 20 minutes',
143
+ :chrono => '2:20'
144
+ },
145
+ (2 * 3600 + 20 * 60) =>
146
+ {
147
+ :micro => '2h20m',
148
+ :short => '2h 20m',
149
+ :default => '2 hrs 20 mins',
150
+ :long => '2 hours 20 minutes',
151
+ :chrono => '2:20:00'
152
+ },
153
+ (6 * 30 * 24 * 3600 + 24 * 3600) =>
154
+ {
155
+ :micro => '6mo1d',
156
+ :short => '6mo 1d',
157
+ :default => '6 mos 1 day',
158
+ :long => '6 months 1 day',
159
+ :chrono => '6:01:00:00:00' # Yuck. FIXME
160
+ },
161
+ (365.25 * 24 * 3600 + 24 * 3600 ).to_i =>
162
+ {
163
+ :micro => '1y1d',
164
+ :short => '1y 1d',
165
+ :default => '1 yr 1 day',
166
+ :long => '1 year 1 day',
167
+ :chrono => '1:00:01:00:00:00'
168
+ },
169
+ (3 * 365.25 * 24 * 3600 + 24 * 3600 ).to_i =>
170
+ {
171
+ :micro => '3y1d',
172
+ :short => '3y 1d',
173
+ :default => '3 yrs 1 day',
174
+ :long => '3 years 1 day',
175
+ :chrono => '3:00:01:00:00:00'
176
+ },
177
+ (3600 * 24 * 30 * 18) =>
178
+ {
179
+ :micro => '18mo',
180
+ :short => '18mo',
181
+ :default => '18 mos',
182
+ :long => '18 months',
183
+ :chrono => '18:00:00:00:00'
184
+ }
185
+ }
186
+
187
+ @exemplars.each do |k, v|
188
+ v.each do |key, val|
189
+ it "properly outputs a duration of #{k} seconds as #{val} using the #{key.to_s} format option" do
190
+ ChronicDuration.output(k, :format => key).should == val
191
+ end
192
+ end
193
+ end
194
+
195
+ @keep_zero_exemplars = {
196
+ (true) =>
197
+ {
198
+ :micro => '0s',
199
+ :short => '0s',
200
+ :default => '0 secs',
201
+ :long => '0 seconds',
202
+ :chrono => '0'
203
+ },
204
+ (false) =>
205
+ {
206
+ :micro => nil,
207
+ :short => nil,
208
+ :default => nil,
209
+ :long => nil,
210
+ :chrono => '0'
211
+ },
212
+ }
213
+
214
+ @keep_zero_exemplars.each do |k, v|
215
+ v.each do |key, val|
216
+ it "should properly output a duration of 0 seconds as #{val.nil? ? "nil" : val} using the #{key.to_s} format option, if the keep_zero option is #{k.to_s}" do
217
+ ChronicDuration.output(0, :format => key, :keep_zero => k).should == val
218
+ end
219
+ end
220
+ end
221
+
222
+ it "returns weeks when needed" do
223
+ ChronicDuration.output(45*24*60*60, :weeks => true).should =~ /.*wk.*/
224
+ end
225
+
226
+ it "returns hours and minutes only when :hours_only option specified" do
227
+ ChronicDuration.output(395*24*60*60 + 15*60, :limit_to_hours => true).should == '9480 hrs 15 mins'
228
+ end
229
+
230
+ context 'with :hours_per_day and :days_per_month params' do
231
+ it 'uses provided :hours_per_day' do
232
+ ChronicDuration.output(24 * 60 * 60, hours_per_day: 24).should == '1 day'
233
+ ChronicDuration.output(24 * 60 * 60, hours_per_day: 8).should == '3 days'
234
+ end
235
+
236
+ it 'uses provided :days_per_month' do
237
+ ChronicDuration.output(7 * 24 * 60 * 60, weeks: true, days_per_month: 30).should == '1 wk'
238
+ ChronicDuration.output(7 * 24 * 60 * 60, weeks: true, days_per_month: 20).should == '1 wk 2 days'
239
+ end
240
+
241
+ it 'uses provided both :hours_per_day and :days_per_month' do
242
+ ChronicDuration.output(7 * 24 * 60 * 60, weeks: true, days_per_month: 30, hours_per_day: 24).should == '1 wk'
243
+ ChronicDuration.output(5 * 8 * 60 * 60, weeks: true, days_per_month: 20, hours_per_day: 8).should == '1 wk'
244
+ end
245
+
246
+ it 'uses provided params alonside with :weeks when converting to months' do
247
+ ChronicDuration.output(30 * 24 * 60 * 60, days_per_month: 30, hours_per_day: 24).should == '1 mo'
248
+ ChronicDuration.output(30 * 24 * 60 * 60, days_per_month: 30, hours_per_day: 24, weeks: true).should == '1 mo 2 days'
249
+
250
+ ChronicDuration.output(20 * 8 * 60 * 60, days_per_month: 20, hours_per_day: 8).should == '1 mo'
251
+ ChronicDuration.output(20 * 8 * 60 * 60, days_per_month: 20, hours_per_day: 8, weeks: true).should == '1 mo'
252
+ end
253
+ end
254
+
255
+ it "returns the specified number of units if provided" do
256
+ ChronicDuration.output(4 * 3600 + 60 + 1, units: 2).should == '4 hrs 1 min'
257
+ ChronicDuration.output(6 * 30 * 24 * 3600 + 24 * 3600 + 3600 + 60 + 1, units: 3, format: :long).should == '6 months 1 day 1 hour'
258
+ end
259
+
260
+ context "when the format is not specified" do
261
+
262
+ it "uses the default format" do
263
+ ChronicDuration.output(2 * 3600 + 20 * 60).should == '2 hrs 20 mins'
264
+ end
265
+
266
+ end
267
+
268
+ @exemplars.each do |seconds, format_spec|
269
+ format_spec.each do |format, _|
270
+ it "outputs a duration for #{seconds} that parses back to the same thing when using the #{format.to_s} format" do
271
+ ChronicDuration.parse(ChronicDuration.output(seconds, :format => format)).should == seconds
272
+ end
273
+ end
274
+ end
275
+
276
+ it "uses user-specified joiner if provided" do
277
+ ChronicDuration.output(2 * 3600 + 20 * 60, joiner: ', ').should == '2 hrs, 20 mins'
278
+ end
279
+
280
+ end
281
+
282
+ describe ".filter_by_type" do
283
+
284
+ it "receives a chrono-formatted time like 3:14 and return a human time like 3 minutes 14 seconds" do
285
+ ChronicDuration.instance_eval("filter_by_type('3:14')").should == '3 minutes 14 seconds'
286
+ end
287
+
288
+ it "receives chrono-formatted time like 12:10:14 and return a human time like 12 hours 10 minutes 14 seconds" do
289
+ ChronicDuration.instance_eval("filter_by_type('12:10:14')").should == '12 hours 10 minutes 14 seconds'
290
+ end
291
+
292
+ it "returns the input if it's not a chrono-formatted time" do
293
+ ChronicDuration.instance_eval("filter_by_type('4 hours')").should == '4 hours'
294
+ end
295
+
296
+ end
297
+
298
+ describe ".cleanup" do
299
+
300
+ it "cleans up extraneous words" do
301
+ ChronicDuration.instance_eval("cleanup('4 days and 11 hours')").should == '4 days 11 hours'
302
+ end
303
+
304
+ it "cleans up extraneous spaces" do
305
+ ChronicDuration.instance_eval("cleanup(' 4 days and 11 hours')").should == '4 days 11 hours'
306
+ end
307
+
308
+ it "inserts spaces where there aren't any" do
309
+ ChronicDuration.instance_eval("cleanup('4m11.5s')").should == '4 minutes 11.5 seconds'
310
+ end
311
+
312
+ end
313
+
314
+ describe "work week" do
315
+ before(:all) do
316
+ ChronicDuration.hours_per_day = 8
317
+ ChronicDuration.days_per_month = 20
318
+ end
319
+
320
+ after(:all) do
321
+ ChronicDuration.hours_per_day = 24
322
+ ChronicDuration.days_per_month = 30
323
+ end
324
+
325
+ it "should parse knowing the work week" do
326
+ week = ChronicDuration.parse('5d')
327
+ ChronicDuration.parse('40h').should == week
328
+ ChronicDuration.parse('1w').should == week
329
+ end
330
+ end
331
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'gitlab_chronic_duration'
4
+
5
+ RSpec.configure do |config|
6
+ end
7
+
8
+
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gitlab_chronic_duration
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.10.6.1
5
+ platform: ruby
6
+ authors:
7
+ - hpoydar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-09-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: numerizer
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 10.0.3
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 10.0.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 2.12.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 2.12.0
55
+ description: A simple Ruby natural language parser for elapsed time. (For example,
56
+ 4 hours and 30 minutes, 6 minutes 4 seconds, 3 days, etc.) Returns all results in
57
+ seconds. Will return an integer unless you get tricky and need a float. (4 minutes
58
+ and 13.47 seconds, for example.) The reverse can also be performed via the output
59
+ method.
60
+ email:
61
+ - henry@poydar.com
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - ".gitignore"
67
+ - ".gitlab-ci.yml"
68
+ - CHANGELOG.md
69
+ - Gemfile
70
+ - LICENSE.txt
71
+ - README.md
72
+ - Rakefile
73
+ - gitlab_chronic_duration.gemspec
74
+ - lib/chronic_duration/version.rb
75
+ - lib/gitlab_chronic_duration.rb
76
+ - spec/lib/chronic_duration_spec.rb
77
+ - spec/spec_helper.rb
78
+ homepage: https://gitlab.com/gitlab-org/gitlab-chronic-duration
79
+ licenses:
80
+ - MIT
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubygems_version: 3.0.3
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: A simple Ruby natural language parser for elapsed time
101
+ test_files:
102
+ - spec/lib/chronic_duration_spec.rb
103
+ - spec/spec_helper.rb