timerizer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: