chronic_duration 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
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