chronic_duration 0.9.6 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -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
data/Gemfile CHANGED
@@ -1,10 +1,2 @@
1
1
  source "http://rubygems.org"
2
-
3
- gem "numerizer", "~> 0.1.1"
4
-
5
- group :development do
6
- gem "rspec", "~> 2.3.0"
7
- gem "bundler", "~> 1.0.0"
8
- gem "jeweler", "~> 1.5.2"
9
- gem "rcov", ">= 0"
10
- end
2
+ gemspec
@@ -1,15 +1,10 @@
1
- = Chronic Duration
1
+ # Chronic Duration
2
2
 
3
- 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.)
3
+ 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.)
4
4
 
5
- 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.
5
+ 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.
6
6
 
7
- == Installation
8
-
9
- $ sudo gem sources -a http://gemcutter.org
10
- $ sudo gem install chronic_duration
11
-
12
- == Usage
7
+ ## Usage
13
8
 
14
9
  >> require 'chronic_duration'
15
10
  => true
@@ -23,13 +18,19 @@ The reverse can also be accomplished with the output method. So pass in seconds
23
18
  => 4 minutes 30 seconds
24
19
  >> ChronicDuration.output(270, :format => :chrono)
25
20
  => 4:30
26
-
21
+ >> ChronicDuration.output(1299600, :weeks => true)
22
+ => 2 wks 1 day 1 hr
23
+ >> ChronicDuration.output(1299600, :weeks => true, :units => 2)
24
+ => 2 wks 1 day
25
+ >> ChronicDuration.output(1296000)
26
+ => 15 days
27
+
27
28
  Nil is returned if the string can't be parsed
28
29
 
29
30
  Examples of parse-able strings:
30
31
 
31
32
  * '12.4 secs'
32
- * '1:20'
33
+ * '1:20'
33
34
  * '1:20.51'
34
35
  * '4:01:01'
35
36
  * '3 mins 4 sec'
@@ -47,12 +48,12 @@ ChronicDuration.raise_exceptions can be set to true to raise exceptions when the
47
48
  >> ChronicDuration.parse('4 elephants and 3 Astroids')
48
49
  ChronicDuration::DurationParseError: An invalid word "elephants" was used in the string to be parsed.
49
50
 
50
-
51
- == Contributors
51
+
52
+ ## Contributors
52
53
 
53
54
  brianjlandau, jduff, olauzon, roboman, ianlevesque
54
-
55
- == TODO
55
+
56
+ ## TODO
56
57
 
57
58
  * Benchmark, optimize
58
59
  * Context specific matching (E.g., for '4m30s', assume 'm' is minutes not months)
data/Rakefile CHANGED
@@ -1,45 +1,6 @@
1
- require 'rubygems'
2
- require 'bundler'
3
- begin
4
- Bundler.setup(:default, :development)
5
- rescue Bundler::BundlerError => e
6
- $stderr.puts e.message
7
- $stderr.puts "Run `bundle install` to install missing gems"
8
- exit e.status_code
9
- end
10
- require 'rake'
11
-
12
- require 'jeweler'
13
- Jeweler::Tasks.new do |gem|
14
- gem.name = "chronic_duration"
15
- gem.summary = %Q{A Ruby natural language parser for elapsed time}
16
- gem.license = "MIT"
17
- 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.}
18
- gem.email = "hpoydar@gmail.com"
19
- gem.homepage = "http://github.com/hpoydar/chronic_duration"
20
- gem.authors = ["hpoydar"]
21
- end
22
- Jeweler::RubygemsDotOrgTasks.new
23
-
24
- require 'rspec/core'
1
+ require "bundler/gem_tasks"
25
2
  require 'rspec/core/rake_task'
26
- RSpec::Core::RakeTask.new(:spec) do |spec|
27
- spec.pattern = FileList['spec/**/*_spec.rb']
28
- end
29
-
30
- RSpec::Core::RakeTask.new(:rcov) do |spec|
31
- spec.pattern = 'spec/**/*_spec.rb'
32
- spec.rcov = true
33
- end
34
3
 
4
+ RSpec::Core::RakeTask.new('spec')
35
5
  task :default => :spec
36
6
 
37
- require 'rake/rdoctask'
38
- Rake::RDocTask.new do |rdoc|
39
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
-
41
- rdoc.rdoc_dir = 'rdoc'
42
- rdoc.title = "chronic_duration #{version}"
43
- rdoc.rdoc_files.include('README*')
44
- rdoc.rdoc_files.include('lib/**/*.rb')
45
- end
@@ -1,62 +1,26 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
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
5
 
