hms 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hms.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Nicholas E. Rabenau
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # HMS
2
+
3
+ Models construction, parsing and formatting durations in hh::mm::ss format.
4
+
5
+ This is an extraction from the [cre-stats](http://github.com/nerab/cre-stats) project.
6
+
7
+ [![Build Status](https://secure.travis-ci.org/nerab/hms.png?branch=master)](http://travis-ci.org/nerab/hms)
8
+
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'hms'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install hms
23
+
24
+ ## Usage
25
+
26
+ d = HMS::Duration.new(42)
27
+ puts "Podcast episode length is #{d}." # 42
28
+
29
+ d = HMS::Duration.new('32:16:08')
30
+ puts "#{d} equals to #{d.to_i} seconds" # 116168
31
+
32
+ See [tests](/nerab/hms/blob/master/test/unit) for advanced usage.
33
+
34
+ ## Contributing
35
+
36
+ 1. Fork it
37
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
38
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
39
+ 4. Push to the branch (`git push origin my-new-feature`)
40
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |test|
6
+ test.libs << 'lib' << 'test' << 'test/unit'
7
+ test.pattern = 'test/unit/test_*.rb'
8
+ end
9
+
10
+ task :default => :test
data/hms.gemspec ADDED
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/hms/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Nicholas E. Rabenau"]
6
+ gem.email = ["nerab@gmx.net"]
7
+ gem.description = %q{Models construction, parsing and formatting durations in hh::mm::ss format}
8
+ gem.summary = %q{Durations in hh::mm::ss format}
9
+ gem.homepage = "https://github.com/nerab/hms"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "hms"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = HMS::VERSION
17
+
18
+ gem.add_development_dependency 'rake'
19
+ end
@@ -0,0 +1,136 @@
1
+ require 'forwardable'
2
+
3
+ module HMS
4
+ #
5
+ # Represents durations in hh:mm:ss format
6
+ #
7
+ # Parts taken from https://gist.github.com/309694
8
+ #
9
+ class Duration < Numeric
10
+ def initialize(val = 0)
11
+ case val
12
+ when String
13
+ @seconds = parse_string(val)
14
+ when Numeric
15
+ @seconds = val.to_i
16
+ when nil
17
+ @seconds = 0
18
+ else
19
+ raise ArgumentError, "Cannot convert #{val} to Duration"
20
+ end
21
+ end
22
+
23
+ def coerce(other)
24
+ [Duration.new(other.to_i), self]
25
+ end
26
+
27
+ #
28
+ # returns the total duration in seconds
29
+ #
30
+ def to_i
31
+ @seconds
32
+ end
33
+
34
+ #
35
+ # returns the hours part of the total duration
36
+ #
37
+ def hours
38
+ @seconds.abs / 60 / 60 * (@seconds < 0 ? -1 : 1)
39
+ end
40
+
41
+ #
42
+ # returns the hours part of the total duration
43
+ #
44
+ def minutes
45
+ (@seconds.abs / 60) % 60 * (@seconds < 0 ? -1 : 1)
46
+ end
47
+
48
+ #
49
+ # Returns the seconds part of the total duration. For the total in seconds
50
+ # use #to_i.
51
+ #
52
+ def seconds
53
+ @seconds.abs % 60 * (@seconds < 0 ? -1 : 1)
54
+ end
55
+
56
+ def to_s
57
+ "#{'-' if @seconds < 0}#{'%02d' % hours.abs}:#{'%02d' % minutes.abs}:#{'%02d' % seconds.abs}"
58
+ end
59
+
60
+ def eql?(other)
61
+ self.class.equal?(other.class) && @seconds == other.to_i
62
+ end
63
+
64
+ def ==(other)
65
+ @seconds == other.to_i
66
+ end
67
+
68
+ # delegate to seconds
69
+ extend Forwardable
70
+ def_delegators :@seconds, :hash, :zero?, :nonzero?, :between?
71
+
72
+ include Comparable
73
+
74
+ def <=>(other)
75
+ case other
76
+ when Duration
77
+ @seconds <=> other.to_i
78
+ when Numeric
79
+ @seconds <=> other
80
+ else
81
+ a, b = other.coerce(self)
82
+ a <=> b
83
+ end rescue nil
84
+ end
85
+
86
+ def +@
87
+ self
88
+ end
89
+
90
+ def -@
91
+ Duration.new(-@seconds)
92
+ end
93
+
94
+ def +(other)
95
+ op(:+, other)
96
+ end
97
+
98
+ def -(other)
99
+ op(:-, other)
100
+ end
101
+
102
+ def *(other)
103
+ op(:*, other)
104
+ end
105
+
106
+ def /(other)
107
+ op(:/, other)
108
+ end
109
+
110
+ private
111
+
112
+ DURATION_REGEXP = /([0-9]+):([0-5][0-9]):([0-5][0-9])/
113
+
114
+ #
115
+ # Loads a duration (seconds) from hh:mm:ss string
116
+ #
117
+ def parse_string(str)
118
+ hms = DURATION_REGEXP.match(str)
119
+ raise ArgumentError, "The duration #{str} does not match the expected format #{DURATION_REGEXP}" if hms.nil?
120
+ hms.captures[0].to_i * 60 * 60 + hms.captures[1].to_i * 60 + hms.captures[2].to_i
121
+ end
122
+
123
+ # generic operator implementation
124
+ def op(sym, o)
125
+ case o
126
+ when Duration
127
+ Duration.new(@seconds.send(sym, o.to_i))
128
+ when Numeric
129
+ Duration.new(@seconds.send(sym, o))
130
+ else
131
+ a, b = o.coerce(self)
132
+ a.send(sym, b)
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,3 @@
1
+ module HMS
2
+ VERSION = "0.0.1"
3
+ end
data/lib/hms.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "hms/version"
2
+ require "hms/duration"
3
+
4
+ module HMS
5
+ # Your code goes here...
6
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,24 @@
1
+ require 'minitest/autorun'
2
+ require 'hms'
3
+
4
+ class MiniTest::Unit::TestCase
5
+ # extensions go here
6
+ end
7
+
8
+ #
9
+ # Allow some better expressiveness
10
+ #
11
+ class Fixnum
12
+ def hour
13
+ minute * 60
14
+ end
15
+ alias hours hour
16
+
17
+ def minute
18
+ to_i * 60
19
+ end
20
+ alias minutes minute
21
+
22
+ alias second to_i
23
+ alias seconds second
24
+ end
@@ -0,0 +1,296 @@
1
+ require 'helper'
2
+
3
+ class TestDuration < MiniTest::Unit::TestCase
4
+ include HMS
5
+
6
+ def test_initial
7
+ d = Duration.new
8
+ assert_equal(0, d.to_i)
9
+ assert_equal('00:00:00', d.to_s)
10
+ end
11
+
12
+ def test_invalid_strings
13
+ assert_raises(ArgumentError){Duration.new('')}
14
+ assert_raises(ArgumentError){Duration.new('foo')}
15
+
16
+ assert(Duration.new('00:00:59'))
17
+ assert_raises(ArgumentError){Duration.new('00:00:60')}
18
+ assert(Duration.new('00:01:00'))
19
+
20
+ assert(Duration.new('00:59:00'))
21
+ assert_raises(ArgumentError){Duration.new('00:60:00')}
22
+ assert(Duration.new('99:00:00'))
23
+ end
24
+
25
+ def test_zero
26
+ sec = 0
27
+ assert_construction('00:00:00', sec)
28
+ assert_parts(0, 0, 0, sec)
29
+ end
30
+
31
+ def test_1_sec
32
+ sec = 1.second
33
+ assert_construction('00:00:01', sec)
34
+ assert_parts(0, 0, 1, sec)
35
+ end
36
+
37
+ def test_59_sec
38
+ sec = 59.seconds
39
+ assert_construction('00:00:59', sec)
40
+ assert_parts(0, 0, 59, sec)
41
+ end
42
+
43
+ def test_60_sec
44
+ sec = 1.minute
45
+ assert_construction('00:01:00', sec)
46
+ assert_parts(0, 1, 0, sec)
47
+ end
48
+
49
+ def test_61_sec
50
+ sec = 1.minute + 1.seconds
51
+ assert_construction('00:01:01', sec)
52
+ assert_parts(0, 1, 1, sec)
53
+ end
54
+
55
+ def test_3_min_59_sec
56
+ sec = 3.minutes + 59.seconds
57
+ assert_construction('00:03:59', sec)
58
+ assert_parts(0, 3, 59, sec)
59
+ end
60
+
61
+ def test_3_min_60_sec
62
+ sec = 3.minutes + 60.seconds
63
+ assert_construction('00:04:00', sec)
64
+ assert_parts(0, 4, 0, sec)
65
+ end
66
+
67
+ def test_3_min_61_sec
68
+ sec = 3.minutes + 61.seconds
69
+ assert_construction('00:04:01', sec)
70
+ assert_parts(0, 4, 1, sec)
71
+ end
72
+
73
+ def test_59_min_59_sec
74
+ sec = 59.minutes + 59.seconds
75
+ assert_construction('00:59:59', sec)
76
+ assert_parts(0, 59, 59, sec)
77
+ end
78
+
79
+ def test_59_min_60_sec
80
+ sec = 59.minutes + 60.seconds
81
+ assert_construction('01:00:00', sec)
82
+ assert_parts(1, 0, 0, sec)
83
+ end
84
+
85
+ def test_59_min_61_sec
86
+ sec = 59.minutes + 61.seconds
87
+ assert_construction('01:00:01', sec)
88
+ assert_parts(1, 0, 1, sec)
89
+ end
90
+
91
+ def test_1_hr_59_sec
92
+ sec = 1.hour + 59.seconds
93
+ assert_construction('01:00:59', sec)
94
+ assert_parts(1, 0, 59, sec)
95
+ end
96
+
97
+ def test_1_hr_60_sec
98
+ sec = 1.hour + 60.seconds
99
+ assert_construction('01:01:00', sec)
100
+ assert_parts(1, 1, 0, sec)
101
+ end
102
+
103
+ def test_1_hr_61_sec
104
+ sec = 1.hour + 61.seconds
105
+ assert_construction('01:01:01', sec)
106
+ assert_parts(1, 1, 1, sec)
107
+ end
108
+
109
+ def test_1_hr_59_min_59_sec
110
+ sec = 1.hour + 59.minutes + 59.seconds
111
+ assert_construction('01:59:59', sec)
112
+ assert_parts(1, 59, 59, sec)
113
+ end
114
+
115
+ def test_1_hr_59_min_60_sec
116
+ sec = 1.hour + 59.minutes + 60.seconds
117
+ assert_construction('02:00:00', sec)
118
+ assert_parts(2, 0, 0, sec)
119
+ end
120
+
121
+ def test_1_hr_59_min_61_sec
122
+ sec = 1.hour + 59.minutes + 61.seconds
123
+ assert_construction('02:00:01', sec)
124
+ assert_parts(2, 0, 1, sec)
125
+ end
126
+
127
+ def test_23_hr_59_min_59_sec
128
+ sec = 23.hours + 59.minutes + 59.seconds
129
+ assert_construction('23:59:59', sec)
130
+ assert_parts(23, 59, 59, sec)
131
+ end
132
+
133
+ # we don't overflow to days
134
+ def test_23_hr_59_min_60_sec
135
+ sec = 23.hours + 59.minutes + 60.seconds
136
+ assert_construction('24:00:00', sec)
137
+ assert_parts(24, 0, 0, sec)
138
+ end
139
+
140
+ def test_23_hr_59_min_61_sec
141
+ sec = 23.hours + 59.minutes + 61.seconds
142
+ assert_construction('24:00:01', sec)
143
+ assert_parts(24, 0, 1, sec)
144
+ end
145
+
146
+ def test_99_hr_59_min_59_sec
147
+ sec = 99.hours + 59.minutes + 59.seconds
148
+ assert_construction('99:59:59', sec)
149
+ assert_parts(99, 59, 59, sec)
150
+ end
151
+
152
+ def test_99_hr_59_min_60_sec
153
+ sec = 99.hours + 59.minutes + 60.seconds
154
+ assert_construction('100:00:00', sec)
155
+ assert_parts(100, 0, 0, sec)
156
+ end
157
+
158
+ def test_99_hr_59_min_61_sec
159
+ sec = 99.hours + 59.minutes + 61.seconds
160
+ assert_construction('100:00:01', sec)
161
+ assert_parts(100, 0, 1, sec)
162
+ end
163
+
164
+ def test_plus_zero
165
+ assert_equal(0, Duration.new + Duration.new)
166
+ assert_equal(0, Duration.new + 0)
167
+ assert_equal(0, 0 + Duration.new)
168
+ end
169
+
170
+ def test_plus_two
171
+ assert_equal(4, Duration.new(2) + Duration.new(2))
172
+ assert_equal(4, Duration.new(2) + 2)
173
+ assert_equal(4, 2 + Duration.new(2))
174
+ end
175
+
176
+ def test_minus_zero
177
+ assert_equal(0, Duration.new(2) - Duration.new(2))
178
+ assert_equal(0, Duration.new(2) - 2)
179
+ assert_equal(0, 2 - Duration.new(2))
180
+ assert_equal('00:00:00', (2 - Duration.new(2)).to_s)
181
+ end
182
+
183
+ def test_minus_positive
184
+ assert_equal(2, Duration.new(5) - Duration.new(3))
185
+ assert_equal(2, Duration.new(5) - 3)
186
+ assert_equal(2, 5 - Duration.new(3))
187
+ assert_equal('00:00:02', (5 - Duration.new(3)).to_s)
188
+ end
189
+
190
+ def test_minus_negative
191
+ assert_equal(-2, Duration.new(5) - Duration.new(7))
192
+ assert_equal(-2, Duration.new(5) - 7)
193
+ assert_equal(-2, 5 - Duration.new(7))
194
+
195
+ minus2 = (5 - Duration.new(7))
196
+ assert_parts(0, 0, -2, minus2)
197
+ assert_equal('-00:00:02', minus2.to_s)
198
+ end
199
+
200
+ def test_negative_to_s
201
+ assert_equal('-00:00:59', Duration.new(-1.minute + 1.second).to_s)
202
+ assert_equal('-00:01:00', Duration.new(-1.minute).to_s)
203
+ assert_equal('-00:01:08', Duration.new(-1.minute - 8.seconds).to_s)
204
+ end
205
+
206
+ def test_compare_zero
207
+ assert_equal(Duration.new, Duration.new)
208
+ assert_equal(0, Duration.new)
209
+ assert_equal(Duration.new, 0)
210
+ end
211
+
212
+ def test_compare_positive
213
+ assert_equal(Duration.new(42), Duration.new(42))
214
+ assert_equal(42, Duration.new(42))
215
+ assert_equal(Duration.new(42), 42)
216
+ end
217
+
218
+ def test_compare_negative
219
+ assert_equal(Duration.new(-23), Duration.new(-23))
220
+ assert_equal(-23, Duration.new(-23))
221
+ assert_equal(Duration.new(-23), -23)
222
+ end
223
+
224
+ def test_equals_zero
225
+ assert(Duration.new == Duration.new)
226
+ assert(0 == Duration.new)
227
+ assert(Duration.new == 0)
228
+ end
229
+
230
+ def test_equals_positive
231
+ assert(Duration.new(42) == Duration.new(42))
232
+ assert(42 == Duration.new(42))
233
+ assert(Duration.new(42) == 42)
234
+ end
235
+
236
+ def test_equals_negative
237
+ assert(Duration.new(-23) == Duration.new(-23))
238
+ assert(-23 == Duration.new(-23))
239
+ assert(Duration.new(-23) == -23)
240
+ end
241
+
242
+ def test_compare_same
243
+ assert_equal(0, Duration.new <=> Duration.new)
244
+ assert_compare_same(0)
245
+ assert_compare_same(42)
246
+ assert_compare_same(-11)
247
+ end
248
+
249
+ def test_compare_shorter
250
+ assert_compare_shorter(23, 42)
251
+ assert_compare_shorter(-42, 23)
252
+ end
253
+
254
+ def test_compare_longer
255
+ assert_compare_longer(42, 23)
256
+ assert_compare_longer(-23, -42)
257
+ end
258
+
259
+ private
260
+
261
+ def assert_construction(str, int)
262
+ d = Duration.new(int)
263
+ assert_equal(int, d.to_i)
264
+ assert_equal(str, d.to_s)
265
+ assert_equal(int, Duration.new(str).to_i)
266
+ assert_equal(str, Duration.new(str).to_s)
267
+ end
268
+
269
+ def assert_parts(hours, minutes, seconds, int)
270
+ d = Duration.new(int)
271
+
272
+ assert_equal(hours, d.hours, 'Wrong hours')
273
+ assert_equal(minutes, d.minutes, 'Wrong minutes')
274
+ assert_equal(seconds, d.seconds, 'Wrong seconds')
275
+ end
276
+
277
+ def assert_compare_same(number)
278
+ assert_equal(0, Duration.new(number) <=> Duration.new(number))
279
+ assert_equal(0, number <=> Duration.new(number))
280
+ assert_equal(0, Duration.new(number) <=> number)
281
+ end
282
+
283
+ def assert_compare_shorter(left, right)
284
+ assert(left < right)
285
+ assert(0 > (Duration.new(left) <=> Duration.new(right)))
286
+ assert(0 > (left <=> Duration.new(right)))
287
+ assert(0 > (Duration.new(left) <=> right))
288
+ end
289
+
290
+ def assert_compare_longer(left, right)
291
+ assert(left > right)
292
+ assert(0 < (Duration.new(left) <=> Duration.new(right)))
293
+ assert(0 < (left <=> Duration.new(right)))
294
+ assert(0 < (Duration.new(left) <=> right))
295
+ end
296
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hms
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Nicholas E. Rabenau
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: Models construction, parsing and formatting durations in hh::mm::ss format
31
+ email:
32
+ - nerab@gmx.net
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - .travis.yml
39
+ - Gemfile
40
+ - LICENSE
41
+ - README.md
42
+ - Rakefile
43
+ - hms.gemspec
44
+ - lib/hms.rb
45
+ - lib/hms/duration.rb
46
+ - lib/hms/version.rb
47
+ - test/helper.rb
48
+ - test/unit/test_duration.rb
49
+ homepage: https://github.com/nerab/hms
50
+ licenses: []
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 1.8.24
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Durations in hh::mm::ss format
73
+ test_files:
74
+ - test/helper.rb
75
+ - test/unit/test_duration.rb