brianjlandau-chronic_duration 0.8.2

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,53 @@
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
+ * '3 weeks and 2 days'
42
+
43
+ == Contributors
44
+
45
+ jduff, olauzon
46
+
47
+ == TODO
48
+
49
+ * Benchmark, optimize
50
+ * Context specific matching (E.g., for '4m30s', assume 'm' is minutes not months)
51
+ * Smartly parse vacation-like durations (E.g., '4 days and 3 nights')
52
+ * :chrono output option should probably change to something like 4 days 4:00:12 instead of 4:04:00:12
53
+ * 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 = "brianjlandau-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/brianjlandau/chronic_duration"
12
+ gem.authors = ["hpoydar", 'brianjlandau']
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 = "chronic_duration #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.8.2
@@ -0,0 +1,51 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{brianjlandau-chronic_duration}
8
+ s.version = "0.8.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["hpoydar", "brianjlandau"]
12
+ s.date = %q{2010-01-22}
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
+ "brianjlandau-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/brianjlandau/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
51
+
@@ -0,0 +1,210 @@
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 :micro
47
+ dividers = {
48
+ :years => 'y', :months => 'm', :days => 'd', :hours => 'h', :minutes => 'm', :seconds => 's' }
49
+ joiner = ''
50
+ when :short
51
+ dividers = {
52
+ :years => 'y', :months => 'm', :days => 'd', :hours => 'h', :minutes => 'm', :seconds => 's' }
53
+ when :default
54
+ dividers = {
55
+ :years => ' yr', :months => ' mo', :days => ' day', :hours => ' hr', :minutes => ' min', :seconds => ' sec',
56
+ :pluralize => true }
57
+ when :long
58
+ dividers = {
59
+ :years => ' year', :months => ' month', :days => ' day', :hours => ' hour', :minutes => ' minute', :seconds => ' second',
60
+ :pluralize => true }
61
+ when :chrono
62
+ dividers = {
63
+ :years => ':', :months => ':', :days => ':', :hours => ':', :minutes => ':', :seconds => ':', :keep_zero => true }
64
+ process = lambda do |str|
65
+ # Pad zeros
66
+ # Get rid of lead off times if they are zero
67
+ # Get rid of lead off zero
68
+ # Get rid of trailing :
69
+ str.gsub(/\b\d\b/) { |d| ("%02d" % d) }.gsub(/^(00:)+/, '').gsub(/^0/, '').gsub(/:$/, '')
70
+ end
71
+ joiner = ''
72
+ end
73
+
74
+ result = []
75
+ [:years, :months, :days, :hours, :minutes, :seconds].each do |t|
76
+ result << humanize_time_unit( eval(t.to_s), dividers[t], dividers[:pluralize], dividers[:keep_zero] )
77
+ end
78
+
79
+ result = result.join(joiner).squeeze(' ').strip
80
+
81
+ if process
82
+ result = process.call(result)
83
+ end
84
+
85
+ result.length == 0 ? nil : result
86
+
87
+ end
88
+
89
+ private
90
+
91
+ def humanize_time_unit(number, unit, pluralize, keep_zero)
92
+ return '' if number == 0 && !keep_zero
93
+ res = "#{number}#{unit}"
94
+ # A poor man's pluralizer
95
+ res << 's' if !(number == 1) && pluralize
96
+ res
97
+ end
98
+
99
+ def calculate_from_words(string)
100
+ val = 0
101
+ words = string.split(' ')
102
+ words.each_with_index do |v, k|
103
+ if v =~ float_matcher
104
+ val += (convert_to_number(v) * duration_units_seconds_multiplier(words[k + 1] || 'seconds'))
105
+ end
106
+ end
107
+ val
108
+ end
109
+
110
+ def cleanup(string)
111
+ res = string.downcase
112
+ res = filter_by_type(Numerizer.numerize(res))
113
+ res = res.gsub(float_matcher) {|n| " #{n} "}.squeeze(' ').strip
114
+ res = filter_through_white_list(res)
115
+ end
116
+
117
+ def convert_to_number(string)
118
+ string.to_f % 1 > 0 ? string.to_f : string.to_i
119
+ end
120
+
121
+ def duration_units_list
122
+ %w(seconds minutes hours days weeks months years)
123
+ end
124
+ def duration_units_seconds_multiplier(unit)
125
+ return 0 unless duration_units_list.include?(unit)
126
+ case unit
127
+ when 'years'; 31557600 # accounts for leap years
128
+ when 'months'; 3600 * 24 * 30
129
+ when 'weeks'; 3600 * 24 * 7
130
+ when 'days'; 3600 * 24
131
+ when 'hours'; 3600
132
+ when 'minutes'; 60
133
+ when 'seconds'; 1
134
+ end
135
+ end
136
+
137
+ def error_message
138
+ 'Sorry, that duration could not be parsed'
139
+ end
140
+
141
+ # Parse 3:41:59 and return 3 hours 41 minutes 59 seconds
142
+ def filter_by_type(string)
143
+ if string.gsub(' ', '') =~ /#{float_matcher}(:#{float_matcher})+/
144
+ res = []
145
+ string.gsub(' ', '').split(':').reverse.each_with_index do |v,k|
146
+ return unless duration_units_list[k]
147
+ res << "#{v} #{duration_units_list[k]}"
148
+ end
149
+ res = res.reverse.join(' ')
150
+ else
151
+ res = string
152
+ end
153
+ res
154
+ end
155
+
156
+ def float_matcher
157
+ /[0-9]*\.?[0-9]+/
158
+ end
159
+
160
+ # Get rid of unknown words and map found
161
+ # words to defined time units
162
+ def filter_through_white_list(string)
163
+ res = []
164
+ string.split(' ').each do |word|
165
+ if word =~ float_matcher
166
+ res << word.strip
167
+ next
168
+ end
169
+ res << mappings[word.strip] if mappings.has_key?(word.strip)
170
+ end
171
+ res.join(' ')
172
+ end
173
+
174
+ def mappings
175
+ {
176
+ 'seconds' => 'seconds',
177
+ 'second' => 'seconds',
178
+ 'secs' => 'seconds',
179
+ 'sec' => 'seconds',
180
+ 's' => 'seconds',
181
+ 'minutes' => 'minutes',
182
+ 'minute' => 'minutes',
183
+ 'mins' => 'minutes',
184
+ 'min' => 'minutes',
185
+ 'm' => 'minutes',
186
+ 'hours' => 'hours',
187
+ 'hour' => 'hours',
188
+ 'hrs' => 'hours',
189
+ 'hr' => 'hours',
190
+ 'h' => 'hours',
191
+ 'days' => 'days',
192
+ 'day' => 'days',
193
+ 'dy' => 'days',
194
+ 'd' => 'days',
195
+ 'weeks' => 'weeks',
196
+ 'week' => 'weeks',
197
+ 'w' => 'weeks',
198
+ 'months' => 'months',
199
+ 'mos' => 'months',
200
+ 'years' => 'years',
201
+ 'yrs' => 'years',
202
+ 'y' => 'years'
203
+ }
204
+ end
205
+
206
+ def white_list
207
+ self.mappings.map {|k, v| k}
208
+ end
209
+
210
+ 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,153 @@
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
+ '3 Mins 4 Sec' => 3 * 60 + 4,
11
+ 'three mins four sec' => 3 * 60 + 4,
12
+ '2 hrs 20 min' => 2 * 3600 + 20 * 60,
13
+ '2h20min' => 2 * 3600 + 20 * 60,
14
+ '6 mos 1 day' => 6 * 30 * 24 * 3600 + 24 * 3600,
15
+ '2.5 hrs' => 2.5 * 3600,
16
+ '47 yrs 6 mos and 4.5d' => 47 * 31557600 + 6 * 30 * 24 * 3600 + 4.5 * 24 * 3600,
17
+ 'two hours and twenty minutes' => 2 * 3600 + 20 * 60,
18
+ 'four hours and forty minutes' => 4 * 3600 + 40 * 60,
19
+ 'four hours and fourty minutes' => 4 * 3600 + 40 * 60,
20
+ '3 weeks and 2 days' => 3600 * 24 * 7 * 3 + 3600 * 24 * 2
21
+ }
22
+
23
+ it "should return nil if the string can't be parsed" do
24
+ ChronicDuration.parse('gobblygoo').should be_nil
25
+ end
26
+
27
+ it "should return a float if seconds are in decimals" do
28
+ ChronicDuration.parse('12 mins 3.141 seconds').is_a?(Float).should be_true
29
+ end
30
+
31
+ it "should return an integer unless the seconds are in decimals" do
32
+ ChronicDuration.parse('12 mins 3 seconds').is_a?(Integer).should be_true
33
+ end
34
+
35
+ @exemplars.each do |k, v|
36
+ it "should properly parse a duration like #{k}" do
37
+ ChronicDuration.parse(k).should == v
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ describe ChronicDuration, '.output' do
44
+
45
+ it "should return nil if the input can't be parsed" do
46
+ ChronicDuration.parse('gobblygoo').should be_nil
47
+ end
48
+
49
+ @exemplars = {
50
+ (60 + 20) =>
51
+ {
52
+ :micro => '1m20s',
53
+ :short => '1m 20s',
54
+ :default => '1 min 20 secs',
55
+ :long => '1 minute 20 seconds',
56
+ :chrono => '1:20'
57
+ },
58
+ (60 + 20.51) =>
59
+ {
60
+ :micro => '1m20.51s',
61
+ :short => '1m 20.51s',
62
+ :default => '1 min 20.51 secs',
63
+ :long => '1 minute 20.51 seconds',
64
+ :chrono => '1:20.51'
65
+ },
66
+ (4 * 3600 + 60 + 1) =>
67
+ {
68
+ :micro => '4h1m1s',
69
+ :short => '4h 1m 1s',
70
+ :default => '4 hrs 1 min 1 sec',
71
+ :long => '4 hours 1 minute 1 second',
72
+ :chrono => '4:01:01'
73
+ },
74
+ (2 * 3600 + 20 * 60) =>
75
+ {
76
+ :micro => '2h20m',
77
+ :short => '2h 20m',
78
+ :default => '2 hrs 20 mins',
79
+ :long => '2 hours 20 minutes',
80
+ :chrono => '2:20'
81
+ },
82
+ (2 * 3600 + 20 * 60) =>
83
+ {
84
+ :micro => '2h20m',
85
+ :short => '2h 20m',
86
+ :default => '2 hrs 20 mins',
87
+ :long => '2 hours 20 minutes',
88
+ :chrono => '2:20:00'
89
+ },
90
+ (6 * 30 * 24 * 3600 + 24 * 3600) =>
91
+ {
92
+ :micro => '6m1d',
93
+ :short => '6m 1d',
94
+ :default => '6 mos 1 day',
95
+ :long => '6 months 1 day',
96
+ :chrono => '6:01:00:00:00' # Yuck. FIXME
97
+ }
98
+ }
99
+
100
+ @exemplars.each do |k, v|
101
+ v.each do |key, val|
102
+ it "should properly output a duration of #{k} seconds as #{val} using the #{key.to_s} format option" do
103
+ ChronicDuration.output(k, :format => key).should == val
104
+ end
105
+ end
106
+ end
107
+
108
+ it "should use the default format when the format is not specified" do
109
+ ChronicDuration.output(2 * 3600 + 20 * 60).should == '2 hrs 20 mins'
110
+ end
111
+
112
+
113
+ end
114
+
115
+
116
+ # Some of the private methods deserve some spec'ing to aid
117
+ # us in development...
118
+
119
+ describe ChronicDuration, "private methods" do
120
+
121
+ describe ".filter_by_type" do
122
+
123
+ it "should take a chrono-formatted time like 3:14 and return a human time like 3 minutes 14 seconds" do
124
+ ChronicDuration.instance_eval("filter_by_type('3:14')").should == '3 minutes 14 seconds'
125
+ end
126
+
127
+ it "should take a chrono-formatted time like 12:10:14 and return a human time like 12 hours 10 minutes 14 seconds" do
128
+ ChronicDuration.instance_eval("filter_by_type('12:10:14')").should == '12 hours 10 minutes 14 seconds'
129
+ end
130
+
131
+ it "should return the input if it's not a chrono-formatted time" do
132
+ ChronicDuration.instance_eval("filter_by_type('4 hours')").should == '4 hours'
133
+ end
134
+
135
+ end
136
+
137
+ describe ".cleanup" do
138
+
139
+ it "should clean up extraneous words" do
140
+ ChronicDuration.instance_eval("cleanup('4 days and 11 hours')").should == '4 days 11 hours'
141
+ end
142
+
143
+ it "should cleanup extraneous spaces" do
144
+ ChronicDuration.instance_eval("cleanup(' 4 days and 11 hours')").should == '4 days 11 hours'
145
+ end
146
+
147
+ it "should insert spaces where there aren't any" do
148
+ ChronicDuration.instance_eval("cleanup('4m11.5s')").should == '4 minutes 11.5 seconds'
149
+ end
150
+
151
+ end
152
+
153
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: brianjlandau-chronic_duration
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.2
5
+ platform: ruby
6
+ authors:
7
+ - hpoydar
8
+ - brianjlandau
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2010-01-22 00:00:00 -05:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rspec
18
+ type: :development
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ version:
26
+ 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.
27
+ email: hpoydar@gmail.com
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - README.rdoc
34
+ files:
35
+ - .gitignore
36
+ - MIT-LICENSE
37
+ - README.rdoc
38
+ - Rakefile
39
+ - VERSION
40
+ - brianjlandau-chronic_duration.gemspec
41
+ - lib/chronic_duration.rb
42
+ - lib/numerizer.rb
43
+ - spec/chronic_duration_spec.rb
44
+ has_rdoc: true
45
+ homepage: http://github.com/brianjlandau/chronic_duration
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --charset=UTF-8
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ requirements: []
66
+
67
+ rubyforge_project:
68
+ rubygems_version: 1.3.5
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: A Ruby natural language parser for elapsed time
72
+ test_files:
73
+ - spec/chronic_duration_spec.rb