chronic_duration 0.7.5

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.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.gem
2
+ doc/*
data/MIT-LICENSE ADDED
@@ -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.
data/README.rdoc ADDED
@@ -0,0 +1,52 @@
1
+ = Chronic Duration
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.)
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.
6
+
7
+ == Installation
8
+
9
+ $ sudo gem sources -a http://gemcutter.org
10
+ $ sudo gem install chronic_duration
11
+
12
+ == Usage
13
+
14
+ >> require 'chronic_duration'
15
+ => true
16
+ >> ChronicDuration.parse('4 minutes and 30 seconds')
17
+ => 270
18
+ >> ChronicDuration.output(270)
19
+ => 4 mins 30 secs
20
+ >> ChronicDuration.output(270, :format => :short)
21
+ => 4m 30s
22
+ >> ChronicDuration.output(270, :format => :long)
23
+ => 4 minutes 30 seconds
24
+ >> ChronicDuration.output(270, :format => :chrono)
25
+ => 4:30
26
+
27
+ Nil is returned if the string can't be parsed
28
+
29
+ Examples of parse-able strings:
30
+
31
+ * '12.4 secs'
32
+ * '1:20'
33
+ * '1:20.51'
34
+ * '4:01:01'
35
+ * '3 mins 4 sec'
36
+ * '2 hrs 20 min'
37
+ * '2h20min'
38
+ * '6 mos 1 day'
39
+ * '47 yrs 6 mos and 4d'
40
+ * 'two hours and twenty minutes'
41
+
42
+ == Contributors
43
+
44
+ jduff, olauzon
45
+
46
+ == TODO
47
+
48
+ * Benchmark, optimize
49
+ * Context specific matching (E.g., for '4m30s', assume 'm' is minutes not months)
50
+ * Smartly parse vacation-like durations (E.g., '4 days and 3 nights')
51
+ * :chrono output option should probably change to something like 4 days 4:00:12 instead of 4:04:00:12
52
+ * Other locale support
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "chronic_duration"
8
+ gem.summary = %Q{A Ruby natural language parser for elapsed time}
9
+ 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.}
10
+ gem.email = "hpoydar@gmail.com"
11
+ gem.homepage = "http://github.com/hpoydar/chronic_duration"
12
+ gem.authors = ["hpoydar"]
13
+ gem.add_development_dependency "rspec"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ if File.exist?('VERSION')
40
+ version = File.read('VERSION')
41
+ else
42
+ version = ""
43
+ end
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "my-gem #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.7.5
@@ -0,0 +1,50 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{chronic_duration}
8
+ s.version = "0.7.5"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["hpoydar"]
12
+ s.date = %q{2009-10-05}
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
+ ".gitignore",
20
+ "MIT-LICENSE",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "chronic_duration.gemspec",
25
+ "lib/chronic_duration.rb",
26
+ "lib/numerizer.rb",
27
+ "spec/chronic_duration_spec.rb"
28
+ ]
29
+ s.homepage = %q{http://github.com/hpoydar/chronic_duration}
30
+ s.rdoc_options = ["--charset=UTF-8"]
31
+ s.require_paths = ["lib"]
32
+ s.rubygems_version = %q{1.3.5}
33
+ s.summary = %q{A Ruby natural language parser for elapsed time}
34
+ s.test_files = [
35
+ "spec/chronic_duration_spec.rb"
36
+ ]
37
+
38
+ if s.respond_to? :specification_version then
39
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
40
+ s.specification_version = 3
41
+
42
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
43
+ s.add_development_dependency(%q<rspec>, [">= 0"])
44
+ else
45
+ s.add_dependency(%q<rspec>, [">= 0"])
46
+ end
47
+ else
48
+ s.add_dependency(%q<rspec>, [">= 0"])
49
+ end
50
+ end
@@ -0,0 +1,202 @@
1
+ require 'numerizer'
2
+ module ChronicDuration
3
+ extend self
4
+
5
+ # Given a string representation of elapsed time,
6
+ # return an integer (or float, if fractions of a
7
+ # second are input)
8
+ def parse(string)
9
+ result = calculate_from_words(cleanup(string))
10
+ result == 0 ? nil : result
11
+ end
12
+
13
+ # Given an integer and an optional format,
14
+ # returns a formatted string representing elapsed time
15
+ def output(seconds, opts = {})
16
+
17
+ opts[:format] ||= :default
18
+
19
+ years = months = days = hours = minutes = 0
20
+
21
+ if seconds >= 60
22
+ minutes = (seconds / 60).to_i
23
+ seconds = seconds % 60
24
+ if minutes >= 60
25
+ hours = (minutes / 60).to_i
26
+ minutes = (minutes % 60).to_i
27
+ if hours >= 24
28
+ days = (hours / 24).to_i
29
+ hours = (hours % 24).to_i
30
+ if days >= 30
31
+ months = (days / 30).to_i
32
+ days = (days % 30).to_i
33
+ if months >= 12
34
+ years = (months / 12).to_i
35
+ months = (months % 12).to_i
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ joiner = ' '
43
+ process = nil
44
+
45
+ case opts[:format]
46
+ when :short
47
+ dividers = {
48
+ :years => 'y', :months => 'm', :days => 'd', :hours => 'h', :minutes => 'm', :seconds => 's' }
49
+ when :default
50
+ dividers = {
51
+ :years => ' yr', :months => ' mo', :days => ' day', :hours => ' hr', :minutes => ' min', :seconds => ' sec',
52
+ :pluralize => true }
53
+ when :long
54
+ dividers = {
55
+ :years => ' year', :months => ' month', :days => ' day', :hours => ' hour', :minutes => ' minute', :seconds => ' second',
56
+ :pluralize => true }
57
+ when :chrono
58
+ dividers = {
59
+ :years => ':', :months => ':', :days => ':', :hours => ':', :minutes => ':', :seconds => ':', :keep_zero => true }
60
+ process = lambda do |str|
61
+ # Pad zeros
62
+ # Get rid of lead off times if they are zero
63
+ # Get rid of lead off zero
64
+ # Get rid of trailing :
65
+ str.gsub(/\b\d\b/) { |d| ("%02d" % d) }.gsub(/^(00:)+/, '').gsub(/^0/, '').gsub(/:$/, '')
66
+ end
67
+ joiner = ''
68
+ end
69
+
70
+ result = []
71
+ [:years, :months, :days, :hours, :minutes, :seconds].each do |t|
72
+ result << humanize_time_unit( eval(t.to_s), dividers[t], dividers[:pluralize], dividers[:keep_zero] )
73
+ end
74
+
75
+ result = result.join(joiner).squeeze(' ').strip
76
+
77
+ if process
78
+ result = process.call(result)
79
+ end
80
+
81
+ result.length == 0 ? nil : result
82
+
83
+ end
84
+
85
+ private
86
+
87
+ def humanize_time_unit(number, unit, pluralize, keep_zero)
88
+ return '' if number == 0 && !keep_zero
89
+ res = "#{number}#{unit}"
90
+ # A poor man's pluralizer
91
+ res << 's' if !(number == 1) && pluralize
92
+ res
93
+ end
94
+
95
+ def calculate_from_words(string)
96
+ val = 0
97
+ words = string.split(' ')
98
+ words.each_with_index do |v, k|
99
+ if v =~ float_matcher
100
+ val += (convert_to_number(v) * duration_units_seconds_multiplier(words[k + 1] || 'seconds'))
101
+ end
102
+ end
103
+ val
104
+ end
105
+
106
+ def cleanup(string)
107
+ res = filter_by_type(Numerizer.numerize(string))
108
+ res = res.gsub(float_matcher) {|n| " #{n} "}.squeeze(' ').strip
109
+ res = filter_through_white_list(res)
110
+ end
111
+
112
+ def convert_to_number(string)
113
+ string.to_f % 1 > 0 ? string.to_f : string.to_i
114
+ end
115
+
116
+ def duration_units_list
117
+ %w(seconds minutes hours days weeks months years)
118
+ end
119
+ def duration_units_seconds_multiplier(unit)
120
+ return 0 unless duration_units_list.include?(unit)
121
+ case unit
122
+ when 'years'; 31557600 # accounts for leap years
123
+ when 'months'; 3600 * 24 * 30
124
+ when 'weeks'; 3600 * 24 * 7
125
+ when 'days'; 3600 * 24
126
+ when 'hours'; 3600
127
+ when 'minutes'; 60
128
+ when 'seconds'; 1
129
+ end
130
+ end
131
+
132
+ def error_message
133
+ 'Sorry, that duration could not be parsed'
134
+ end
135
+
136
+ # Parse 3:41:59 and return 3 hours 41 minutes 59 seconds
137
+ def filter_by_type(string)
138
+ if string.gsub(' ', '') =~ /#{float_matcher}(:#{float_matcher})+/
139
+ res = []
140
+ string.gsub(' ', '').split(':').reverse.each_with_index do |v,k|
141
+ return unless duration_units_list[k]
142
+ res << "#{v} #{duration_units_list[k]}"
143
+ end
144
+ res = res.reverse.join(' ')
145
+ else
146
+ res = string
147
+ end
148
+ res
149
+ end
150
+
151
+ def float_matcher
152
+ /[0-9]*\.?[0-9]+/
153
+ end
154
+
155
+ # Get rid of unknown words and map found
156
+ # words to defined time units
157
+ def filter_through_white_list(string)
158
+ res = []
159
+ string.split(' ').each do |word|
160
+ if word =~ float_matcher
161
+ res << word.strip
162
+ next
163
+ end
164
+ res << mappings[word.strip] if mappings.has_key?(word.strip)
165
+ end
166
+ res.join(' ')
167
+ end
168
+
169
+ def mappings
170
+ {
171
+ 'seconds' => 'seconds',
172
+ 'second' => 'seconds',
173
+ 'secs' => 'seconds',
174
+ 'sec' => 'seconds',
175
+ 's' => 'seconds',
176
+ 'minutes' => 'minutes',
177
+ 'minute' => 'minutes',
178
+ 'mins' => 'minutes',
179
+ 'min' => 'minutes',
180
+ 'm' => 'minutes',
181
+ 'hours' => 'hours',
182
+ 'hour' => 'hours',
183
+ 'hrs' => 'hours',
184
+ 'hr' => 'hours',
185
+ 'h' => 'hours',
186
+ 'days' => 'days',
187
+ 'day' => 'days',
188
+ 'dy' => 'days',
189
+ 'd' => 'days',
190
+ 'months' => 'months',
191
+ 'mos' => 'months',
192
+ 'years' => 'years',
193
+ 'yrs' => 'years',
194
+ 'y' => 'years'
195
+ }
196
+ end
197
+
198
+ def white_list
199
+ self.mappings.map {|k, v| k}
200
+ end
201
+
202
+ end
data/lib/numerizer.rb ADDED
@@ -0,0 +1,98 @@
1
+ require 'strscan'
2
+
3
+ class Numerizer
4
+
5
+ DIRECT_NUMS = [
6
+ ['eleven', '11'],
7
+ ['twelve', '12'],
8
+ ['thirteen', '13'],
9
+ ['fourteen', '14'],
10
+ ['fifteen', '15'],
11
+ ['sixteen', '16'],
12
+ ['seventeen', '17'],
13
+ ['eighteen', '18'],
14
+ ['nineteen', '19'],
15
+ ['ninteen', '19'], # Common mis-spelling
16
+ ['zero', '0'],
17
+ ['one', '1'],
18
+ ['two', '2'],
19
+ ['three', '3'],
20
+ ['four(\W|$)', '4\1'], # The weird regex is so that it matches four but not fourty
21
+ ['five', '5'],
22
+ ['six(\W|$)', '6\1'],
23
+ ['seven(\W|$)', '7\1'],
24
+ ['eight(\W|$)', '8\1'],
25
+ ['nine(\W|$)', '9\1'],
26
+ ['ten', '10'],
27
+ ['\ba[\b^$]', '1'] # doesn't make sense for an 'a' at the end to be a 1
28
+ ]
29
+
30
+ TEN_PREFIXES = [ ['twenty', 20],
31
+ ['thirty', 30],
32
+ ['forty', 40],
33
+ ['fourty', 40], # Common misspelling
34
+ ['fifty', 50],
35
+ ['sixty', 60],
36
+ ['seventy', 70],
37
+ ['eighty', 80],
38
+ ['ninety', 90]
39
+ ]
40
+
41
+ BIG_PREFIXES = [ ['hundred', 100],
42
+ ['thousand', 1000],
43
+ ['million', 1_000_000],
44
+ ['billion', 1_000_000_000],
45
+ ['trillion', 1_000_000_000_000],
46
+ ]
47
+
48
+ def self.numerize(string)
49
+ string = string.dup
50
+
51
+ # preprocess
52
+ string.gsub!(/ +|([^\d])-([^\d])/, '\1 \2') # will mutilate hyphenated-words but shouldn't matter for date extraction
53
+ string.gsub!(/a half/, 'haAlf') # take the 'a' out so it doesn't turn into a 1, save the half for the end
54
+
55
+ # easy/direct replacements
56
+
57
+ DIRECT_NUMS.each do |dn|
58
+ string.gsub!(/#{dn[0]}/i, '<num>' + dn[1])
59
+ end
60
+
61
+ # ten, twenty, etc.
62
+
63
+ TEN_PREFIXES.each do |tp|
64
+ string.gsub!(/(?:#{tp[0]}) *<num>(\d(?=[^\d]|$))*/i) { '<num>' + (tp[1] + $1.to_i).to_s }
65
+ end
66
+
67
+ TEN_PREFIXES.each do |tp|
68
+ string.gsub!(/#{tp[0]}/i) { '<num>' + tp[1].to_s }
69
+ end
70
+
71
+ # hundreds, thousands, millions, etc.
72
+
73
+ BIG_PREFIXES.each do |bp|
74
+ string.gsub!(/(?:<num>)?(\d*) *#{bp[0]}/i) { '<num>' + (bp[1] * $1.to_i).to_s}
75
+ andition(string)
76
+ end
77
+
78
+ # fractional addition
79
+ # I'm not combining this with the previous block as using float addition complicates the strings
80
+ # (with extraneous .0's and such )
81
+ string.gsub!(/(\d+)(?: | and |-)*haAlf/i) { ($1.to_f + 0.5).to_s }
82
+
83
+ string.gsub(/<num>/, '')
84
+ end
85
+
86
+ private
87
+
88
+ def self.andition(string)
89
+ sc = StringScanner.new(string)
90
+ while(sc.scan_until(/<num>(\d+)( | and )<num>(\d+)(?=[^\w]|$)/i))
91
+ if sc[2] =~ /and/ || sc[1].size > sc[3].size
92
+ string[(sc.pos - sc.matched_size)..(sc.pos-1)] = '<num>' + (sc[1].to_i + sc[3].to_i).to_s
93
+ sc.reset
94
+ end
95
+ end
96
+ end
97
+
98
+ end
@@ -0,0 +1,145 @@
1
+ require 'chronic_duration'
2
+
3
+ describe ChronicDuration, '.parse' do
4
+
5
+ @exemplars = {
6
+ '1:20' => 60 + 20,
7
+ '1:20.51' => 60 + 20.51,
8
+ '4:01:01' => 4 * 3600 + 60 + 1,
9
+ '3 mins 4 sec' => 3 * 60 + 4,
10
+ 'three mins four sec' => 3 * 60 + 4,
11
+ '2 hrs 20 min' => 2 * 3600 + 20 * 60,
12
+ '2h20min' => 2 * 3600 + 20 * 60,
13
+ '6 mos 1 day' => 6 * 30 * 24 * 3600 + 24 * 3600,
14
+ '2.5 hrs' => 2.5 * 3600,
15
+ '47 yrs 6 mos and 4.5d' => 47 * 31557600 + 6 * 30 * 24 * 3600 + 4.5 * 24 * 3600,
16
+ 'two hours and twenty minutes' => 2 * 3600 + 20 * 60,
17
+ 'four hours and forty minutes' => 4 * 3600 + 40 * 60,
18
+ 'four hours and fourty minutes' => 4 * 3600 + 40 * 60
19
+ }
20
+
21
+ it "should return nil if the string can't be parsed" do
22
+ ChronicDuration.parse('gobblygoo').should be_nil
23
+ end
24
+
25
+ it "should return a float if seconds are in decimals" do
26
+ ChronicDuration.parse('12 mins 3.141 seconds').is_a?(Float).should be_true
27
+ end
28
+
29
+ it "should return an integer unless the seconds are in decimals" do
30
+ ChronicDuration.parse('12 mins 3 seconds').is_a?(Integer).should be_true
31
+ end
32
+
33
+ @exemplars.each do |k, v|
34
+ it "should properly parse a duration like #{k}" do
35
+ ChronicDuration.parse(k).should == v
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ describe ChronicDuration, '.output' do
42
+
43
+ it "should return nil if the input can't be parsed" do
44
+ ChronicDuration.parse('gobblygoo').should be_nil
45
+ end
46
+
47
+ @exemplars = {
48
+ (60 + 20) =>
49
+ {
50
+ :short => '1m 20s',
51
+ :default => '1 min 20 secs',
52
+ :long => '1 minute 20 seconds',
53
+ :chrono => '1:20'
54
+ },
55
+ (60 + 20.51) =>
56
+ {
57
+ :short => '1m 20.51s',
58
+ :default => '1 min 20.51 secs',
59
+ :long => '1 minute 20.51 seconds',
60
+ :chrono => '1:20.51'
61
+ },
62
+ (4 * 3600 + 60 + 1) =>
63
+ {
64
+ :short => '4h 1m 1s',
65
+ :default => '4 hrs 1 min 1 sec',
66
+ :long => '4 hours 1 minute 1 second',
67
+ :chrono => '4:01:01'
68
+ },
69
+ (2 * 3600 + 20 * 60) =>
70
+ {
71
+ :short => '2h 20m',
72
+ :default => '2 hrs 20 mins',
73
+ :long => '2 hours 20 minutes',
74
+ :chrono => '2:20'
75
+ },
76
+ (2 * 3600 + 20 * 60) =>
77
+ {
78
+ :short => '2h 20m',
79
+ :default => '2 hrs 20 mins',
80
+ :long => '2 hours 20 minutes',
81
+ :chrono => '2:20:00'
82
+ },
83
+ (6 * 30 * 24 * 3600 + 24 * 3600) =>
84
+ {
85
+ :short => '6m 1d',
86
+ :default => '6 mos 1 day',
87
+ :long => '6 months 1 day',
88
+ :chrono => '6:01:00:00:00' # Yuck. FIXME
89
+ }
90
+ }
91
+
92
+ @exemplars.each do |k, v|
93
+ v.each do |key, val|
94
+ it "should properly output a duration of #{k} seconds as #{val} using the #{key.to_s} format option" do
95
+ ChronicDuration.output(k, :format => key).should == val
96
+ end
97
+ end
98
+ end
99
+
100
+ it "should use the default format when the format is not specified" do
101
+ ChronicDuration.output(2 * 3600 + 20 * 60).should == '2 hrs 20 mins'
102
+ end
103
+
104
+
105
+ end
106
+
107
+
108
+ # Some of the private methods deserve some spec'ing to aid
109
+ # us in development...
110
+
111
+ describe ChronicDuration, "private methods" do
112
+
113
+ describe ".filter_by_type" do
114
+
115
+ it "should take a chrono-formatted time like 3:14 and return a human time like 3 minutes 14 seconds" do
116
+ ChronicDuration.instance_eval("filter_by_type('3:14')").should == '3 minutes 14 seconds'
117
+ end
118
+
119
+ it "should take a chrono-formatted time like 12:10:14 and return a human time like 12 hours 10 minutes 14 seconds" do
120
+ ChronicDuration.instance_eval("filter_by_type('12:10:14')").should == '12 hours 10 minutes 14 seconds'
121
+ end
122
+
123
+ it "should return the input if it's not a chrono-formatted time" do
124
+ ChronicDuration.instance_eval("filter_by_type('4 hours')").should == '4 hours'
125
+ end
126
+
127
+ end
128
+
129
+ describe ".cleanup" do
130
+
131
+ it "should clean up extraneous words" do
132
+ ChronicDuration.instance_eval("cleanup('4 days and 11 hours')").should == '4 days 11 hours'
133
+ end
134
+
135
+ it "should cleanup extraneous spaces" do
136
+ ChronicDuration.instance_eval("cleanup(' 4 days and 11 hours')").should == '4 days 11 hours'
137
+ end
138
+
139
+ it "should insert spaces where there aren't any" do
140
+ ChronicDuration.instance_eval("cleanup('4m11.5s')").should == '4 minutes 11.5 seconds'
141
+ end
142
+
143
+ end
144
+
145
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chronic_duration
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.5
5
+ platform: ruby
6
+ authors:
7
+ - hpoydar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-05 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ 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.
26
+ email: hpoydar@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.rdoc
33
+ files:
34
+ - .gitignore
35
+ - MIT-LICENSE
36
+ - README.rdoc
37
+ - Rakefile
38
+ - VERSION
39
+ - chronic_duration.gemspec
40
+ - lib/chronic_duration.rb
41
+ - lib/numerizer.rb
42
+ - spec/chronic_duration_spec.rb
43
+ has_rdoc: true
44
+ homepage: http://github.com/hpoydar/chronic_duration
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options:
49
+ - --charset=UTF-8
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ requirements: []
65
+
66
+ rubyforge_project:
67
+ rubygems_version: 1.3.5
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: A Ruby natural language parser for elapsed time
71
+ test_files:
72
+ - spec/chronic_duration_spec.rb