timerizer 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 (2) hide show
  1. data/lib/timerizer.rb +336 -0
  2. metadata +46 -0
data/lib/timerizer.rb ADDED
@@ -0,0 +1,336 @@
1
+ require 'date'
2
+
3
+ # Represents a relative amount of time. For example, `5 days`, `4 years`, and `5 years, 4 hours, 3 minutes, 2 seconds` are all RelativeTimes.
4
+ class RelativeTime
5
+ # All potential units. Key is the unit name, and the value is its plural form.
6
+ @@units = {
7
+ :second => :seconds,
8
+ :minute => :minutes,
9
+ :hour => :hours,
10
+ :day => :days,
11
+ :week => :weeks,
12
+ :month => :months,
13
+ :year => :years,
14
+ :decade => :decades,
15
+ :century => :centuries,
16
+ :millennium => :millennia
17
+ }
18
+
19
+ # Unit values in seconds. If a unit is not present in this hash, it is assumed to be in the {@@in_months} hash.
20
+ @@in_seconds = {
21
+ :second => 1,
22
+ :minute => 60,
23
+ :hour => 3600,
24
+ :day => 86400,
25
+ :week => 604800
26
+ }
27
+
28
+ # Unit values in months. If a unit is not present in this hash, it is assumed to be in the {@@in_seconds} hash.
29
+ @@in_months = {
30
+ :month => 1,
31
+ :year => 12,
32
+ :decade => 120,
33
+ :century => 1200,
34
+ :millennium => 12000
35
+ }
36
+
37
+ # Average amount of time in a given unit. Used internally within the {#average} and {#unaverage} methods.
38
+ @@average_seconds = {
39
+ :month => 2629746,
40
+ :year => 31556952
41
+ }
42
+
43
+ # Initialize a new instance of RelativeTime.
44
+ # @overload new(hash)
45
+ # @param [Hash] units The base units to initialize with
46
+ # @option units [Fixnum] :seconds The number of seconds
47
+ # @option units [Fixnum] :months The number of months
48
+ # @overload new(count, unit)
49
+ # @param [Fixnum] count The number of units to initialize with
50
+ # @param [Symbol] unit The unit to initialize. See {@@units}
51
+ def initialize(count = 0, unit = :second)
52
+ if(count.is_a? Hash)
53
+ @seconds = count[:seconds] || 0
54
+ @months = count[:months] || 0
55
+ return
56
+ end
57
+
58
+ @seconds = 0
59
+ @months = 0
60
+
61
+ if(@@in_seconds.has_key?(unit))
62
+ @seconds = count * @@in_seconds.fetch(unit)
63
+ elsif(@@in_months.has_key?(unit))
64
+ @months = count * @@in_months.fetch(unit)
65
+ end
66
+ end
67
+
68
+ # Return the number of base units in a RelativeTime.
69
+ # @param [Symbol] unit The unit to return, either :seconds or :months
70
+ # @return [Fixnum] The requested unit count
71
+ # @raise [ArgumentError] Unit requested was not :seconds or :months
72
+ def get(unit)
73
+ return @seconds if unit == :seconds
74
+ return @months if unit == :months
75
+ raise ArgumentError
76
+ end
77
+
78
+ # Determines the time between RelativeTime and the given time.
79
+ # @param [Time] time The initial time.
80
+ # @return [Time] The difference between the current RelativeTime and the given time
81
+ # @example 5 hours before January 1st, 2000 at noon
82
+ # 5.minutes.before(Time.now(2000, 1, 1, 12, 00, 00))
83
+ # => 2000-01-01 11:55:00 -0800
84
+ # @see #ago
85
+ # @see #after
86
+ # @see #from_now
87
+ def before(time)
88
+ time = time.to_time - @seconds
89
+
90
+ new_month = time.month - @months
91
+ new_year = time.year
92
+ while new_month < 1
93
+ new_month += 12
94
+ new_year -= 1
95
+ end
96
+ if(Date.valid_date?(new_year, new_month, time.day))
97
+ new_day = time.day
98
+ else
99
+ new_day = Date.new(new_year, new_month).days_in_month
100
+ end
101
+
102
+ new_time = Time.new(
103
+ new_year, new_month, new_day,
104
+ time.hour, time.min, time.sec
105
+ )
106
+ Time.at(new_time.to_i, time.nsec/1000)
107
+ end
108
+
109
+ # Return the time between the RelativeTime and the current time.
110
+ # @return [Time] The difference between the current RelativeTime and Time#now
111
+ # @see #before
112
+ def ago
113
+ self.before(Time.now)
114
+ end
115
+
116
+ # Return the time after the given time according to the current RelativeTime.
117
+ # @param [Time] time The starting time
118
+ # @return [Time] The time after the current RelativeTime and the given time
119
+ # @see #before
120
+ def after(time)
121
+ time = time.to_time + @seconds
122
+
123
+ new_year = time.year
124
+ new_month = time.month + @months
125
+ while new_month > 12
126
+ new_year += 1
127
+ new_month -= 12
128
+ end
129
+ if(Date.valid_date?(new_year, new_month, time.day))
130
+ new_day = time.day
131
+ else
132
+ new_day = Date.new(new_year, new_month).days_in_month
133
+ end
134
+
135
+
136
+ new_time = Time.new(
137
+ new_year, new_month, new_day,
138
+ time.hour, time.min, time.sec
139
+ )
140
+ Time.at(new_time.to_i, time.nsec/1000.0)
141
+ end
142
+
143
+ # Return the time after the current time and the RelativeTime.
144
+ # @return [Time] The time after the current time
145
+ def from_now
146
+ self.after(Time.now)
147
+ end
148
+
149
+ @@units.each do |unit, plural|
150
+ in_method = "in_#{plural}"
151
+ count_method = plural
152
+ superior_unit = @@units.keys.index(unit) + 1
153
+
154
+ define_method(in_method) do
155
+ if(@@in_seconds.has_key?(unit))
156
+ @seconds / @@in_seconds[unit]
157
+ elsif(@@in_months.has_key?(unit))
158
+ @months / @@in_months[unit]
159
+ end
160
+ end
161
+
162
+ define_method(count_method) do
163
+ in_superior = "in_#{@@units.values[superior_unit]}"
164
+ count_superior = @@units.keys[superior_unit]
165
+
166
+ time = self.send(in_method)
167
+ if(@@units.length > superior_unit)
168
+ time -= self.send(in_superior).send(count_superior).send(in_method)
169
+ end
170
+ time
171
+ end
172
+ end
173
+
174
+ # Average second-based units to month-based units.
175
+ # @return [RelativeTime] The averaged RelativeTime
176
+ # @example
177
+ # 5.weeks.average
178
+ # => 1 month, 4 days, 13 hours, 30 minutes, 54 seconds
179
+ # @see #average!
180
+ # @see #unaverage
181
+ def average
182
+ return self unless @seconds > 0
183
+
184
+ months = (@seconds / @@average_seconds[:month])
185
+ seconds = @seconds - months.months.unaverage.get(:seconds)
186
+ RelativeTime.new({
187
+ :seconds => seconds,
188
+ :months => months + @months
189
+ })
190
+ end
191
+
192
+ # Destructively average second-based units to month-based units.
193
+ # @see #average
194
+ def average!
195
+ averaged = self.average
196
+ @seconds = averaged.get(:seconds)
197
+ @months = averaged.get(:months)
198
+ self
199
+ end
200
+
201
+ # Average month-based units to second-based units.
202
+ # @return [RelativeTime] the unaveraged RelativeTime.
203
+ # @example
204
+ # 1.month.unaverage
205
+ # => 4 weeks, 2 days, 10 hours, 29 minutes, 6 seconds
206
+ # @see #average
207
+ # @see #unaverage!
208
+ def unaverage
209
+ seconds = @@average_seconds[:month] * @months
210
+ seconds += @seconds
211
+ RelativeTime.new({:seconds => seconds})
212
+ end
213
+
214
+ # Destructively average month-based units to second-based units.
215
+ # @see #unaverage
216
+ def unaverage!
217
+ unaveraged = self.average
218
+ @seconds = unaverage.get(:seconds)
219
+ @months = unaverage.get(:months)
220
+ self
221
+ end
222
+
223
+ # Add two {RelativeTime}s together.
224
+ # @raise ArgumentError Argument isn't a {RelativeTime}
225
+ # @see #-
226
+ def +(time)
227
+ raise ArgumentError unless time.is_a?(RelativeTime)
228
+ RelativeTime.new({
229
+ :seconds => @seconds + time.get(:seconds),
230
+ :months => @months + time.get(:months)
231
+ })
232
+ end
233
+
234
+ # Find the difference between two {RelativeTime}s.
235
+ # @raise ArgumentError Argument isn't a {RelativeTime}
236
+ # @see #+
237
+ def -(time)
238
+ raise ArgumentError unless time.is_a?(RelativeTime)
239
+ RelativeTime.new({
240
+ :seconds => @seconds - time.get(:seconds),
241
+ :months => @months - time.get(:months)
242
+ })
243
+ end
244
+
245
+ # Convert {RelativeTime} to a human-readable format.
246
+ # @example
247
+ # (14.months 49.hours).to_s
248
+ # => 2 years, 2 months, 3 days, 1 hour
249
+ def to_s
250
+ times = []
251
+
252
+ @@units.each do |unit, plural|
253
+ time = self.respond_to?(plural) ? self.send(plural) : 0
254
+ times << [time, (time != 1) ? plural : unit] if time > 0
255
+ end
256
+
257
+ times.map do |time|
258
+ time.join(' ')
259
+ end.reverse.join(', ')
260
+ end
261
+ end
262
+ # {Time} class monkeywrenched with {RelativeTime} support.
263
+ class Time
264
+ add = instance_method(:+)
265
+ define_method(:+) do |time|
266
+ if(time.class == RelativeTime)
267
+ time.after(self)
268
+ else
269
+ add.bind(self).(time)
270
+ end
271
+ end
272
+
273
+ subtract = instance_method(:-)
274
+ define_method(:-) do |time|
275
+ if(time.class == RelativeTime)
276
+ time.before(self)
277
+ else
278
+ subtract.bind(self).(time)
279
+ end
280
+ end
281
+
282
+ # Convert {Time} to {Date}.
283
+ # @return [Date] {Time} as {Date}
284
+ # @example
285
+ # Time.new(2000, 1, 1, 12, 30).to_date
286
+ # => #<Date: 2000-01-01 ((2451545j,0s,0n),+0s,2299161j)>
287
+ def to_date
288
+ Date.new(self.year, self.month, self.day)
289
+ end
290
+
291
+ # Convert self to {Time}.
292
+ # @see Date#to_time
293
+ def to_time
294
+ self
295
+ end
296
+ end
297
+
298
+ # {Date} class monkeywrenched with {RelativeTime} helpers.
299
+ class Date
300
+ # Return the number of days in a given month.
301
+ # @return [Fixnum] Number of days in the month of the {Date}.
302
+ # @example
303
+ # Date.new(2000, 2).days_in_month
304
+ # => 29
305
+ def days_in_month
306
+ days_in_feb = (not self.leap?) ? 28 : 29
307
+ number_of_days = [
308
+ 31, days_in_feb, 31, 30, 31, 30,
309
+ 31, 31, 30, 31, 30, 31
310
+ ]
311
+
312
+ number_of_days.fetch(self.month - 1)
313
+ end
314
+
315
+ # Return self as {Date}.
316
+ # @see Time#to_date
317
+ def to_date
318
+ self
319
+ end
320
+ end
321
+
322
+ # Monkeywrenched {Fixnum} class enabled to return {RelativeTime} objects.
323
+ # @example
324
+ # 5.minutes
325
+ # => 5 minutes
326
+ # @see RelativeTime @@units
327
+ class Fixnum
328
+ units = RelativeTime.class_variable_get(:@@units)
329
+ units.each do |unit, plural|
330
+ define_method(unit) do |added_time = RelativeTime.new|
331
+ time = RelativeTime.new(self, unit)
332
+ time + added_time
333
+ end
334
+ alias_method(plural, unit)
335
+ end
336
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: timerizer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kyle Lacy
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-17 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: A simple set of Rails-like time helpers
15
+ email: kylelacy@me.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/timerizer.rb
21
+ homepage: http://github.com/kylewlacy/timerizer
22
+ licenses: []
23
+ post_install_message:
24
+ rdoc_options: []
25
+ require_paths:
26
+ - lib
27
+ required_ruby_version: !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ requirements: []
40
+ rubyforge_project:
41
+ rubygems_version: 1.8.24
42
+ signing_key:
43
+ specification_version: 3
44
+ summary: Rails time helpers... without the Rails
45
+ test_files: []
46
+ has_rdoc: