hour-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +34 -0
  3. data/lib/hour.rb +184 -0
  4. data/spec/hour_spec.rb +110 -0
  5. metadata +47 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b037de6a208ce2bf419a9e1cf5558ecbfc8885e3ee28c58e0f958242c0a75764
4
+ data.tar.gz: eec358d6872bc3414c6a5789db74a688cc211381aead80b12cad3eca0601b57f
5
+ SHA512:
6
+ metadata.gz: aa59a5c611500afb23db52439e8f43754f8e857f96dfd6b209352bb57328668421722e7be59268fd19fd6caa50b0d2b291bd5d3e7242555ff03a40885a7c82c5
7
+ data.tar.gz: c70a2bf14a4f3bddf1e3cdbb69a0c391041549b85d2b26a60806cdd94edbd73511e6629ebb94e740599ff610e1d078b4b5cb68d386a68f6f70d9ef6a6504f315
data/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # About
2
+ [![Build status][BS img]][Build status]
3
+
4
+ Hour class to work with hours, minutes and seconds, convert between various units and format the output.
5
+
6
+ # Installation
7
+
8
+ ```yaml
9
+ gem install hour-ruby
10
+ ```
11
+
12
+ # Usage
13
+
14
+ ```crystal
15
+ require "hour"
16
+
17
+ hour = Hour.from(minutes: 85)
18
+ puts "It's #{hour.hours.value}:#{hour.minutes.value}!"
19
+
20
+ hour = Hour.new(1, 25) + Hour.new(s: 10)
21
+ puts "It's #{hour.to_s}!"
22
+
23
+ puts "The system time is #{Hour.now}!"
24
+ ```
25
+
26
+ # TODO
27
+
28
+ - Rubocop.
29
+ - Travis.
30
+ - Link hour-crystal.
31
+ - Release version 0.1.
32
+
33
+ [Build status]: https://travis-ci.org/botanicus/hour-ruby
34
+ [BS img]: https://travis-ci.org/botanicus/hour-ruby.svg?branch=master
data/lib/hour.rb ADDED
@@ -0,0 +1,184 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hour
4
+ # Abstract time unit class.
5
+ #
6
+ # Subclasses are decorating the `Hour` class with functionality
7
+ # specific for their particular type (hours, minutes and seconds).
8
+ #
9
+ # @api private
10
+ class Unit
11
+ def value
12
+ raise NotImplementedError.new("Override in subclasses.")
13
+ end
14
+
15
+ def initialize(hour)
16
+ @hour = hour
17
+ end
18
+ end
19
+
20
+ class HourUnit < Unit
21
+ def value
22
+ @hour.h
23
+ end
24
+
25
+ def round
26
+ self.value + ((0..29).include?(@hour.m) ? 0 : 1)
27
+ end
28
+ end
29
+
30
+ class MinuteUnit < Unit
31
+ def value
32
+ @hour.m
33
+ end
34
+
35
+ def round
36
+ self.value + ((0..29).include?(@hour.s) ? 0 : 1)
37
+ end
38
+
39
+ def total
40
+ @hour.h * 60 + self.value
41
+ end
42
+
43
+ def round_total
44
+ self.total + ((0..29).include?(@hour.s) ? 0 : 1)
45
+ end
46
+ end
47
+
48
+ class SecondUnit < Unit
49
+ def value
50
+ @hour.s
51
+ end
52
+
53
+ def total
54
+ @hour.h * 60 * 60 + @hour.m * 60 + self.value
55
+ end
56
+ end
57
+
58
+ # TODO: Test me and document me.
59
+ def self.now
60
+ self.from_time(Time.now)
61
+ end
62
+
63
+ # TODO: document and write tests.
64
+ def self.from_time(time)
65
+ self.new(h: time.hours, m: time.minutes, s: time.seconds)
66
+ end
67
+
68
+ # Build an hour instance from an hour string.
69
+ #
70
+ # Hour.parse("1:00:00")
71
+ # Hour.parse("1:00", "%h:%m?") # Will work with "1:00" or just "1".
72
+ #
73
+ # TODO: Implement me, test me and document me.
74
+ def self.parse(string, formatting_string = nil)
75
+ argument_array = serialised_hour.split(':').map(&:to_i)
76
+
77
+ case argument_array.size
78
+ when 3
79
+ self.new(*argument_array)
80
+ when (0..2)
81
+ # TODO: if formatting_string ...
82
+ raise ArgumentError.new("If format is not H:M:S, formatting string must be provided.")
83
+ when (4..Float::INFINITY) # Until we have infinite ranges like (4..) in Ruby 2.6.
84
+ raise ArgumentError, "Too many arguments."
85
+ end
86
+ end
87
+
88
+ # Build an hour instance from *either* **minutes** *or* **seconds**.
89
+ # Unlike `.new`, either of these values can be over 60.
90
+ #
91
+ # Hour.from(minutes: 85) # => Hour.new(h: 1, m: 25)
92
+ # Hour.from(seconds: 120) # => Hour.new(m: 2)
93
+ def self.from(minutes: 0, seconds: 0)
94
+ if minutes != 0 && seconds != 0
95
+ raise ArgumentError.new("Use either minutes OR seconds, not both.")
96
+ end
97
+
98
+ if minutes > 0
99
+ self.new(h: minutes / 60, m: minutes % 60)
100
+ else
101
+ self.from(minutes: seconds / 60) + self.new(s: seconds % 60)
102
+ end
103
+ end
104
+
105
+ attr_reader :h, :m, :s
106
+
107
+ # Build an hour instance from *h*, *m* and *s*.
108
+ # Raises an argument error if *m* or *s* is a value over 60.
109
+ #
110
+ # For instantiating this class from a *minutes* or *seconds* value over 60, use `.from`.
111
+ def initialize(*args)
112
+ if args.length == 1 && args.first.is_a?(Hash)
113
+ initialize_from_keyword_args(**args.first)
114
+ else
115
+ # Pad with 0s.
116
+ args = args + Array.new(3 - args.length, 0)
117
+ @h, @m, @s = args
118
+ end
119
+
120
+ if @m > 60
121
+ raise ArgumentError.new("Minutes must be a number between 0 and 60.")
122
+ end
123
+
124
+ if @s > 60
125
+ raise ArgumentError.new("Seconds must be a number between 0 and 60.")
126
+ end
127
+ end
128
+
129
+ # Returns a new Hour instance returning the total time of the two hour instances.
130
+ #
131
+ # Hour.new(m: 25, s: 10) + Hour.new(h: 1) # => Hour.new(1, 25, 10)
132
+ def +(other)
133
+ hours = @h + other.h + (@m + other.m + ((@s + other.s) / 60)) / 60
134
+ minutes = (@m + other.m + ((@s + other.s) / 60)) % 60
135
+ seconds = (@s + other.s) % 60
136
+ self.class.new(hours, minutes, seconds)
137
+ end
138
+
139
+ # Returns a decorator providing convenience methods for working with hours.
140
+ #
141
+ # Hour.new(1, 25).hours.round # => 1
142
+ # Hour.new(1, 45).hours.round # => 2
143
+ def hours
144
+ HourUnit.new(self)
145
+ end
146
+
147
+ # Returns a decorator providing convenience methods for working with minutes.
148
+ #
149
+ # Hour.new(1, 25, 52).minutes.value # => 25
150
+ # Hour.new(1, 25, 52).minutes.round # => 26
151
+ # Hour.new(1, 25, 52).minutes.total # => 85
152
+ # Hour.new(1, 25, 52).minutes.round_total # => 86
153
+ def minutes
154
+ MinuteUnit.new(self)
155
+ end
156
+
157
+ # Returns a decorator providing convenience methods for working with seconds.
158
+ #
159
+ # Hour.new(m: 1, s: 10).seconds.value # => 10
160
+ # Hour.new(1, 45, 10 ).seconds.total # => (1 * 60 * 60) + (45 * 60) + 10
161
+ def seconds
162
+ SecondUnit.new(self)
163
+ end
164
+
165
+ # TODO: Add formatting string support.
166
+ # TODO: Pad 0s. I. e. "#{self.hours}:#{format('%02d', self.minutes_over_the_hour)}"
167
+ def to_s(format = nil)
168
+ "#{@h}:#{@m}:#{@s}"
169
+ end
170
+
171
+ alias_method :inspect, :to_s
172
+
173
+ def to_time(today = Time.now)
174
+ Time.new(today.year, today.month, today.day, self.hours, self.minutes_over_the_hour)
175
+ end
176
+
177
+ private
178
+ def initialize_from_keyword_args(h: 0, m: 0, s: 0)
179
+ @h, @m, @s = h, m, s
180
+ end
181
+ end
182
+
183
+ # https://github.com/botanicus/commonjs_modules
184
+ export { Hour } if defined?(export)
data/spec/hour_spec.rb ADDED
@@ -0,0 +1,110 @@
1
+ require "hour"
2
+
3
+ describe Hour do
4
+ describe ".parse" do
5
+ pending "implement me" #do
6
+ # TODO
7
+ # p Hour.parse("1:02:30")
8
+ #end
9
+ end
10
+
11
+ describe ".now" do
12
+ pending "implement me" #do
13
+ # TODO
14
+ #end
15
+ end
16
+
17
+ describe "#to_s" do
18
+ pending "implement me" #do
19
+ # TODO
20
+ #end
21
+ end
22
+
23
+ describe "#+" do
24
+ it "returns a new Hour instance returning the total time of the two hour instances" do
25
+ hour = Hour.new(m: 25, s: 10) + Hour.new(h: 1)
26
+ expect(hour.hours.value).to eq(1)
27
+ expect(hour.minutes.value).to eq(25)
28
+ expect(hour.seconds.value).to eq(10)
29
+ end
30
+ end
31
+
32
+ describe ".from" do
33
+ context "minutes" do
34
+ it "returns a new Hour instance" do
35
+ hour = Hour.from(minutes: 85)
36
+ expect(hour.hours.value).to eq(1)
37
+ expect(hour.minutes.value).to eq(25)
38
+ expect(hour.seconds.value).to eq(0)
39
+ end
40
+ end
41
+
42
+ context "seconds" do
43
+ it "returns a new Hour instance" do
44
+ hour = Hour.from(seconds: 2 * 60 * 60 + 25 * 60 + 5)
45
+ expect(hour.hours.value).to eq(2)
46
+ expect(hour.minutes.value).to eq(25)
47
+ expect(hour.seconds.value).to eq(5)
48
+ end
49
+ end
50
+ end
51
+
52
+ describe "#hours" do
53
+ describe "#value" do
54
+ it "returns the exact hour value" do
55
+ expect(Hour.new(1, 10).hours.value).to eq(1)
56
+ expect(Hour.new(1, 59).hours.value).to eq(1)
57
+ end
58
+ end
59
+
60
+ describe "#round" do
61
+ it "returns the rounded hour" do
62
+ expect(Hour.new(1, 10).hours.round).to eq(1)
63
+ expect(Hour.new(1, 29, 59).hours.round).to eq(1)
64
+ expect(Hour.new(1, 30).hours.round).to eq(2)
65
+ expect(Hour.new(1, 59).hours.round).to eq(2)
66
+ end
67
+ end
68
+ end
69
+
70
+ describe "#minutes" do
71
+ describe "#value" do
72
+ it "returns the exact minute value" do
73
+ expect(Hour.new(1, 25, 52).minutes.value).to eq(25)
74
+ end
75
+ end
76
+
77
+ describe "#round" do
78
+ it "returns the rounded minutes" do
79
+ expect(Hour.new(1, 25, 52).minutes.round).to eq(26)
80
+ end
81
+ end
82
+
83
+ describe "#total" do
84
+ it "returns the total number of minutes" do
85
+ expect(Hour.new(1, 25, 52).minutes.total).to eq(85)
86
+ end
87
+ end
88
+
89
+ describe "#round_total" do
90
+ it "returns the rounded total number of minutes" do
91
+ expect(Hour.new(1, 25, 52).minutes.round_total).to eq(86)
92
+ end
93
+ end
94
+ end
95
+
96
+ describe "#seconds" do
97
+ describe "#value" do
98
+ it "returns the exact second value" do
99
+ expect(Hour.new(m: 1, s: 25).seconds.value).to eq(25)
100
+ end
101
+ end
102
+
103
+ describe "#total" do
104
+ it "returns the exact total number of seconds" do
105
+ expect(Hour.new(m: 1, s: 25).seconds.total).to eq(85)
106
+ expect(Hour.new(1, 10, 25).seconds.total).to eq(1 * 60 * 60 + 10 * 60 + 25)
107
+ end
108
+ end
109
+ end
110
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hour-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - James C Russell
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-06-21 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: "."
14
+ email: james@101ideas.cz
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - README.md
20
+ - lib/hour.rb
21
+ - spec/hour_spec.rb
22
+ homepage: http://github.com/botanicus/hour-ruby
23
+ licenses:
24
+ - MIT
25
+ metadata:
26
+ yard.run: yri
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubyforge_project:
43
+ rubygems_version: 2.7.6
44
+ signing_key:
45
+ specification_version: 4
46
+ summary: ''
47
+ test_files: []