blaxter-duration 0.1.4

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.
@@ -0,0 +1,13 @@
1
+ == 0.1.1
2
+
3
+ * Numeric#weeks is actually defined this time
4
+ * Default locales aren't assumed for missing constant definitions in other locales
5
+ * migrated to newgem schema
6
+ * removed README, LICENSE, duration.gemspec
7
+ * added Readme.txt, License.txt, Manifest.txt and other newgem files
8
+ * Duration::VERSION is now a module: Duration::VERSION::{MAJOR,MINOR,TINY,STRING}
9
+ * added ::christmas and ::new_years to Time::Holidays
10
+ * added some docs to duration/localizations/english.rb for translators to read
11
+ * added French and Dutch localizations (thanks to Jean-François, Jean-François, Siep Korteling)
12
+ * added Polish localization (thanks to Marcin Raczkowski)
13
+ * added Duration.version which returns Duration::VERSION::STRING
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2007 Matthew Harris
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,36 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ config/hoe.rb
7
+ config/requirements.rb
8
+ lib/duration.rb
9
+ lib/duration/holidays.rb
10
+ lib/duration/locale.rb
11
+ lib/duration/localizations.rb
12
+ lib/duration/localizations/dutch.rb
13
+ lib/duration/localizations/english.rb
14
+ lib/duration/localizations/french.rb
15
+ lib/duration/localizations/korean.rb
16
+ lib/duration/localizations/polish.rb
17
+ lib/duration/localizations/spanish.rb
18
+ lib/duration/numeric.rb
19
+ lib/duration/time.rb
20
+ lib/duration/time/holidays.rb
21
+ lib/duration/version.rb
22
+ log/debug.log
23
+ script/destroy
24
+ script/generate
25
+ script/txt2html
26
+ setup.rb
27
+ tasks/deployment.rake
28
+ tasks/environment.rake
29
+ tasks/website.rake
30
+ test/test_duration.rb
31
+ test/test_helper.rb
32
+ website/index.html
33
+ website/index.txt
34
+ website/javascripts/rounded_corners_lite.inc.js
35
+ website/stylesheets/screen.css
36
+ website/template.rhtml
@@ -0,0 +1,151 @@
1
+ = Project
2
+ Duration <http://rubyforge.org/projects/duration>
3
+
4
+ = Author
5
+ Matthew Harris <matt@matthewharris.org>
6
+ http://matthewharris.org
7
+
8
+ = Synopsis
9
+ Duration objects are simple mechanisms that allow you to operate on durations
10
+ of time.
11
+
12
+ They allow you to know how much time has passed since a certain
13
+ point in time, or they can tell you how much time something is (when given as
14
+ seconds) in different units of time measurement.
15
+
16
+ Durations would particularly be useful for those scripts or applications that
17
+ allow you to know the uptime of themselves or perhaps provide a countdown until
18
+ a certain event.
19
+
20
+ As of version 0.1.0, the duration library has been completely rewritten with
21
+ better knowledge.
22
+
23
+ = Features
24
+ Duration is packed with many features and has plans for many more.
25
+
26
+ === Current features
27
+ * Duck typing objects to work with Duration
28
+ * Localization support of formatted strings (feel free to submit your own!)
29
+ * Capability to compare durations to any object (duck typing)
30
+ * Arithmetic operations from Duration instances are legal (duck typing)
31
+ * Convenience methods for creating durations from Time objects
32
+
33
+ === Soon to come features
34
+ * Collection of holiday-related methods (creating durations since/until a holiday)
35
+ * A larger collection of locales
36
+ * Definitely have to put more Rubyisms in there too!
37
+ * ...and much more! (can't think of any yet)
38
+
39
+ = Credits
40
+ I want to thank everyone who downloaded duration, tried it, and sent me e-mails
41
+ of appreciation, feedback, bug reports and suggestions.
42
+
43
+ Your gratitude has motivated me to rework this almost-dead library and continue
44
+ to maintain it.
45
+
46
+ Thanks!
47
+
48
+ = Usage
49
+ Using the duration library is fairly simple and straight-forward. The base
50
+ class most of you will be using is the Duration class.
51
+
52
+ === Initialization
53
+ There are two ways to initialize the Duration class. One way is to pass an
54
+ object capable of responding to the #to_i method. That object is expected to
55
+ return an integer or float that represents the number of seconds for the
56
+ duration object.
57
+
58
+ The second way to initialize the Duration class is to pass a key=>value paired
59
+ Hash object. The hash's keys will be scanned for units of measurement defiend
60
+ in Duration::UNITS or (the keys of) Duration::MULTIPLES.
61
+
62
+ Here's an example of using the formerly mentioned solution:
63
+ duration = Duration.new(Time.now)
64
+ => #<Duration: 1981 weeks, 3 hours, 50 minutes and 44 seconds>
65
+
66
+ As you can see, it gave us the duration since the UNIX epoch. This works fine
67
+ because Time objects respond to the #to_i method. And Time#to_i returns a
68
+ UNIX timestamp (which is the number of seconds that have passed since the UNIX
69
+ epoch, January 1, 1970).
70
+
71
+ Before I move on to demonstrating the second solution, I'd like to note that
72
+ there are Time#to_duration, Time#duration and Duration.since_epoch for
73
+ accomplishing similar tasks.
74
+
75
+ Alright, the second solution is to pass in a Hash object, as previously
76
+ mentioned.
77
+
78
+ For example:
79
+ duration = Duration.new(:hours => 12, :minutes => 58, :days => 14)
80
+ => #<Duration: 2 weeks, 12 hours and 58 minutes>
81
+
82
+ So, it works. But also notice that I gave it `:days => 14', but it spat out
83
+ `2 weeks'. Yes, Duration knows what it's doing.
84
+
85
+ There are more keys you can pass. Check all the keys available in the
86
+ Duration::MULTIPLES hash to see. All those keys are valid to pass. Any
87
+ duplicated multiples will merely be added to each other, so be careful if that's
88
+ not the desired behavior.
89
+
90
+ === Formatting
91
+ The Duration class provides Duration#format (alias: Duration#strftime) for
92
+ formatting the time units into a string.
93
+
94
+ For more information on the identifiers it supports, check out Duration#format.
95
+
96
+ Here's an example of how to format the known minutes in the duration:
97
+ duration = Duration.since_epoch
98
+ duration.format('%~m passed: %m')
99
+ => "minutes passed: 4"
100
+
101
+ Now, this may look a bit tricky at first, but it's somewhat similar to the
102
+ standard strftime() function you see in many lanuages, with the exception of the
103
+ weird "%~m" type identifiers. These identifiers are the correct terminology for
104
+ the corresponding time unit.
105
+
106
+ The identifiers are locale-sensitive and can be easily changed to another
107
+ language. Localization is done internally from the duration library, and does
108
+ not use the system's locales.
109
+
110
+ If you would like to write a locale for your language (if it doesn't exist),
111
+ you can simply read the source code of the existing locales lying within the
112
+ "duration/localizations" directory. They are straight-forward 10-15 line
113
+ modules and are not complex at all.
114
+
115
+ === Arithmetic
116
+ Duration objects support arithmetic against any object that response to the
117
+ #to_i method. This is pretty self-explanatory. What happens when you divide
118
+ a duration by 2?
119
+
120
+ duration = Duration.since_epoch
121
+ => #<Duration: 1981 weeks, 4 hours, 12 minutes and 54 seconds>
122
+
123
+ duration / 2
124
+ => #<Duration: 990 weeks, 3 days, 14 hours, 6 minutes and 27 seconds>
125
+
126
+ Pretty simple, right?
127
+
128
+ === Magical Methods
129
+ Finally, Duration has a few magical methods that are handled through the
130
+ #method_missing override. Duration instances support all method calls to such
131
+ methods as `round_<unit>_to_<unit>' and `<unit>_to_<unit>'. These methods
132
+ basically convert their <unit> to another <unit> and spit back the numbers.
133
+
134
+ The `round_<unit>_to_<unit>' method rounds the converted unit.
135
+
136
+ Duration.since_epoch.weeks_to_days
137
+ => 13867.0
138
+
139
+ Duration.since_epoch.round_weeks_to_days
140
+ => 13867
141
+
142
+ Duration.since_epoch.weeks_to_days.round
143
+ => 13867
144
+
145
+ = Feedback
146
+ Well, I hope you learned a lot from the above explanations. Time for you to get
147
+ creative on your own now!
148
+
149
+ If you have any feedback, bug reports, suggestions or locale submissions, just
150
+ e-mail me at matt@matthewharris.org or shugotenshi@gmail.com and I will be glad
151
+ to answer you back.
@@ -0,0 +1,4 @@
1
+ require './config/requirements.rb'
2
+ require './config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
@@ -0,0 +1,73 @@
1
+ require 'duration/version'
2
+
3
+ AUTHOR = 'Matthew Harris' # can also be an array of Authors
4
+ EMAIL = "matt@matthewharris.org"
5
+ DESCRIPTION = %[
6
+ Duration is a library for manipulating timespans. It can give you readable
7
+ output for a timespan as well as manipulate the timespan itself. With this
8
+ it is possible to make "countdowns" or "time passed since" type objects.
9
+ ]
10
+ GEM_NAME = 'blaxter-duration' # what ppl will type to install your gem
11
+ RUBYFORGE_PROJECT = 'duration' # The unix name for your project
12
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
13
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
14
+
15
+ @config_file = "~/.rubyforge/user-config.yml"
16
+ @config = nil
17
+ RUBYFORGE_USERNAME = "kuja"
18
+ def rubyforge_username
19
+ unless @config
20
+ begin
21
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
22
+ rescue
23
+ puts <<-EOS
24
+ ERROR: No rubyforge config file found: #{@config_file}
25
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
26
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
27
+ EOS
28
+ exit
29
+ end
30
+ end
31
+ RUBYFORGE_USERNAME.replace @config["username"]
32
+ end
33
+
34
+
35
+ REV = nil
36
+ # UNCOMMENT IF REQUIRED:
37
+ # REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
38
+ VERS = Duration::VERSION::STRING + (REV ? ".#{REV}" : "")
39
+ RDOC_OPTS = ['--quiet', '--title', 'duration documentation',
40
+ "--opname", "index.html",
41
+ "--line-numbers",
42
+ "--main", "README.txt",
43
+ '--exclude', 'index.txt',
44
+ "--inline-source"]
45
+
46
+ class Hoe
47
+ def extra_deps
48
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
49
+ @extra_deps
50
+ end
51
+ end
52
+
53
+ # Generate all the Rake tasks
54
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
55
+ hoe = Hoe.spec GEM_NAME do
56
+ self.author = AUTHOR
57
+ self.description = DESCRIPTION
58
+ self.email = EMAIL
59
+ self.summary = DESCRIPTION
60
+ self.url = HOMEPATH
61
+ self.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
62
+ self.test_globs = ["test/**/test_*.rb"]
63
+ self.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store']
64
+ self.version = VERS
65
+
66
+ # == Optional
67
+ self.changes = self.paragraphs_of("History.txt", 0..1).join("\n\n")
68
+ end
69
+
70
+ CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
71
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
72
+ hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
73
+ hoe.rsync_args = '-av --delete --ignore-errors'
@@ -0,0 +1,17 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
16
+
17
+ require 'duration'
@@ -0,0 +1,216 @@
1
+ require 'duration/version'
2
+ require 'duration/numeric'
3
+ require 'duration/time'
4
+ require 'duration/localizations'
5
+ Duration::Localizations.load_all
6
+
7
+ # Duration objects are simple mechanisms that allow you to operate on durations
8
+ # of time. They allow you to know how much time has passed since a certain
9
+ # point in time, or they can tell you how much time something is (when given as
10
+ # seconds) in different units of time measurement. Durations would particularly
11
+ # be useful for those scripts or applications that allow you to know the uptime
12
+ # of themselves or perhaps provide a countdown until a certain event.
13
+ class Duration
14
+ include Comparable
15
+ include Enumerable
16
+
17
+ # Unit multiples
18
+ MULTIPLES = {
19
+ :seconds => 1,
20
+ :minutes => 60,
21
+ :hours => 3600,
22
+ :days => 86400,
23
+ :weeks => 604800,
24
+ :second => 1,
25
+ :minute => 60,
26
+ :hour => 3600,
27
+ :day => 86400,
28
+ :week => 604800
29
+ }
30
+
31
+ # Unit names
32
+ UNITS = [:seconds, :minutes, :hours, :days, :weeks]
33
+
34
+ attr_reader :total, :seconds, :minutes, :hours, :days, :weeks
35
+
36
+ # Change the locale Duration will use when converting itself to a string
37
+ def Duration.change_locale(locale)
38
+ @@locale = Localizations.locales[locale.to_sym] or raise LocaleError, "undefined locale '#{locale}'"
39
+ end
40
+
41
+ # Load default locale
42
+ change_locale(Localizations::DEFAULT_LOCALE)
43
+
44
+ # Constructs a Duration instance that represents the duration since the UNIX
45
+ # epoch (1970-01-01T00:00:00Z)
46
+ def Duration.since_epoch
47
+ new(Time.now)
48
+ end
49
+
50
+ # Calculates the duration "since" a given time. Assumes that the time is in
51
+ # the past. Does not error if the time is in the present or future.
52
+ def Duration.since(time)
53
+ new(Time.now.to_i - time.to_i)
54
+ end
55
+
56
+ # Calculates the duration "until" a given time. Assumes that the time is in
57
+ # the future. Does not error if a time in the present or past is given.
58
+ def Duration.until(time)
59
+ new(time.to_i - Time.now.to_i)
60
+ end
61
+
62
+ # Initialize a duration. `args' can be a hash or anything else. If a hash is
63
+ # passed, it will be scanned for a key=>value pair of time units such as those
64
+ # listed in the Duration::UNITS array or Duration::MULTIPLES hash.
65
+ #
66
+ # If anything else except a hash is passed, #to_i is invoked on that object
67
+ # and expects that it return the number of seconds desired for the duration.
68
+ def initialize(args = 0)
69
+ # Two types of arguments are accepted. If it isn't a hash, it's converted
70
+ # to an integer.
71
+ if args.kind_of?(Hash)
72
+ @seconds = 0
73
+ MULTIPLES.each do |unit, multiple|
74
+ unit = unit.to_sym
75
+ @seconds += args[unit] * multiple if args.key?(unit)
76
+ end
77
+ else
78
+ @seconds = args.to_i
79
+ end
80
+
81
+ # Calculate duration
82
+ calculate!
83
+ end
84
+
85
+ # Calculates the duration from seconds and figures out what the actual
86
+ # durations are in specific units. This method is called internally, and
87
+ # does not need to be called by user code.
88
+ def calculate!
89
+ multiples = [MULTIPLES[:weeks], MULTIPLES[:days], MULTIPLES[:hours], MULTIPLES[:minutes], MULTIPLES[:seconds]]
90
+ units = []
91
+ @total = @seconds.to_f.round
92
+ multiples.inject(@total) do |total, multiple|
93
+ # Divide into largest unit
94
+ units << total / multiple
95
+ total % multiple # The remainder will be divided as the next largest
96
+ end
97
+
98
+ # Gather the divided units
99
+ @weeks, @days, @hours, @minutes, @seconds = units
100
+ end
101
+
102
+ # Compare this duration to another (or objects that respond to #to_i)
103
+ def <=>(other)
104
+ @total <=> other.to_i
105
+ end
106
+
107
+ # Convenient iterator for going through each duration unit from lowest to
108
+ # highest. (Goes from seconds...weeks)
109
+ def each
110
+ UNITS.each { |unit| yield unit, __send__(unit) }
111
+ end
112
+
113
+ # Format a duration into a human-readable string.
114
+ #
115
+ # %w => weeks
116
+ # %d => days
117
+ # %h => hours
118
+ # %m => minutes
119
+ # %s => seconds
120
+ # %t => total seconds
121
+ # %H => zero-padded hours
122
+ # %M => zero-padded minutes
123
+ # %S => zero-padded seconds
124
+ # %~s => locale-dependent "seconds" terminology
125
+ # %~m => locale-dependent "minutes" terminology
126
+ # %~h => locale-dependent "hours" terminology
127
+ # %~d => locale-dependent "days" terminology
128
+ # %~w => locale-dependent "weeks" terminology
129
+ #
130
+ def format(format_str)
131
+ identifiers = {
132
+ 'w' => @weeks,
133
+ 'd' => @days,
134
+ 'h' => @hours,
135
+ 'm' => @minutes,
136
+ 's' => @seconds,
137
+ 't' => @total,
138
+ 'H' => @hours.to_s.rjust(2, '0'),
139
+ 'M' => @minutes.to_s.rjust(2, '0'),
140
+ 'S' => @seconds.to_s.rjust(2, '0'),
141
+ '~s' => @seconds == 1 ? @@locale.singulars[0] : @@locale.plurals[0],
142
+ '~m' => @minutes == 1 ? @@locale.singulars[1] : @@locale.plurals[1],
143
+ '~h' => @hours == 1 ? @@locale.singulars[2] : @@locale.plurals[2],
144
+ '~d' => @days == 1 ? @@locale.singulars[3] : @@locale.plurals[3],
145
+ '~w' => @weeks == 1 ? @@locale.singulars[4] : @@locale.plurals[4]
146
+ }
147
+
148
+ format_str.gsub(/%?%(w|d|h|m|s|t|H|M|S|~(?:s|m|h|d|w))/) do |match|
149
+ match['%%'] ? match : identifiers[match[1..-1]]
150
+ end.gsub('%%', '%')
151
+ end
152
+
153
+ def method_missing(m, *args, &block)
154
+ units = UNITS.join('|')
155
+ match = /(round_)?(#{units})_to_(#{units})$/.match(m.to_s)
156
+ if match
157
+ seconds = ((__send__(match[2].to_sym) * MULTIPLES[match[2].to_sym]) / MULTIPLES[match[3].to_sym].to_f)
158
+ match[1] ? seconds.round : seconds
159
+ else
160
+ super
161
+ end
162
+ end
163
+
164
+ # String representation of the Duration object.
165
+ def to_s
166
+ @@locale.format.call(self)
167
+ end
168
+
169
+ # Inspect Duration object.
170
+ def inspect
171
+ "#<#{self.class}: #{(s = to_s).empty? ? '...' : s}>"
172
+ end
173
+
174
+ def +(other)
175
+ Duration.new(@total + other.to_i)
176
+ end
177
+
178
+ def -(other)
179
+ Duration.new(@total - other.to_i)
180
+ end
181
+
182
+ def *(other)
183
+ Duration.new(@total * other.to_i)
184
+ end
185
+
186
+ def /(other)
187
+ Duration.new(@total / other.to_i)
188
+ end
189
+
190
+ def %(other)
191
+ Duration.new(@total % other.to_i)
192
+ end
193
+
194
+ def seconds=(seconds)
195
+ initialize :seconds => (@total + seconds) - @seconds
196
+ end
197
+
198
+ def minutes=(minutes)
199
+ initialize :seconds => @total - minutes_to_seconds, :minutes => minutes
200
+ end
201
+
202
+ def hours=(hours)
203
+ initialize :seconds => @total - hours_to_seconds, :hours => hours
204
+ end
205
+
206
+ def days=(days)
207
+ initialize :seconds => @total - days_to_seconds, :days => days
208
+ end
209
+
210
+ def weeks=(weeks)
211
+ initialize :seconds => @total - weeks_to_seconds, :weeks => weeks
212
+ end
213
+
214
+ alias_method :to_i, :total
215
+ alias_method :strftime, :format
216
+ end