richunits 0.2.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rich_units.rb CHANGED
@@ -1,4 +1 @@
1
- require 'rich_units/multipliers'
2
- require 'rich_units/bytes'
3
- require 'rich_units/times'
4
-
1
+ require 'richunits'
data/lib/richunits.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'richunits/multipliers'
2
+ require 'richunits/bytes'
3
+ require 'richunits/times'
4
+ require 'richunits/weekdays'
5
+ require 'richunits/duration'
@@ -0,0 +1,178 @@
1
+ # TITLE:
2
+ #
3
+ # Bytes
4
+ #
5
+ # DESCRIPTION:
6
+ #
7
+ # Additional methods for Numeric class to make working with
8
+ # bits and bytes easier.
9
+ #
10
+ # COPYRIGHT:
11
+ #
12
+ # Copyright (c) 2005 Rich Kilmer
13
+ #
14
+ # LICENSE:
15
+ #
16
+ # Ruby License
17
+ #
18
+ # This module is free software. You may use, modify, and/or redistribute this
19
+ # software under the same terms as Ruby.
20
+ #
21
+ # This program is distributed in the hope that it will be useful, but WITHOUT
22
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23
+ # FOR A PARTICULAR PURPOSE.
24
+ #
25
+ # HISTORY:
26
+ #
27
+ # Special thanks to Richard Kilmer for the orignal work.
28
+ # This library is based on the original library bytes.rb
29
+ # Copyright (c) 2004 by Rich Kilmer.
30
+ #
31
+ # Also thanks to Alexander Kellett for suggesting it be
32
+ # included in Facets.
33
+ #
34
+ # AUTHORS:
35
+ #
36
+ # - Rich Kilmer
37
+ # - Thomas Sawyer
38
+ #
39
+ # NOTES:
40
+ #
41
+ # - This library is not compatible with STICK's units.rb (an spin-off
42
+ # of Facets old units.rb library). Do not attempt to use both at the same time.
43
+ #
44
+ # TODOs:
45
+ #
46
+ # - Currently kilo, mega, etc. are all powers of two and not ten,
47
+ # which technically isn't corrent even though it is common usage.
48
+ #
49
+ # - The in_* notation is weak. If a better nomentclature is thought
50
+ # of then consider changing this.
51
+
52
+ #
53
+ module RichUnits
54
+
55
+ # = Binary Multipliers
56
+ #
57
+ module Bytes
58
+
59
+ # = Binary Multipliers for Numeric
60
+ #
61
+ # Additional methods for Numeric class to make working with
62
+ # bits and bytes easier. Bits are used as the base value and
63
+ # these methods can be used to convert between different
64
+ # magnitudes.
65
+ #
66
+ # == Synopisis
67
+ #
68
+ # 1.byte #=> 8
69
+ # 2.bytes #=> 16
70
+ # 1.kilobit #=> 1024
71
+ # 1.kilobyte #=> 8192
72
+ #
73
+ # Use the in_* methods to perform the inverse operations.
74
+ #
75
+ # 8192.in_kilobytes #=> 1
76
+ # 1024.in_kilobits #=> 1
77
+ #
78
+ module Numeric
79
+
80
+ def bit ; self ; end
81
+ def bits ; self ; end
82
+ def byte ; self * 8 ; end
83
+ def bytes ; self * 8 ; end
84
+
85
+ [ 'kilo', 'mega', 'giga', 'tera', 'peta', 'exa' ].each_with_index do |m, i|
86
+ j = i + 1
87
+ class_eval %{
88
+ def #{m}bit ; self * #{1024**j} ; end
89
+ def #{m}byte ; self * #{1024**j*8} ; end
90
+ def in_#{m}bits ; self / #{1024**j} ; end
91
+ def in_#{m}bytes ; self / #{1024**j*8} ; end
92
+ alias_method :#{m}bits, :#{m}bit
93
+ alias_method :#{m}bytes, :#{m}byte
94
+ }
95
+ end
96
+
97
+ [ 'kibi', 'mebi', 'gibi', 'tebi', 'pebi', 'exbi' ].each_with_index do |m, i|
98
+ j = i + 1
99
+ class_eval %{
100
+ def #{m}bit ; self * #{1024**j} ; end
101
+ def #{m}byte ; self * #{1024**j*8} ; end
102
+ def in_#{m}bits ; self / #{1024**j} ; end
103
+ def in_#{m}bytes ; self / #{1024**j*8} ; end
104
+ alias_method :#{m}bits, :#{m}bit
105
+ alias_method :#{m}bytes, :#{m}byte
106
+ }
107
+ end
108
+
109
+ # Formated string of bits proportial to size.
110
+ #
111
+ # 1024.bits_to_s #=> "1.00 kb"
112
+ # 1048576.bits_to_s #=> "1.00 mb"
113
+ # 1073741824.bits_to_s #=> "1.00 gb"
114
+ # 1099511627776.bits_to_s #=> "1.00 tb"
115
+ #
116
+ # Takes a format string to adjust output.
117
+ #
118
+ # 1024.bits_to_s('%.0f') #=> "1 kb"
119
+ #
120
+ def strfbits(fmt='%.2f')
121
+ case
122
+ when self < 1024
123
+ "#{self} bits"
124
+ when self < 1024**2
125
+ "#{fmt % (self.to_f / 1024)} kb"
126
+ when self < 1024**3
127
+ "#{fmt % (self.to_f / 1024**2)} mb"
128
+ when self < 1024**4
129
+ "#{fmt % (self.to_f / 1024**3)} gb"
130
+ when self < 1024**5
131
+ "#{fmt % (self.to_f / 1024**4)} tb"
132
+ else
133
+ "#{self} bits"
134
+ end
135
+ end
136
+
137
+ # Formated string of bytes proportial to size.
138
+ #
139
+ # 1024.bytes_to_s #=> "1.00 KB"
140
+ # 1048576.bytes_to_s #=> "1.00 MB"
141
+ # 1073741824.bytes_to_s #=> "1.00 GB"
142
+ # 1099511627776.bytes_to_s #=> "1.00 TB"
143
+ #
144
+ # Takes a format string to adjust output.
145
+ #
146
+ # 1024.bytes_to_s('%.0f') #=> "1 KB"
147
+ #
148
+ def strfbytes(fmt='%.2f')
149
+ case
150
+ when self < 1024
151
+ "#{self} bytes"
152
+ when self < 1024**2
153
+ "#{fmt % (self.to_f / 1024)} KB"
154
+ when self < 1024**3
155
+ "#{fmt % (self.to_f / 1024**2)} MB"
156
+ when self < 1024**4
157
+ "#{fmt % (self.to_f / 1024**3)} GB"
158
+ when self < 1024**5
159
+ "#{fmt % (self.to_f / 1024**4)} TB"
160
+ else
161
+ "#{self} bytes"
162
+ end
163
+ end
164
+
165
+ # TODO: deprecate octet_units (?)
166
+ alias_method :octet_units, :strfbytes
167
+
168
+ end#module Numeric
169
+
170
+ end#module Bytes
171
+
172
+ end#module RichUnits
173
+
174
+
175
+ class Numeric #:nodoc:
176
+ include RichUnits::Bytes::Numeric
177
+ end
178
+
@@ -0,0 +1,290 @@
1
+ module RichUnits
2
+
3
+ class Duration
4
+ include Comparable
5
+
6
+ SECOND = 1
7
+ MINUTE = 60 * SECOND
8
+ HOUR = 60 * MINUTE
9
+ DAY = 24 * HOUR
10
+ WEEK = 7 * DAY
11
+ YEAR = 365 * DAY
12
+
13
+ SEGMENTS = %w{years weeks days hours minutes seconds}.collect{ |s| s.to_sym }
14
+
15
+ #
16
+ def self.[](seconds, *segments)
17
+ new(seconds, *segments)
18
+ end
19
+
20
+ #
21
+ def initialize(seconds=0, *segments)
22
+ @seconds = seconds.to_i
23
+ reset_segments(*segments)
24
+ end
25
+
26
+ #
27
+ def segments; @segments; end
28
+
29
+ #
30
+ def reset_segments(*segments)
31
+ case segments.size
32
+ when 0
33
+ @segments = [:days, :hours, :minutes, :seconds]
34
+ when 1
35
+ case segments = segments[0]
36
+ when Array
37
+ @segments = segments.collect{ |p| (p.to_s.downcase.chomp('s') + 's').to_sym }
38
+ raise ArgumentError unless @segments.all?{ |s| SEGMENTS.include?(s) }
39
+ else
40
+ f = SEGMENTS.index(segments)
41
+ @segments = SEGMENTS[f..0]
42
+ end
43
+ when 2
44
+ f = SEGMENTS.index(segments[0])
45
+ t = SEGMENTS.index(segments[1])
46
+ @segments = SEGMENTS[f..t]
47
+ else
48
+ raise ArgumentError
49
+ end
50
+ end
51
+
52
+ def inspect
53
+ h = to_h
54
+ segments.reverse.collect do |l|
55
+ "#{h[l.to_sym]} #{l}"
56
+ end.join(' ')
57
+ end
58
+
59
+ def to_i ; @seconds.to_i ; end
60
+ def to_f ; @seconds.to_f ; end
61
+
62
+ public
63
+
64
+ def to_a
65
+ a, s = [], @seconds
66
+ a[5], s = *s.divmod(YEAR) if @segments.include?(:years)
67
+ a[4], s = *s.divmod(WEEK) if @segments.include?(:weeks)
68
+ a[3], s = *s.divmod(DAY) if @segments.include?(:days)
69
+ a[2], s = *s.divmod(HOUR) if @segments.include?(:hours)
70
+ a[1], s = *s.divmod(MINUTE) if @segments.include?(:minutes)
71
+ a[0], s = *s.divmod(SECOND) if @segments.include?(:seconds)
72
+ a.compact.reverse
73
+ end
74
+
75
+ #
76
+ def to_h
77
+ h, s = {}, @seconds
78
+ h[:years], s = *s.divmod(YEAR) if @segments.include?(:years)
79
+ h[:weeks], s = *s.divmod(WEEK) if @segments.include?(:weeks)
80
+ h[:days], s = *s.divmod(DAY) if @segments.include?(:days)
81
+ h[:hours], s = *s.divmod(HOUR) if @segments.include?(:hours)
82
+ h[:minutes], s = *s.divmod(MINUTE) if @segments.include?(:minutes)
83
+ h[:seconds], s = *s.divmod(SECOND) if @segments.include?(:seconds)
84
+ h
85
+ end
86
+
87
+ def to_s
88
+ h = to_h
89
+ segments.reverse.collect do |l|
90
+ "#{h[l.to_sym]} #{l}"
91
+ end.join(' ')
92
+ end
93
+
94
+ # Returns true if <tt>other</tt> is also a Duration instance with the
95
+ # same <tt>value</tt>, or if <tt>other == value</tt>.
96
+ def ==(other)
97
+ if Duration === other
98
+ other.seconds == seconds
99
+ else
100
+ other == seconds
101
+ end
102
+ end
103
+
104
+ def <=>(other)
105
+ @seconds <=> other.to_i
106
+ end
107
+
108
+ #def is_a?(klass) #:nodoc:
109
+ # klass == self.class
110
+ #end
111
+
112
+ #def self.===(other) #:nodoc:
113
+ # other.is_a?(Duration) rescue super
114
+ #end
115
+
116
+ def years ; to_h[:years] ; end
117
+ def weeks ; to_h[:weeks] ; end
118
+ def days ; to_h[:days] ; end
119
+ def hours ; to_h[:hours] ; end
120
+ def minutes ; to_h[:minutes] ; end
121
+ def seconds ; to_h[:seconds] ; end
122
+
123
+ def total ; seconds ; end
124
+
125
+ def +(other)
126
+ self.class.new(@seconds + other.to_i, segments)
127
+ end
128
+
129
+ def -(other)
130
+ self.class.new(@seconds - other.to_i, segments)
131
+ end
132
+
133
+ def +(other)
134
+ self.class.new(@seconds * other.to_i, segments)
135
+ end
136
+
137
+ def /(other)
138
+ self.class.new(@seconds / other.to_i, segments)
139
+ end
140
+
141
+ #
142
+ def segmented(*segments)
143
+ self.class.new(@seconds, segments)
144
+ #segments = segments.collect{ |p| p.to_s.downcase.chomp('s') }
145
+ #y,w,d,h,m,s = nil,nil,nil,nil,nil,nil
146
+ #x = @seconds
147
+ #y, x = *x.divmod(YEAR) if segments.include?('year')
148
+ #w, x = *x.divmod(WEEK) if segments.include?('week')
149
+ #d, x = *x.divmod(DAY) if segments.include?('day')
150
+ #h, x = *x.divmod(HOUR) if segments.include?('hour')
151
+ #m, x = *x.divmod(MINUTE) if segments.include?('minute')
152
+ #s = x if segments.include?('second')
153
+ #[y, w, d, h, m, s].compact
154
+ end
155
+
156
+ # Format duration.
157
+ #
158
+ # *Identifiers*
159
+ #
160
+ # %w -- Number of weeks
161
+ # %d -- Number of days
162
+ # %h -- Number of hours
163
+ # %m -- Number of minutes
164
+ # %s -- Number of seconds
165
+ # %t -- Total number of seconds
166
+ # %x -- Duration#to_s
167
+ # %% -- Literal `%' character
168
+ #
169
+ # *Example*
170
+ #
171
+ # d = Duration.new(:weeks => 10, :days => 7)
172
+ # => #<Duration: 11 weeks>
173
+ # d.strftime("It's been %w weeks!")
174
+ # => "It's been 11 weeks!"
175
+ #
176
+ def strftime(fmt)
177
+ h = to_h
178
+ hx = {
179
+ 'y' => h[:years] ,
180
+ 'w' => h[:weeks] ,
181
+ 'd' => h[:days] ,
182
+ 'h' => h[:hours] ,
183
+ 'm' => h[:minutes],
184
+ 's' => h[:seconds],
185
+ 't' => total,
186
+ 'x' => to_s
187
+ }
188
+ fmt.gsub(/%?%(w|d|h|m|s|t|x)/) do |match|
189
+ hx[match[1..1]]
190
+ end.gsub('%%', '%')
191
+ end
192
+
193
+ #
194
+ def -@ #:nodoc:
195
+ self.class.new(-@seconds)
196
+ end
197
+
198
+ #
199
+ def +@ #:nodoc:
200
+ self.class.new(+@seconds)
201
+ end
202
+
203
+ #
204
+ # Need to wrap back to numeric methods, maybe use method_missing?
205
+ #
206
+
207
+ #
208
+ def before(time)
209
+ @seconds.before(time)
210
+ end
211
+
212
+ #
213
+ def after(time)
214
+ @seconds.after(time)
215
+ end
216
+
217
+
218
+ # = Numeric Extensions for Durations
219
+ #
220
+ module Numeric
221
+
222
+ # Enables the use of time calculations and declarations,
223
+ # like 45.minutes + 2.hours + 4.years. The base unit for
224
+ # all of these Numeric time methods is seconds.
225
+ def seconds ; Duration[self] ; end
226
+ alias_method :second, :seconds
227
+
228
+ # Converts minutes into seconds.
229
+ def minutes ; Duration[self * 60] ; end
230
+ alias_method :minute, :minutes
231
+
232
+ # Converts hours into seconds.
233
+ def hours ; Duration[self * 3600] ; end
234
+ alias_method :hour, :hours
235
+ #def as_hours ; self / 60.minutes ; end
236
+
237
+ # Converts days into seconds.
238
+ def days ; Duration[self * 86400] ; end
239
+ alias_method :day, :days
240
+
241
+ # Converts weeks into seconds.
242
+ def weeks ; Duration[self * 604800] ; end
243
+ alias_method :week, :weeks
244
+
245
+ # Converts fortnights into seconds.
246
+ # (A fortnight is 2 weeks)
247
+ def fortnights ; Duration[self * 1209600] ; end
248
+ alias_method :fortnight, :fortnights
249
+
250
+ # Converts months into seconds.
251
+ # WARNING: This is not exact as it assumes 30 days to a month.
252
+ def months ; Duration[self * 30 * 86400] ; end
253
+ alias_method :month, :months
254
+
255
+ # Converts years into seconds.
256
+ # WARNING: This is not exact as it assumes 365 days to a year.
257
+ # ie. It doesn not account for leap years.
258
+ def years ; Duration[self * 365 * 86400, :years] ; end
259
+ alias_method :year, :years
260
+
261
+ end
262
+
263
+ # Time#duration has been added to convert the UNIX timestamp into a Duration.
264
+ # See Time#duration for an example.
265
+ #
266
+ module Time
267
+ # Create a Duration object from the UNIX timestamp.
268
+ #
269
+ # *Example*
270
+ #
271
+ # Time.now.duration
272
+ # => #<Duration: 1898 weeks, 6 days, 1 hour, 12 minutes and 1 second>
273
+ #
274
+ def duration
275
+ Duration[to_i]
276
+ end
277
+ end
278
+
279
+ end
280
+
281
+ end
282
+
283
+ class Numeric #:nodoc:
284
+ include RichUnits::Duration::Numeric
285
+ end
286
+
287
+ class Time #:nodoc:
288
+ include RichUnits::Duration::Time
289
+ end
290
+