6
- Gem::Specification.new do |s|
7
- s.name = %q{chronic_duration}
8
- s.version = "0.9.6"
6
+ Gem::Specification.new do |gem|
9
7
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["hpoydar"]
12
- s.date = %q{2011-06-04}
13
- s.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.}
14
- s.email = %q{hpoydar@gmail.com}
15
- s.extra_rdoc_files = [
16
- "README.rdoc"
17
- ]
18
- s.files = [
19
- "Gemfile",
20
- "Gemfile.lock",
21
- "MIT-LICENSE",
22
- "README.rdoc",
23
- "Rakefile",
24
- "VERSION",
25
- "chronic_duration.gemspec",
26
- "lib/chronic_duration.rb",
27
- "spec/chronic_duration_spec.rb"
28
- ]
29
- s.homepage = %q{http://github.com/hpoydar/chronic_duration}
30
- s.licenses = ["MIT"]
31
- s.require_paths = ["lib"]
32
- s.rubygems_version = %q{1.5.2}
33
- s.summary = %q{A Ruby natural language parser for elapsed time}
34
- s.test_files = [
35
- "spec/chronic_duration_spec.rb"
36
- ]
8
+ gem.name = "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://github.com/hpoydar/chronic_duration"
37
15
 
38
- if s.respond_to? :specification_version then
39
- s.specification_version = 3
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
40
20
 
41
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
42
- s.add_runtime_dependency(%q<numerizer>, ["~> 0.1.1"])
43
- s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
44
- s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
45
- s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
46
- s.add_development_dependency(%q<rcov>, [">= 0"])
47
- else
48
- s.add_dependency(%q<numerizer>, ["~> 0.1.1"])
49
- s.add_dependency(%q<rspec>, ["~> 2.3.0"])
50
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
51
- s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
52
- s.add_dependency(%q<rcov>, [">= 0"])
53
- end
54
- else
55
- s.add_dependency(%q<numerizer>, ["~> 0.1.1"])
56
- s.add_dependency(%q<rspec>, ["~> 2.3.0"])
57
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
58
- s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
59
- s.add_dependency(%q<rcov>, [">= 0"])
60
- end
61
- end
21
+ gem.add_runtime_dependency "numerizer", "~> 0.1.1"
22
+
23
+ gem.add_development_dependency "rake", "~> 10.0.3"
24
+ gem.add_development_dependency "rspec", "~> 2.12.0"
62
25
 
26
+ end
@@ -1,40 +1,42 @@
1
1
  require 'numerizer' unless defined?(Numerizer)
2
+
2
3
  module ChronicDuration
4
+
3
5
  extend self
4
-
6
+
5
7
  class DurationParseError < StandardError
6
8
  end
7
-
9
+
8
10
  @@raise_exceptions = false
9
-
11
+
10
12
  def self.raise_exceptions
11
13
  !!@@raise_exceptions
12
14
  end
13
-
15
+
14
16
  def self.raise_exceptions=(value)
15
17
  @@raise_exceptions = !!value
16
18
  end
17
-
19
+
18
20
  # Given a string representation of elapsed time,
19
21
  # return an integer (or float, if fractions of a
20
22
  # second are input)
21
23
  def parse(string, opts = {})
22
24
  result = calculate_from_words(cleanup(string), opts)
23
25
  result == 0 ? nil : result
24
- end
25
-
26
+ end
27
+
26
28
  # Given an integer and an optional format,
27
29
  # returns a formatted string representing elapsed time
28
30
  def output(seconds, opts = {})
29
-
31
+
30
32
  opts[:format] ||= :default
31
-
32
- years = months = days = hours = minutes = 0
33
-
33
+
34
+ years = months = weeks = days = hours = minutes = 0
35
+
34
36
  decimal_places = seconds.to_s.split('.').last.length if seconds.is_a?(Float)
35
37
 
36
38
  if seconds >= 60
37
- minutes = (seconds / 60).to_i
39
+ minutes = (seconds / 60).to_i
38
40
  seconds = seconds % 60
39
41
  if minutes >= 60
40
42
  hours = (minutes / 60).to_i
@@ -42,40 +44,52 @@ module ChronicDuration
42
44
  if hours >= 24
43
45
  days = (hours / 24).to_i
44
46
  hours = (hours % 24).to_i
45
- if days >= 30
46
- months = (days / 30).to_i
47
- days = (days % 30).to_i
48
- if months >= 12
49
- years = (months / 12).to_i
50
- months = (months % 12).to_i
47
+ if opts[:weeks]
48
+ if days >= 7
49
+ weeks = (days / 7).to_i
50
+ days = (days % 7).to_i
51
+ if weeks >= 4
52
+ months = (weeks / 4).to_i
53
+ weeks = (weeks % 4).to_i
54
+ end
55
+ end
56
+ else
57
+ if days >= 30
58
+ months = (days / 30).to_i
59
+ days = (days % 30).to_i
51
60
  end
52
61
  end
62
+ if months >= 12
63
+ years = (months / 12).to_i
64
+ months = (months % 12).to_i
65
+ days = days - (5 * years)
66
+ end
53
67
  end
54
68
  end
55
69
  end
56
-
70
+
57
71
  joiner = ' '
58
72
  process = nil
59
-
73
+
60
74
  case opts[:format]
61
75
  when :micro
62
- dividers = {
63
- :years => 'y', :months => 'm', :days => 'd', :hours => 'h', :minutes => 'm', :seconds => 's' }
76
+ dividers = {
77
+ :years => 'y', :months => 'mo', :weeks => 'w', :days => 'd', :hours => 'h', :minutes => 'm', :seconds => 's' }
64
78
  joiner = ''
65
79
  when :short
66
- dividers = {
67
- :years => 'y', :months => 'm', :days => 'd', :hours => 'h', :minutes => 'm', :seconds => 's' }
68
- when :default
69
80
  dividers = {
70
- :years => ' yr', :months => ' mo', :days => ' day', :hours => ' hr', :minutes => ' min', :seconds => ' sec',
81
+ :years => 'y', :months => 'mo', :weeks => 'w', :days => 'd', :hours => 'h', :minutes => 'm', :seconds => 's' }
82
+ when :default
83
+ dividers = {
84
+ :years => ' yr', :months => ' mo', :weeks => ' wk', :days => ' day', :hours => ' hr', :minutes => ' min', :seconds => ' sec',
71
85
  :pluralize => true }
72
- when :long
86
+ when :long
73
87
  dividers = {
74
- :years => ' year', :months => ' month', :days => ' day', :hours => ' hour', :minutes => ' minute', :seconds => ' second',
88
+ :years => ' year', :months => ' month', :weeks => ' week', :days => ' day', :hours => ' hour', :minutes => ' minute', :seconds => ' second',
75
89
  :pluralize => true }
76
90
  when :chrono
77
91
  dividers = {
78
- :years => ':', :months => ':', :days => ':', :hours => ':', :minutes => ':', :seconds => ':', :keep_zero => true }
92
+ :years => ':', :months => ':', :weeks => ':', :days => ':', :hours => ':', :minutes => ':', :seconds => ':', :keep_zero => true }
79
93
  process = lambda do |str|
