hour-ruby 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.
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: []