richunits 0.2.0 → 0.5.0

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.
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
+