80
94
  # Pad zeros
81
95
  # Get rid of lead off times if they are zero
@@ -85,34 +99,36 @@ module ChronicDuration
85
99
  end
86
100
  joiner = ''
87
101
  end
88
-
89
- result = []
90
- [:years, :months, :days, :hours, :minutes, :seconds].each do |t|
102
+
103
+ result = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].map do |t|
104
+ next if t == :weeks && !opts[:weeks]
91
105
  num = eval(t.to_s)
92
- num = ("%.#{decimal_places}f" % num) if num.is_a?(Float) && t == :seconds
93
- result << humanize_time_unit( num, dividers[t], dividers[:pluralize], dividers[:keep_zero] )
94
- end
106
+ num = ("%.#{decimal_places}f" % num) if num.is_a?(Float) && t == :seconds
107
+ humanize_time_unit( num, dividers[t], dividers[:pluralize], dividers[:keep_zero] )
108
+ end.compact!
109
+
110
+ result = result[0...opts[:units]] if opts[:units]
111
+
112
+ result = result.join(joiner)
95
113
 
96
- result = result.join(joiner).squeeze(' ').strip
97
-
98
114
  if process
99
115
  result = process.call(result)
100
116
  end
101
-
117
+
102
118
  result.length == 0 ? nil : result
103
119
 
104
120
  end
105
-
121
+
106
122
  private
107
-
123
+
108
124
  def humanize_time_unit(number, unit, pluralize, keep_zero)
109
- return '' if number == 0 && !keep_zero
125
+ return nil if number == 0 && !keep_zero
110
126
  res = "#{number}#{unit}"
111
127
  # A poor man's pluralizer
112
128
  res << 's' if !(number == 1) && pluralize
113
129
  res
114
130
  end
115
-
131
+
116
132
  def calculate_from_words(string, opts)
117
133
  val = 0
118
134
  words = string.split(' ')
@@ -123,18 +139,18 @@ private
123
139
  end
124
140
  val
125
141
  end
126
-
142
+
127
143
  def cleanup(string)
128
144
  res = string.downcase
129
145
  res = filter_by_type(Numerizer.numerize(res))
130
146
  res = res.gsub(float_matcher) {|n| " #{n} "}.squeeze(' ').strip
131
147
  res = filter_through_white_list(res)
132
148
  end
133
-
149
+
134
150
  def convert_to_number(string)
135
151
  string.to_f % 1 > 0 ? string.to_f : string.to_i
136
152
  end
137
-
153
+
138
154
  def duration_units_list
139
155
  %w(seconds minutes hours days weeks months years)
140
156
  end
@@ -150,18 +166,19 @@ private
150
166
  when 'seconds'; 1
151
167
  end
152
168
  end
153
-
169
+
154
170
  def error_message
155
171
  'Sorry, that duration could not be parsed'
156
172
  end
157
-
173
+
158
174
  # Parse 3:41:59 and return 3 hours 41 minutes 59 seconds
159
175
  def filter_by_type(string)
176
+ chrono_units_list = duration_units_list.reject {|v| v == "weeks"}
160
177
  if string.gsub(' ', '') =~ /#{float_matcher}(:#{float_matcher})+/
161
178
  res = []
162
179
  string.gsub(' ', '').split(':').reverse.each_with_index do |v,k|
163
- return unless duration_units_list[k]
164
- res << "#{v} #{duration_units_list[k]}"
180
+ return unless chrono_units_list[k]
181
+ res << "#{v} #{chrono_units_list[k]}"
165
182
  end
