blaxter-duration 0.1.4

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