166
183
  res = res.reverse.join(' ')
167
184
  else
@@ -169,11 +186,11 @@ private
169
186
  end
170
187
  res
171
188
  end
172
-
189
+
173
190
  def float_matcher
174
191
  /[0-9]*\.?[0-9]+/
175
192
  end
176
-
193
+
177
194
  # Get rid of unknown words and map found
178
195
  # words to defined time units
179
196
  def filter_through_white_list(string)
@@ -190,11 +207,13 @@ private
190
207
  raise DurationParseError, "An invalid word #{word.inspect} was used in the string to be parsed."
191
208
  end
192
209
  end
210
+ # add '1' at front if string starts with something recognizable but not with a number, like 'day' or 'minute 30sec'
211
+ res.unshift(1) if res.length > 0 && mappings[res[0]]
193
212
  res.join(' ')
194
213
  end
195
-
214
+
196
215
  def mappings
197
- {
216
+ {
198
217
  'seconds' => 'seconds',
199
218
  'second' => 'seconds',
200
219
  'secs' => 'seconds',
@@ -218,21 +237,23 @@ private
218
237
  'week' => 'weeks',
219
238
  'w' => 'weeks',
220
239
  'months' => 'months',
240
+ 'mo' => 'months',
221
241
  'mos' => 'months',
222
242
  'month' => 'months',
223
243
  'years' => 'years',
224
244
  'year' => 'years',
225
245
  'yrs' => 'years',
246
+ 'yr' => 'years',
226
247
  'y' => 'years'
227
248
  }
228
249
  end
229
-
250
+
230
251
  def join_words
231
252
  ['and', 'with', 'plus']
232
253
  end
233
-
254
+
234
255
  def white_list
235
- self.mappings.map {|k, v| k}
256
+ self.mappings.keys
236
257
  end
237
-
258
+
238
259
  end
@@ -0,0 +1,3 @@
1
+ module ChronicDuration
2
+ VERSION = '0.10.0'
3
+ end
@@ -1,8 +1,8 @@
1
1
  require 'chronic_duration'
2
2
 
3
3
  describe ChronicDuration, '.parse' do
4
-
5
- @exemplars = {
4
+
5
+ @exemplars = {
6
6
  '1:20' => 60 + 20,
7
7
  '1:20.51' => 60 + 20.51,
8
8
  '4:01:01' => 4 * 3600 + 60 + 1,
@@ -22,47 +22,49 @@ describe ChronicDuration, '.parse' do
22
22
  '3 weeks, plus 2 days' => 3600 * 24 * 7 * 3 + 3600 * 24 * 2,
23
23
  '3 weeks with 2 days' => 3600 * 24 * 7 * 3 + 3600 * 24 * 2,
24
24
  '1 month' => 3600 * 24 * 30,
25
- '2 months' => 3600 * 24 * 30 * 2
25
+ '2 months' => 3600 * 24 * 30 * 2,
26
+ 'day' => 3600 * 24,
27
+ 'minute 30s' => 90
26
28
  }
27
-
29
+
28
30
  it "should return nil if the string can't be parsed" do
29
31
  ChronicDuration.parse('gobblygoo').should be_nil
30
32
  end
31
-
33
+
32
34
  it "should raise an exception if the string can't be parsed and @@raise_exceptions is set to true" do
33
35
  ChronicDuration.raise_exceptions = true
34
36
  lambda { ChronicDuration.parse('23 gobblygoos') }.should raise_exception(ChronicDuration::DurationParseError)
35
37
  ChronicDuration.raise_exceptions = false
36
38
  end
37
-
39
+
38
40
  it "should return a float if seconds are in decimals" do
39
41
  ChronicDuration.parse('12 mins 3.141 seconds').is_a?(Float).should be_true
40
42
  end
41
-
43
+
42
44
  it "should return an integer unless the seconds are in decimals" do
43
45
  ChronicDuration.parse('12 mins 3 seconds').is_a?(Integer).should be_true
44
46
  end
45
-
47
+
46
48
  it "should be able to parse minutes by default" do
47
49
  ChronicDuration.parse('5', :default_unit => "minutes").should == 300
48
50
  end
49
-
51
+
50
52
  @exemplars.each do |k, v|
51
53
  it "should properly parse a duration like #{k}" do
52
54
  ChronicDuration.parse(k).should == v
53
55
  end
54
56
  end
55
-
57
+
56
58
  end
57
59
 
58
60
  describe ChronicDuration, '.output' do
59
-
61
+
60
62
  it "should return nil if the input can't be parsed" do
61
63
  ChronicDuration.parse('gobblygoo').should be_nil
62
64
  end
63
-
64
- @exemplars = {
65
- #(0) =>
65
+
66
+ @exemplars = {
67
+ #(0) =>
66
68
  #{
67
69
  #:micro => '0s',
68
70
  #:short => '0s',
@@ -70,64 +72,80 @@ describe ChronicDuration, '.output' do
70
72
  #:long => '0 seconds',
71
73
  #:chrono => '0'
72
74
  #},
73
- (60 + 20) =>
74
- {
75
+ (60 + 20) =>
76
+ {
75
77
  :micro => '1m20s',
76
78
  :short => '1m 20s',
77
79
  :default => '1 min 20 secs',
78
80
  :long => '1 minute 20 seconds',
79
81
  :chrono => '1:20'
80
82
  },
81
- (60 + 20.51) =>
82
- {
83
+ (60 + 20.51) =>
84
+ {
83
85
  :micro => '1m20.51s',
84
86
  :short => '1m 20.51s',
85
87
  :default => '1 min 20.51 secs',
86
88
  :long => '1 minute 20.51 seconds',
87
89
  :chrono => '1:20.51'
88
90
  },
89
- (60 + 20.51928) =>
90
- {
91
+ (60 + 20.51928) =>
92
+ {
91
93
  :micro => '1m20.51928s',
92
94
  :short => '1m 20.51928s',
93
95
  :default => '1 min 20.51928 secs',
94
96
  :long => '1 minute 20.51928 seconds',
95
97
  :chrono => '1:20.51928'
96
98
  },
97
- (4 * 3600 + 60 + 1) =>
98
- {
99
+ (4 * 3600 + 60 + 1) =>
100
+ {
99
101
  :micro => '4h1m1s',
100
102
  :short => '4h 1m 1s',
101
103
  :default => '4 hrs 1 min 1 sec',
102
104
  :long => '4 hours 1 minute 1 second',
103
105
  :chrono => '4:01:01'
104
106
  },
105
- (2 * 3600 + 20 * 60) =>
106
- {
107
+ (2 * 3600 + 20 * 60) =>
108
+ {
107
109
  :micro => '2h20m',
108
110
  :short => '2h 20m',
109
111
  :default => '2 hrs 20 mins',
110
112
  :long => '2 hours 20 minutes',
111
113
  :chrono => '2:20'
112
114
  },
113
- (2 * 3600 + 20 * 60) =>
114
- {
115
+ (2 * 3600 + 20 * 60) =>
116
+ {
115
117
  :micro => '2h20m',
116
118
  :short => '2h 20m',
117
119
  :default => '2 hrs 20 mins',
118
120
  :long => '2 hours 20 minutes',
119
121
  :chrono => '2:20:00'
120
122
  },
121
- (6 * 30 * 24 * 3600 + 24 * 3600) =>
122
- {
123
- :micro => '6m1d',
124
- :short => '6m 1d',
123
+ (6 * 30 * 24 * 3600 + 24 * 3600) =>
124
+ {
125
+ :micro => '6mo1d',
126
+ :short => '6mo 1d',
125
127
  :default => '6 mos 1 day',
126
128
  :long => '6 months 1 day',
127
129
  :chrono => '6:01:00:00:00' # Yuck. FIXME
128
- }
130
+ },
131
+ (365 * 24 * 3600 + 24 * 3600 ) =>
132
+ {
133
+ :micro => '1y1d',
134
+ :short => '1y 1d',
135
+ :default => '1 yr 1 day',
136
+ :long => '1 year 1 day',
137
+ :chrono => '1:00:01:00:00:00'
138
+ },
139
+ (3 * 365 * 24 * 3600 + 24 * 3600 ) =>
140
+ {
141
+ :micro => '3y1d',
142
+ :short => '3y 1d',
143
+ :default => '3 yrs 1 day',
144
+ :long => '3 years 1 day',
145
+ :chrono => '3:00:01:00:00:00'
146
+ },
129
147
  }
130
-
148
+
131
149
  @exemplars.each do |k, v|
132
150
  v.each do |key, val|
133
151
  it "should properly output a duration of #{k} seconds as #{val} using the #{key.to_s} format option" do
@@ -135,12 +153,27 @@ describe ChronicDuration, '.output' do
135
153
  end
136
154
  end
137
155
  end
138
-
156
+
157
+ it "should show weeks when needed" do
158
+ ChronicDuration.output(15*24*60*60, :weeks => true).should =~ /.*wk.*/
159
+ end
160
+
161
+ it "should show the specified number of units if provided" do
162
+ ChronicDuration.output(4 * 3600 + 60 + 1, units: 2).should == '4 hrs 1 min'
163
+ ChronicDuration.output(6 * 30 * 24 * 3600 + 24 * 3600 + 3600 + 60 + 1, units: 3, format: :long).should == '6 months 1 day 1 hour'
164
+ end
165
+
139
166
  it "should use the default format when the format is not specified" do
140
167
  ChronicDuration.output(2 * 3600 + 20 * 60).should == '2 hrs 20 mins'
141
168
  end
142
-
143
-
169
+
170
+ @exemplars.each do |seconds,format_spec|
171
+ format_spec.each do |format,_|
172
+ it "it should properly output a duration for #{seconds} that parses back to the same thing when using the #{format.to_s} format" do
173
+ ChronicDuration.parse(ChronicDuration.output(seconds, :format => format)).should == seconds
174
+ end
175
+ end
176
+ end
144
177
  end
145
178
 
146
179
 
@@ -148,37 +181,37 @@ end
148
181
  # us in development...
149
182
 
150
183
  describe ChronicDuration, "private methods" do
151
-
184
+
152
185
  describe ".filter_by_type" do
153
-
186
+
154
187
  it "should take a chrono-formatted time like 3:14 and return a human time like 3 minutes 14 seconds" do
155
188
  ChronicDuration.instance_eval("filter_by_type('3:14')").should == '3 minutes 14 seconds'
156
189
  end
157
-
190
+
158
191
  it "should take a chrono-formatted time like 12:10:14 and return a human time like 12 hours 10 minutes 14 seconds" do
159
192
  ChronicDuration.instance_eval("filter_by_type('12:10:14')").should == '12 hours 10 minutes 14 seconds'
160
193
  end
161
-
194
+
162
195
  it "should return the input if it's not a chrono-formatted time" do
163
196
  ChronicDuration.instance_eval("filter_by_type('4 hours')").should == '4 hours'
164
197
  end
165
-
198
+
166
199
  end
167
-
200
+
168
201
  describe ".cleanup" do
169
-
202
+
170
203
  it "should clean up extraneous words" do
171
204
  ChronicDuration.instance_eval("cleanup('4 days and 11 hours')").should == '4 days 11 hours'
172
205
  end
173
-
206
+
174
207
  it "should cleanup extraneous spaces" do
175
208
  ChronicDuration.instance_eval("cleanup(' 4 days and 11 hours')").should == '4 days 11 hours'
176
209
  end
177
-
210
+
178
211
  it "should insert spaces where there aren't any" do
179
212
  ChronicDuration.instance_eval("cleanup('4m11.5s')").should == '4 minutes 11.5 seconds'
180
213
  end
181
-
214
+
182
215
  end
183
-
184
- end
216
+
217
+ end
@@ -0,0 +1,212 @@
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 * 31536000 + 6 * 30 * 24 * 3600 + 24 * 3600,
18
+ '2.5 hrs' => 2.5 * 3600,
19
+ '47 yrs 6 mos and 4.5d' => 47 * 31536000 + 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
+ }
29
+
30
+ context "when string can't be parsed" do
31
+
32
+ it "returns nil" do
33
+ ChronicDuration.parse('gobblygoo').should be_nil
34
+ end
35
+
36
+ context "when @@raise_exceptions set to true" do
37
+
38
+ it "raises with ChronicDuration::DurationParseError" do
39
+ ChronicDuration.raise_exceptions = true
40
+ lambda { ChronicDuration.parse('23 gobblygoos') }.should raise_exception(ChronicDuration::DurationParseError)
41
+ ChronicDuration.raise_exceptions = false
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+
48
+ it "should return a float if seconds are in decimals" do
49
+ ChronicDuration.parse('12 mins 3.141 seconds').is_a?(Float).should be_true
50
+ end
51
+
52
+ it "should return an integer unless the seconds are in decimals" do
53
+ ChronicDuration.parse('12 mins 3 seconds').is_a?(Integer).should be_true
54
+ end
55
+
56
+ it "should be able to parse minutes by default" do
57
+ ChronicDuration.parse('5', :default_unit => "minutes").should == 300
58
+ end
59
+
60
+ @exemplars.each do |k, v|
61
+ it "parses a duration like #{k}" do
62
+ ChronicDuration.parse(k).should == v
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ describe '.output' do
69
+
70
+ @exemplars = {
71
+ (60 + 20) =>
72
+ {
73
+ :micro => '1m20s',
74
+ :short => '1m 20s',
75
+ :default => '1 min 20 secs',
76
+ :long => '1 minute 20 seconds',
77
+ :chrono => '1:20'
78
+ },
79
+ (60 + 20.51) =>
80
+ {
81
+ :micro => '1m20.51s',
82
+ :short => '1m 20.51s',
83
+ :default => '1 min 20.51 secs',
84
+ :long => '1 minute 20.51 seconds',
85
+ :chrono => '1:20.51'
86
+ },
87
+ (60 + 20.51928) =>
88
+ {
89
+ :micro => '1m20.51928s',
90
+ :short => '1m 20.51928s',
91
+ :default => '1 min 20.51928 secs',
92
+ :long => '1 minute 20.51928 seconds',
93
+ :chrono => '1:20.51928'
94
+ },
95
+ (4 * 3600 + 60 + 1) =>
96
+ {
97
+ :micro => '4h1m1s',
98
+ :short => '4h 1m 1s',
99
+ :default => '4 hrs 1 min 1 sec',
100
+ :long => '4 hours 1 minute 1 second',
101
+ :chrono => '4:01:01'
102
+ },
103
+ (2 * 3600 + 20 * 60) =>
104
+ {
105
+ :micro => '2h20m',
106
+ :short => '2h 20m',
107
+ :default => '2 hrs 20 mins',
108
+ :long => '2 hours 20 minutes',
109
+ :chrono => '2:20'
110
+ },
111
+ (2 * 3600 + 20 * 60) =>
112
+ {
113
+ :micro => '2h20m',
114
+ :short => '2h 20m',
115
+ :default => '2 hrs 20 mins',
116
+ :long => '2 hours 20 minutes',
117
+ :chrono => '2:20:00'
118
+ },
119
+ (6 * 30 * 24 * 3600 + 24 * 3600) =>
120
+ {
121
+ :micro => '6mo1d',
122
+ :short => '6mo 1d',
123
+ :default => '6 mos 1 day',
124
+ :long => '6 months 1 day',
125
+ :chrono => '6:01:00:00:00' # Yuck. FIXME
126
+ },
127
+ (365 * 24 * 3600 + 24 * 3600 ) =>
128
+ {
129
+ :micro => '1y1d',
130
+ :short => '1y 1d',
131
+ :default => '1 yr 1 day',
132
+ :long => '1 year 1 day',
133
+ :chrono => '1:00:01:00:00:00'
134
+ },
135
+ (3 * 365 * 24 * 3600 + 24 * 3600 ) =>
136
+ {
137
+ :micro => '3y1d',
138
+ :short => '3y 1d',
139
+ :default => '3 yrs 1 day',
140
+ :long => '3 years 1 day',
141
+ :chrono => '3:00:01:00:00:00'
142
+ },
143
+ }
144
+
145
+ @exemplars.each do |k, v|
146
+ v.each do |key, val|
147
+ it "properly outputs a duration of #{k} seconds as #{val} using the #{key.to_s} format option" do
148
+ ChronicDuration.output(k, :format => key).should == val
149
+ end
150
+ end
151
+ end
152
+
153
+ it "returns weeks when needed" do
154
+ ChronicDuration.output(15*24*60*60, :weeks => true).should =~ /.*wk.*/
155
+ end
156
+
157
+ it "returns the specified number of units if provided" do
158
+ ChronicDuration.output(4 * 3600 + 60 + 1, units: 2).should == '4 hrs 1 min'
159
+ ChronicDuration.output(6 * 30 * 24 * 3600 + 24 * 3600 + 3600 + 60 + 1, units: 3, format: :long).should == '6 months 1 day 1 hour'
160
+ end
161
+
162
+ context "when the format is not specified" do
163
+
164
+ it "uses the default format" do
165
+ ChronicDuration.output(2 * 3600 + 20 * 60).should == '2 hrs 20 mins'
166
+ end
167
+
168
+ end
169
+
170
+ @exemplars.each do |seconds, format_spec|
171
+ format_spec.each do |format, _|
172
+ it "outputs a duration for #{seconds} that parses back to the same thing when using the #{format.to_s} format" do
173
+ ChronicDuration.parse(ChronicDuration.output(seconds, :format => format)).should == seconds
174
+ end
175
+ end
176
+ end
177
+
178
+ end
179
+
180
+ describe ".filter_by_type" do
181
+
182
+ it "receives a chrono-formatted time like 3:14 and return a human time like 3 minutes 14 seconds" do
183
+ ChronicDuration.instance_eval("filter_by_type('3:14')").should == '3 minutes 14 seconds'
184
+ end
185
+
186
+ it "receives chrono-formatted time like 12:10:14 and return a human time like 12 hours 10 minutes 14 seconds" do
187
+ ChronicDuration.instance_eval("filter_by_type('12:10:14')").should == '12 hours 10 minutes 14 seconds'
188
+ end
189
+
190
+ it "returns the input if it's not a chrono-formatted time" do
191
+ ChronicDuration.instance_eval("filter_by_type('4 hours')").should == '4 hours'
192
+ end
193
+
194
+ end
195
+
196
+ describe ".cleanup" do
197
+
198
+ it "cleans up extraneous words" do
199
+ ChronicDuration.instance_eval("cleanup('4 days and 11 hours')").should == '4 days 11 hours'
200
+ end
201
+
202
+ it "cleans up extraneous spaces" do
203
+ ChronicDuration.instance_eval("cleanup(' 4 days and 11 hours')").should == '4 days 11 hours'
204
+ end
205
+
206
+ it "inserts spaces where there aren't any" do
207
+ ChronicDuration.instance_eval("cleanup('4m11.5s')").should == '4 minutes 11.5 seconds'
208
+ end
209
+
210
+ end
211
+
212
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'chronic_duration'
4
+
5
+ RSpec.configure do |config|
6
+ end
7
+
8
+
metadata CHANGED
@@ -1,121 +1,111 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: chronic_duration
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.10.0
4
5
  prerelease:
5
- version: 0.9.6
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - hpoydar
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-06-04 00:00:00 -04:00
14
- default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
12
+ date: 2013-02-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
17
15
  name: numerizer
18
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
19
17
  none: false
20
- requirements:
18
+ requirements:
21
19
  - - ~>
22
- - !ruby/object:Gem::Version
20
+ - !ruby/object:Gem::Version
23
21
  version: 0.1.1
24
22
  type: :runtime
25
23
  prerelease: false
26
- version_requirements: *id001
27
- - !ruby/object:Gem::Dependency
28
- name: rspec
29
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
30
25
  none: false
31
- requirements:
26
+ requirements:
32
27
  - - ~>
33
- - !ruby/object:Gem::Version
34
- version: 2.3.0
35
- type: :development
36
- prerelease: false
37
- version_requirements: *id002
38
- - !ruby/object:Gem::Dependency
39
- name: bundler
40
- requirement: &id003 !ruby/object:Gem::Requirement
28
+ - !ruby/object:Gem::Version
29
+ version: 0.1.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
41
33
  none: false
42
- requirements:
34
+ requirements:
43
35
  - - ~>
44
- - !ruby/object:Gem::Version
45
- version: 1.0.0
36
+ - !ruby/object:Gem::Version
37
+ version: 10.0.3
46
38
  type: :development
47
39
  prerelease: false
48
- version_requirements: *id003
49
- - !ruby/object:Gem::Dependency
50
- name: jeweler
51
- requirement: &id004 !ruby/object:Gem::Requirement
40
+ version_requirements: !ruby/object:Gem::Requirement
52
41
  none: false
53
- requirements:
42
+ requirements:
54
43
  - - ~>
55
- - !ruby/object:Gem::Version
56
- version: 1.5.2
57
- type: :development
58
- prerelease: false
59
- version_requirements: *id004
60
- - !ruby/object:Gem::Dependency
61
- name: rcov
62
- requirement: &id005 !ruby/object:Gem::Requirement
44
+ - !ruby/object:Gem::Version
45
+ version: 10.0.3
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
63
49
  none: false
64
- requirements:
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- version: "0"
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 2.12.0
68
54
  type: :development
69
55
  prerelease: false
70
- version_requirements: *id005
71
- description: 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.
72
- email: hpoydar@gmail.com
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.12.0
62
+ description: A simple Ruby natural language parser for elapsed time. (For example,
63
+ 4 hours and 30 minutes, 6 minutes 4 seconds, 3 days, etc.) Returns all results in
64
+ seconds. Will return an integer unless you get tricky and need a float. (4 minutes
65
+ and 13.47 seconds, for example.) The reverse can also be performed via the output
66
+ method.
67
+ email:
68
+ - henry@poydar.com
73
69
  executables: []
74
-
75
70
  extensions: []
76
-
77
- extra_rdoc_files:
78
- - README.rdoc
79
- files:
71
+ extra_rdoc_files: []
72
+ files:
73
+ - .gitignore
80
74
  - Gemfile
81
- - Gemfile.lock
82
75
  - MIT-LICENSE
83
- - README.rdoc
76
+ - README.md
84
77
  - Rakefile
85
- - VERSION
86
78
  - chronic_duration.gemspec
87
79
  - lib/chronic_duration.rb
80
+ - lib/chronic_duration/version.rb
88
81
  - spec/chronic_duration_spec.rb
89
- has_rdoc: true
90
- homepage: http://github.com/hpoydar/chronic_duration
91
- licenses:
92
- - MIT
82
+ - spec/lib/chronic_duration_spec.rb
83
+ - spec/spec_helper.rb
84
+ homepage: https://github.com/hpoydar/chronic_duration
85
+ licenses: []
93
86
  post_install_message:
94
87
  rdoc_options: []
95
-
96
- require_paths:
88
+ require_paths:
97
89
  - lib
98
- required_ruby_version: !ruby/object:Gem::Requirement
90
+ required_ruby_version: !ruby/object:Gem::Requirement
99
91
  none: false
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- hash: 2762452318022251891
104
- segments:
105
- - 0
106
- version: "0"
107
- required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
97
  none: false
109
- requirements:
110
- - - ">="
111
- - !ruby/object:Gem::Version
112
- version: "0"
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
113
102
  requirements: []
114
-
115
103
  rubyforge_project:
116
- rubygems_version: 1.5.2
104
+ rubygems_version: 1.8.23
117
105
  signing_key:
118
106
  specification_version: 3
119
- summary: A Ruby natural language parser for elapsed time
120
- test_files:
107
+ summary: A simple Ruby natural language parser for elapsed time
108
+ test_files:
121
109
  - spec/chronic_duration_spec.rb
110
+ - spec/lib/chronic_duration_spec.rb
111
+ - spec/spec_helper.rb
data/Gemfile.lock DELETED
@@ -1,30 +0,0 @@
1
- GEM
2
- remote: http://rubygems.org/
3
- specs:
4
- diff-lcs (1.1.2)
5
- git (1.2.5)
6
- jeweler (1.5.2)
7
- bundler (~> 1.0.0)
8
- git (>= 1.2.5)
9
- rake
10
- numerizer (0.1.1)
11
- rake (0.8.7)
12
- rcov (0.9.9)
13
- rspec (2.3.0)
14
- rspec-core (~> 2.3.0)
15
- rspec-expectations (~> 2.3.0)
16
- rspec-mocks (~> 2.3.0)
17
- rspec-core (2.3.1)
18
- rspec-expectations (2.3.0)
19
- diff-lcs (~> 1.1.2)
20
- rspec-mocks (2.3.0)
21
-
22
- PLATFORMS
23
- ruby
24
-
25
- DEPENDENCIES
26
- bundler (~> 1.0.0)
27
- jeweler (~> 1.5.2)
28
- numerizer (~> 0.1.1)
29
- rcov
30
- rspec (~> 2.3.0)
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.9.6