34 Commits

Author SHA1 Message Date
0bef341111 Merge branch 'release/v0.2.8' 2010-11-24 13:22:07 +00:00
6686731651 Bumped version to 0.2.7 2010-11-24 13:21:57 +00:00
fceb7b8ff2 switched to using bundle's own gemspec format
instead of jeweler
2010-11-24 13:19:04 +00:00
803ad03d6c Merge branch 'release/v0.2.7' into dev 2010-11-24 09:48:29 +00:00
b29c232939 Merge branch 'release/v0.2.7' 2010-11-24 09:48:23 +00:00
f3b9d3751a Version bump to 0.2.7 2010-11-24 09:48:10 +00:00
a612a17c54 added a Gemfile 2010-11-24 09:47:55 +00:00
71c5421587 added i18n to dependencies as activesupport needs
it, but doesn't have it as a dependency
2010-11-24 09:47:37 +00:00
d564c31b93 updated project to rspec 2.x 2010-11-24 09:46:31 +00:00
29e1c7f500 Merge branch 'release-0.2.6' into stable 2010-09-28 19:18:09 +03:00
f49c6bd455 Version bump to 0.2.6 2010-09-28 19:17:19 +03:00
5ece10846a added spec to check for core extention method
aliases
2010-09-28 19:16:36 +03:00
5afb6bc2b9 removed last traces of Time#map method which was
causing issues with Active Record
2010-09-28 19:16:11 +03:00
9bbd974a75 fixed compatibility with Active Support 3.x 2010-09-28 19:15:39 +03:00
ca929c6ae1 Merge branch 'release-0.2.5' into stable 2010-08-23 12:41:45 +03:00
edd828bb3e Version bump to 0.2.5 2010-08-23 12:41:23 +03:00
4afd3f84d5 removed Time#map alias to Time#map_each as it was
causing issues with ActiveRecord
2010-08-23 12:40:58 +03:00
ea6d87e35f Merge branch 'release-0.2.4' into stable 2010-08-11 13:52:07 +03:00
74574beaf0 Version bump to 0.2.4 2010-08-11 13:51:50 +03:00
ba33dfec57 added min and sec aliases to minutes and seconds
methods respectively to Numeric for unit
consitency
2010-08-11 13:51:21 +03:00
c9cd80fd9e Merge branch 'release-0.2.3' into stable 2010-08-05 13:01:03 +03:00
06e167d1ab Version bump to 0.2.3 2010-08-05 13:00:05 +03:00
ac1ad2dba4 big oops, yes, I suck... 2010-08-05 12:56:26 +03:00
ab6e1e8003 Merge branch 'release-0.2.2' into stable 2010-08-05 12:50:14 +03:00
3d2b5d0b9e Version bump to 0.2.2 2010-08-05 12:49:49 +03:00
b8ba272b95 added include_end option to #iterate 2010-08-04 18:26:14 +03:00
3201485eaf Added #of_the / #of range modifier which is used
like #until and #from. Except #of_the makes the
iteration walk through each unit of time passed to
the #each / #map method, from the beginning till
end of unit passed to #of_the.

Example:
Time.now.each_hour.of_the(:month) { |t puts t }
#=> iterates over each over of the whole current month
2010-08-04 17:58:58 +03:00
77d9d67ed1 merged MethodChain and BackwardsCompatibility
modules into a single TimeExt::Support module
2010-08-04 17:28:22 +03:00
e8d7d9d47b Merge branch 'release-0.2.1' into stable 2010-08-03 14:09:08 +03:00
40b86cafc0 Version bump to 0.2.1 2010-08-03 14:08:36 +03:00
66f661aef8 created a #map alias for #map_each 2010-08-03 14:07:58 +03:00
c1abca091a Merge branches 'master' and 'dev' 2010-08-03 14:04:03 +03:00
e6bd7fc000 restructured classes/modules and files 2010-08-03 14:03:32 +03:00
b3eb247e50 fixed a typo in the readme 2010-07-31 10:09:38 +03:00
22 changed files with 498 additions and 358 deletions

7
.gitignore vendored
View File

@@ -16,9 +16,10 @@ tmtags
## PROJECT::GENERAL ## PROJECT::GENERAL
coverage coverage
rdoc rdoc
pkg pkg/*
*.gem
.bundle
## PROJECT::SPECIFIC ## PROJECT::SPECIFIC
.yardoc/* .yardoc
*.gemspec
doc/* doc/*

2
.rspec Normal file
View File

@@ -0,0 +1,2 @@
--format documentation
--color

9
Gemfile Normal file
View File

@@ -0,0 +1,9 @@
source 'http://rubygems.org/'
# Specify your gem's dependencies in time_ext.gemspec
gemspec
group :development do
gem 'rspec', '>= 2.1.0'
gem 'yard', '>= 0.6.3'
end

32
Gemfile.lock Normal file
View File

@@ -0,0 +1,32 @@
PATH
remote: .
specs:
time_ext (0.2.7)
activesupport (>= 2.3.0)
i18n (>= 0.4.2)
GEM
remote: http://rubygems.org/
specs:
activesupport (3.0.3)
diff-lcs (1.1.2)
i18n (0.4.2)
rspec (2.1.0)
rspec-core (~> 2.1.0)
rspec-expectations (~> 2.1.0)
rspec-mocks (~> 2.1.0)
rspec-core (2.1.0)
rspec-expectations (2.1.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.1.0)
yard (0.6.3)
PLATFORMS
ruby
DEPENDENCIES
activesupport (>= 2.3.0)
i18n (>= 0.4.2)
rspec (>= 2.1.0)
time_ext!
yard (>= 0.6.3)

View File

@@ -14,7 +14,7 @@ This gem extends the abilities of Ruby's built-in Time class by building on top
require "time/ext" # or "time_ext" require "time/ext" # or "time_ext"
Time.utc(2010, 6, 19).round(:month) #=> Thu Jul 01 00:00:00 UTC 2010 Time.utc(2010, 6, 19).round(:month) #=> Thu Jul 01 00:00:00 UTC 2010
#=> Beginning of this week, or next week depending on which date is closest #=> Beginning of this month, or beginning of next month depending on which is closest
Time.now.each_hour.until(6.hours.from_now) { |t| puts t.to_s } Time.now.each_hour.until(6.hours.from_now) { |t| puts t.to_s }
#=> Prints the time at one hour interals from now till 6 hours from now #=> Prints the time at one hour interals from now till 6 hours from now

View File

@@ -1,43 +1,27 @@
require 'rubygems' require 'bundler'
require 'rake' Bundler::GemHelper.install_tasks
begin
require 'jeweler' #
Jeweler::Tasks.new do |gem| # Rspec
gem.name = "time_ext" #
gem.summary = %Q{Extends the abilities of Ruby's built-in Time class by building on top of ActiveSupport.}
gem.description = %Q{Extends the abilities of Ruby's built-in Time class by building on top of ActiveSupport.} require 'rspec/core/rake_task'
gem.email = "contact@jimeh.me" RSpec::Core::RakeTask.new(:spec) do |spec|
gem.homepage = "http://github.com/jimeh/time_ext" spec.pattern = 'spec/**/*_spec.rb'
gem.authors = ["Jim Myhrberg"]
gem.add_dependency "activesupport", ">= 2.3.0"
gem.add_development_dependency "rspec", ">= 1.2.9"
gem.add_development_dependency "yard", ">= 0"
end
Jeweler::GemcutterTasks.new
rescue LoadError
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
end end
require 'spec/rake/spectask' RSpec::Core::RakeTask.new(:rcov) do |spec|
Spec::Rake::SpecTask.new(:spec) do |spec|
spec.libs << 'lib' << 'spec'
spec.spec_files = FileList['spec/**/*_spec.rb']
end
Spec::Rake::SpecTask.new(:rcov) do |spec|
spec.libs << 'lib' << 'spec'
spec.pattern = 'spec/**/*_spec.rb' spec.pattern = 'spec/**/*_spec.rb'
spec.rcov = true spec.rcov = true
end end
task :spec => :check_dependencies
task :default => :spec task :default => :spec
task :console do
exec "irb -r spec/spec_helper" #
end # Yard
#
begin begin
require 'yard' require 'yard'
@@ -47,3 +31,14 @@ rescue LoadError
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard" abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
end end
end end
#
# Misc.
#
desc "Start an irb console with TimeExt pre-loaded."
task :console do
exec "irb -r spec/spec_helper"
end
task :c => :console

View File

@@ -1 +0,0 @@
0.2.0

View File

@@ -1,13 +1,11 @@
require 'rubygems' require 'rubygems'
require 'active_support' require 'active_support'
require 'time_ext/method_chain'
require 'time_ext/core_ext/time/calculations'
require 'time_ext/core_ext/time/iterations'
module TimeExt # support both Active Support 2.x and 3.x
require 'active_support/time' if !Time.respond_to?(:days_in_month)
module Base
TIME_EXT_UNITS = [:year, :month, :day, :hour, :min, :sec, :usec] require 'time_ext/calculations'
end require 'time_ext/iterations'
require 'time_ext/support'
end require 'time_ext/core_ext/time'
require 'time_ext/core_ext/numeric'

View File

@@ -0,0 +1,193 @@
module TimeExt
# Adds an even greater extent of calculation methods on top of those already provided by ActiveSupport.
module Calculations
# Returns a new Time representing the start of the unit specified (second by default).
def floor(unit = :sec)
self.send("beginning_of_#{unit}")
end
alias :beginning_of :floor
alias :at_beginning_of :floor
# Returns a new Time representing the start of the next unit specified (second by default).
def ceil(unit = :sec)
self.send("next_#{unit}").send("beginning_of_#{unit}")
end
alias :beginning_of_next :ceil
alias :at_beginning_of_next :ceil
# Returns a new Time representing the start of the current or next unit specified (second by default) depending which is closest
def round(unit = :sec)
next_unit = self.ceil(unit)
this_unit = self.floor(unit)
(self - this_unit) < (next_unit - self) ? this_unit : next_unit
end
alias :beginning_of_closest :round
# Returns a new Time representing the previoius unit specified (defaults to second).
def prev(unit = :sec)
send("prev_#{unit}")
end
# Returns a new Time representing the next unit specified (defaults to second).
def next(unit = :sec)
send("next_#{unit}")
end
# Short-hand for seconds_ago(1).
def prev_second
seconds_ago(1)
end
alias :prev_sec :prev_second
# Short-hand for seconds_since(1).
def next_second
seconds_since(1)
end
alias :next_sec :next_second
# Short-hand for minutes_ago(1).
def prev_minute
minutes_ago(1)
end
alias :prev_min :prev_minute
# Short-hand for minutes_since(1).
def next_minute
minutes_since(1)
end
alias :next_min :next_minute
# Short-hand for hours_ago(1).
def prev_hour
hours_ago(1)
end
# Short-hand for hours_since(1).
def next_hour
hours_since(1)
end
# Short-hand for days_ago(1).
def prev_day
days_ago(1)
end
# Short-hand for days_since(1).
def next_day
days_since(1)
end
# Returns a new Time representing the start of the given day in the previous week (default is Monday).
def prev_week(day = :monday)
weeks_ago(1).beginning_of_week.since(days_into_week[day].day).change(:hour => 0)
end
# Short-hand for quarters_since(1).beginning_of_quarter.
def next_quarter
quarters_since(1).beginning_of_quarter
end
# Short-hand for quarters_ago(1).beginning_of_quarter.
def prev_quarter
quarters_ago(1).beginning_of_quarter
end
# Returns a new Time representing the time a number of specified minutes ago.
def minutes_ago(minutes)
ago(minutes.minutes)
end
alias :mins_ago :minutes_ago
# Returns a new Time representing the time a number of specified minutes in the future.
def minutes_since(minutes)
since(minutes.minutes)
end
alias :mins_since :minutes_since
# Returns a new Time representing the time a number of specified hours ago.
def hours_ago(hours)
ago(hours.hours)
end
# Returns a new Time representing the time a number of specified hours in the future.
def hours_since(hours)
since(hours.hours)
end
# Returns a new Time representing the time a number of specified days ago.
def days_ago(days)
ago(days.days)
end
# Returns a new Time representing the time a number of specified days in the future.
def days_since(days)
since(days.days)
end
# Returns a new Time representing the time a number of specified weeks ago.
def weeks_ago(weeks)
ago(weeks.weeks)
end
# Returns a new Time representing the time a number of specified weeks in the future.
def weeks_since(weeks)
since(weeks.weeks)
end
# Returns a new Time representing the time a number of specified quarters (3 months) ago.
def quarters_ago(quarters)
ago((quarters * 3).months)
end
# Returns a new Time representing the time a number of specified quarters (3 months) in the future.
def quarters_since(quarters)
since((quarters * 3).months)
end
# Returns a new Time representing the end of the unit specified (defaults to second).
def end_of(unit = :sec)
send("end_of_#{unit}")
end
# Returns a new Time representing the start of the second, XX:XX:XX.000000 (.000000000 in ruby1.9).
def beginning_of_second
change(:usec => 0)
end
alias :beginning_of_sec :beginning_of_second
alias :at_beginning_of_sec :beginning_of_second
alias :at_beginning_of_second :beginning_of_second
# Returns a new Time representing the end of the hour, XX:XX:XX.999999 (.999999999 in ruby1.9).
def end_of_second
change(:usec => 999999.999)
end
alias :end_of_sec :end_of_second
# Returns a new Time representing the start of the minute (XX:XX:00).
def beginning_of_minute
change(:sec => 0, :usec => 0)
end
alias :beginning_of_min :beginning_of_minute
alias :at_beginning_of_min :beginning_of_minute
alias :at_beginning_of_minute :beginning_of_minute
# Returns a new Time representing the end of the hour, XX:XX:59.999999 (.999999999 in ruby1.9).
def end_of_minute
change(:sec => 59, :usec => 999999.999)
end
alias :end_of_min :end_of_minute
# Returns a new Time representing the start of the hour (XX:00:00).
def beginning_of_hour
change(:min => 0, :sec => 0, :usec => 0)
end
alias :at_beginning_of_hour :beginning_of_hour
# Returns a new Time representing the end of the hour, XX:59:59.999999 (.999999999 in ruby1.9).
def end_of_hour
change(:min => 59, :sec => 59, :usec => 999999.999)
end
end
end

View File

@@ -0,0 +1,4 @@
class Numeric
alias :sec :seconds
alias :min :minutes
end

View File

@@ -0,0 +1,11 @@
class Time
include TimeExt::Support
include TimeExt::Calculations
include TimeExt::Iterations
# Aliases to keep available method names to a standard pattern.
alias :secs_ago :ago
alias :seconds_ago :ago
alias :secs_since :since
alias :seconds_since :since
end

View File

@@ -1,206 +0,0 @@
class Time
# Helper method for backwards compatibility with ActiveSupport.
def days_into_week
defined?(DAYS_INTO_WEEK) ? DAYS_INTO_WEEK : { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6 }
end
# Helper method for backwards compatibility with ActiveSupport.
def common_year_days_in_month
defined?(COMMON_YEAR_DAYS_IN_MONTH) ? COMMON_YEAR_DAYS_IN_MONTH : [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
end
# Returns a new Time representing the start of the unit specified (second by default).
def floor(unit = :sec)
self.send("beginning_of_#{unit}")
end
alias :beginning_of :floor
alias :at_beginning_of :floor
# Returns a new Time representing the start of the next unit specified (second by default).
def ceil(unit = :sec)
self.send("next_#{unit}").send("beginning_of_#{unit}")
end
alias :beginning_of_next :ceil
alias :at_beginning_of_next :ceil
# Returns a new Time representing the start of the current or next unit specified (second by default) depending which is closest
def round(unit = :sec)
next_unit = self.ceil(unit)
this_unit = self.floor(unit)
(self - this_unit) < (next_unit - self) ? this_unit : next_unit
end
alias :beginning_of_closest :round
# Returns a new Time representing the previoius unit specified (defaults to second).
def prev(unit = :sec)
send("prev_#{unit}")
end
# Returns a new Time representing the next unit specified (defaults to second).
def next(unit = :sec)
send("next_#{unit}")
end
# Short-hand for seconds_ago(1).
def prev_second
seconds_ago(1)
end
alias :prev_sec :prev_second
# Short-hand for seconds_since(1).
def next_second
seconds_since(1)
end
alias :next_sec :next_second
# Short-hand for minutes_ago(1).
def prev_minute
minutes_ago(1)
end
alias :prev_min :prev_minute
# Short-hand for minutes_since(1).
def next_minute
minutes_since(1)
end
alias :next_min :next_minute
# Short-hand for hours_ago(1).
def prev_hour
hours_ago(1)
end
# Short-hand for hours_since(1).
def next_hour
hours_since(1)
end
# Short-hand for days_ago(1).
def prev_day
days_ago(1)
end
# Short-hand for days_since(1).
def next_day
days_since(1)
end
# Returns a new Time representing the start of the given day in the previous week (default is Monday).
def prev_week(day = :monday)
weeks_ago(1).beginning_of_week.since(days_into_week[day].day).change(:hour => 0)
end
# Short-hand for quarters_since(1).beginning_of_quarter.
def next_quarter
quarters_since(1).beginning_of_quarter
end
# Short-hand for quarters_ago(1).beginning_of_quarter.
def prev_quarter
quarters_ago(1).beginning_of_quarter
end
# Aliases to keep available method names to a standard pattern.
alias :secs_ago :ago
alias :seconds_ago :ago
alias :secs_since :since
alias :seconds_since :since
# Returns a new Time representing the time a number of specified minutes ago.
def minutes_ago(minutes)
ago(minutes.minutes)
end
alias :mins_ago :minutes_ago
# Returns a new Time representing the time a number of specified minutes in the future.
def minutes_since(minutes)
since(minutes.minutes)
end
alias :mins_since :minutes_since
# Returns a new Time representing the time a number of specified hours ago.
def hours_ago(hours)
ago(hours.hours)
end
# Returns a new Time representing the time a number of specified hours in the future.
def hours_since(hours)
since(hours.hours)
end
# Returns a new Time representing the time a number of specified days ago.
def days_ago(days)
ago(days.days)
end
# Returns a new Time representing the time a number of specified days in the future.
def days_since(days)
since(days.days)
end
# Returns a new Time representing the time a number of specified weeks ago.
def weeks_ago(weeks)
ago(weeks.weeks)
end
# Returns a new Time representing the time a number of specified weeks in the future.
def weeks_since(weeks)
since(weeks.weeks)
end
# Returns a new Time representing the time a number of specified quarters (3 months) ago.
def quarters_ago(quarters)
ago((quarters * 3).months)
end
# Returns a new Time representing the time a number of specified quarters (3 months) in the future.
def quarters_since(quarters)
since((quarters * 3).months)
end
# Returns a new Time representing the end of the unit specified (defaults to second).
def end_of(unit = :sec)
send("end_of_#{unit}")
end
# Returns a new Time representing the start of the second, XX:XX:XX.000000 (.000000000 in ruby1.9).
def beginning_of_second
change(:usec => 0)
end
alias :beginning_of_sec :beginning_of_second
alias :at_beginning_of_sec :beginning_of_second
alias :at_beginning_of_second :beginning_of_second
# Returns a new Time representing the end of the hour, XX:XX:XX.999999 (.999999999 in ruby1.9).
def end_of_second
change(:usec => 999999.999)
end
alias :end_of_sec :end_of_second
# Returns a new Time representing the start of the minute (XX:XX:00).
def beginning_of_minute
change(:sec => 0, :usec => 0)
end
alias :beginning_of_min :beginning_of_minute
alias :at_beginning_of_min :beginning_of_minute
alias :at_beginning_of_minute :beginning_of_minute
# Returns a new Time representing the end of the hour, XX:XX:59.999999 (.999999999 in ruby1.9).
def end_of_minute
change(:sec => 59, :usec => 999999.999)
end
alias :end_of_min :end_of_minute
# Returns a new Time representing the start of the hour (XX:00:00).
def beginning_of_hour
change(:min => 0, :sec => 0, :usec => 0)
end
alias :at_beginning_of_hour :beginning_of_hour
# Returns a new Time representing the end of the hour, XX:59:59.999999 (.999999999 in ruby1.9).
def end_of_hour
change(:min => 59, :sec => 59, :usec => 999999.999)
end
end

View File

@@ -1,79 +0,0 @@
class Time
include TimeExt::MethodChain
# Used by #each, #map_each and similar methods to iterate over ranges of time.
def iterate(unit, options = {}, &block)
options.reverse_merge!(:map_result => false, :beginning_of => false, :include_start => false)
if block_given?
units = [:year, :month, :day, :hour, :min, :sec, :usec]
parent_unit = units[units.index(unit)-1]
@until ||= (!parent_unit.nil?) ? self.send("#{parent_unit}s_since", 1) : self.send("#{unit}s_since", 1)
time = self.clone
direction = (self < @until) ? :f : :b
succ_method = (direction == :f) ? "next_#{unit}" : "prev_#{unit}"
time = time.beginning_of(unit) if options[:beginning_of]
time = time.send(succ_method) if !options[:include_start]
results = []
while (direction == :f && time <= @until) || (direction == :b && time >= @until)
options[:map_result] ? results << yield(time) : yield(time)
time = time.send(succ_method)
end
options[:map_result] ? results : self
else
add_to_chain(:iterate, unit, options)
self
end
end
# Used togeter with #each and other iteration methods to specify end of interation.
def until(time, &block)
time = time.to_time if time.is_a?(::Date)
@until = time
return call_chain(block) if block_given?
self
end
alias :till :until
# Used together with #each and other interation methods to specify start of iteration, and end will be current object.
def from(time, &block)
time = time.to_time if time.is_a?(::Date)
method, args = @method_chain.pop if block_given?
if !method.nil?
time.until(self).send(method, *args, &block)
else
time.until(self)
end
end
# Executes passed block for each "unit" of time specified, with a new time object for each interval passed to the block.
def each(unit, options = {}, &block)
iterate(unit, options.merge(:map_result => false), &block)
end
# Executes passed block for each "unit" of time specified, with a new time object set to the beginning of "unit" for each interval passed to the block.
def beginning_of_each(unit, options = {}, &block)
iterate(unit, options.merge(:map_result => false, :beginning_of => true), &block)
end
# Executes passed block for each "unit" of time specified, returning an array with the return values from the passed block.
def map_each(unit, options = {}, &block)
iterate(unit, options.merge(:map_result => true), &block)
end
# Executes passed block for each "unit" of time specified, returning an array with the return values from passed block. Additionally the time object passed into the block is set to the beginning of specified "unit".
def map_beginning_of_each(unit, options = {}, &block)
iterate(unit, options.merge(:map_result => true, :beginning_of => true), &block)
end
# Dynamically define convenience methods, like #each_hour instead of #each(:hour).
[:year, :month, :day, :hour, :min, :sec].each do |unit|
[:each, :beginning_of_each, :map_each, :map_beginning_of_each].each do |method|
define_method "#{method}_#{unit}" do |*args, &block|
send(method, unit, *args, &block)
end
class_eval { alias :"#{method}_minute" :"#{method}_min" } if unit == :min
class_eval { alias :"#{method}_second" :"#{method}_sec" } if unit == :sec
end
end
end

103
lib/time_ext/iterations.rb Normal file
View File

@@ -0,0 +1,103 @@
module TimeExt
# Allows you to iterate over Time objects with #each and other methods almost as if it was an Array or Hash.
module Iterations
# Used by #each, #map_each and similar methods to iterate over ranges of time.
def iterate(unit, options = {}, &block)
options.reverse_merge!(:map_result => false, :beginning_of => false, :include_start => false, :include_end => true)
if block_given?
units = [:year, :month, :day, :hour, :min, :sec, :usec]
parent_unit = units[units.index(unit)-1]
if @of_the.nil?
time = self.clone
@until ||= (!parent_unit.nil?) ? self.send("#{parent_unit}s_since", 1) : self.send("#{unit}s_since", 1)
else
time = self.beginning_of(@of_the)
@until = self.next(@of_the).beginning_of(@of_the)
options.merge!(:beginning_of => true, :include_start => true, :include_end => false)
end
direction = (self < @until) ? :f : :b
succ_method = (direction == :f) ? "next_#{unit}" : "prev_#{unit}"
time = time.beginning_of(unit) if options[:beginning_of]
time = time.send(succ_method) if !options[:include_start]
@until = @until.prev(unit).end_of(unit) if !options[:include_end]
results = []
while (direction == :f && time <= @until) || (direction == :b && time >= @until)
options[:map_result] ? results << yield(time) : yield(time)
time = time.send(succ_method)
end
options[:map_result] ? results : self
else
add_to_chain(:iterate, unit, options)
self
end
end
# Used togeter with #each and other iteration methods to specify end of interation.
def until(time, &block)
time = time.to_time if time.is_a?(::Date)
@until = time
return call_chain(block) if block_given?
self
end
alias :till :until
# Used together with #each and other interation methods to specify start of iteration, and end will be current object.
def from(time, &block)
time = time.to_time if time.is_a?(::Date)
method, args = @method_chain.pop if block_given?
if !method.nil?
time.until(self).send(method, *args, &block)
else
time.until(self)
end
end
# Let's you iterate over every unit specified in the #each or #map call for the specified unit.
def of_the(unit, &block)
@of_the = unit
return call_chain(block) if block_given?
self
end
alias :of :of_the
# Executes passed block for each "unit" of time specified, with a new time object for each interval passed to the block.
def each(unit, options = {}, &block)
iterate(unit, options.merge(:map_result => false), &block)
end
# Executes passed block for each "unit" of time specified, with a new time object set to the beginning of "unit" for each interval passed to the block.
def beginning_of_each(unit, options = {}, &block)
iterate(unit, options.merge(:map_result => false, :beginning_of => true), &block)
end
# Executes passed block for each "unit" of time specified, returning an array with the return values from the passed block.
def map_each(unit, options = {}, &block)
iterate(unit, options.merge(:map_result => true), &block)
end
# Executes passed block for each "unit" of time specified, returning an array with the return values from passed block. Additionally the time object passed into the block is set to the beginning of specified "unit".
def map_beginning_of_each(unit, options = {}, &block)
iterate(unit, options.merge(:map_result => true, :beginning_of => true), &block)
end
# Dynamically define convenience methods, like #each_hour instead of #each(:hour).
[:year, :month, :day, :hour, :min, :sec].each do |unit|
[:each, :beginning_of_each, :map_each, :map_beginning_of_each].each do |method|
define_method "#{method}_#{unit}" do |*args, &block|
send(method, unit, *args, &block)
end
class_eval { alias :"#{method}_minute" :"#{method}_min" } if unit == :min
class_eval { alias :"#{method}_second" :"#{method}_sec" } if unit == :sec
end
[:of_the, :of].each do |method|
define_method "#{method}_#{unit}" do |*args, &block|
send(method, unit, *args, &block)
end
class_eval { alias :"#{method}_minute" :"#{method}_min" } if unit == :min
class_eval { alias :"#{method}_second" :"#{method}_sec" } if unit == :sec
end
end
end
end

View File

@@ -1,18 +0,0 @@
module TimeExt
module MethodChain
def add_to_chain(method, *args, &block)
@method_chain ||= []
@method_chain << [method.to_sym, args, block]
end
def call_chain(custom_block = nil, &block)
method, args, iblock = @method_chain.pop
return nil if method.nil?
iblock = custom_block if !custom_block.nil?
method, args, iblock = yield(method, args, iblock) if block_given?
self.send(method, *args, &iblock)
end
end
end

27
lib/time_ext/support.rb Normal file
View File

@@ -0,0 +1,27 @@
module TimeExt
# Provides helper methods used by TimeExt::Calculations for backwards compatibility with ActiveSupport, and method chaining helpers for TimeExt::Iterations.
module Support
def days_into_week
defined?(DAYS_INTO_WEEK) ? DAYS_INTO_WEEK : { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6 }
end
def common_year_days_in_month
defined?(COMMON_YEAR_DAYS_IN_MONTH) ? COMMON_YEAR_DAYS_IN_MONTH : [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
end
def add_to_chain(method, *args, &block)
@method_chain ||= []
@method_chain << [method.to_sym, args, block]
end
def call_chain(custom_block = nil, &block)
method, args, iblock = @method_chain.pop
return nil if method.nil?
iblock = custom_block if !custom_block.nil?
method, args, iblock = yield(method, args, iblock) if block_given?
self.send(method, *args, &iblock)
end
end
end

3
lib/time_ext/version.rb Normal file
View File

@@ -0,0 +1,3 @@
module TimeExt
VERSION = "0.2.8"
end

19
spec/core_ext_spec.rb Normal file
View File

@@ -0,0 +1,19 @@
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe "Core Extensions" do
it "should alias Numeric class methods" do
number = 1
number.sec.should == number.second
number.min.should == number.minute
end
it "should alias Time instance methods" do
time = Time.now
time.secs_ago(10).should == time.ago(10)
time.seconds_ago(10).should == time.ago(10)
time.secs_since(10).should == time.since(10)
time.seconds_since(10).should == time.since(10)
end
end

View File

@@ -1,2 +0,0 @@
--format specdoc
--color

View File

@@ -1,9 +1,6 @@
$LOAD_PATH.unshift(File.dirname(__FILE__)) $LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
require 'time_ext'
require 'spec'
require 'spec/autorun'
Spec::Runner.configure do |config| require 'time_ext'
require 'rspec'
end require 'rspec/autorun'

View File

@@ -66,4 +66,32 @@ describe "Time Iterations" do
(@now + 6.hours).from(@now).map_each(:hour) { |time| time }.should == match (@now + 6.hours).from(@now).map_each(:hour) { |time| time }.should == match
end end
end it "should iterate over time objects with #map_each and #of_the via method chaining" do
match = (0..23).map { |i| ((@now - (@now.hour).hours) + i.hours).beginning_of_hour }
@now.map_each_hour.of_the(:day) { |time| time }.should == match
@now.map_each_hour.of_the_day { |time| time }.should == match
match = @now.beginning_of_month.map_beginning_of_each_hour(:include_start => true, :include_end => false).until(@now.next_month.beginning_of_month) { |time| time }
@now.map_each_hour.of_the(:month) { |time| time }.should == match
end
it "should iterate and respect the include_start and include_end options" do
match = (1..6).map { |i| @now + i.hours }
@now.map_each_hour.until(@now + 6.hours) { |time| time }.should == match
match = (1..6).map { |i| @now + i.hours }
@now.map_each_hour(:include_end => true).until(@now + 6.hours) { |time| time }.should == match
@now.map_each_hour(:include_start => false).until(@now + 6.hours) { |time| time }.should == match
match = (0..6).map { |i| @now + i.hours }
@now.map_each_hour(:include_start => true).until(@now + 6.hours) { |time| time }.should == match
match = (0..5).map { |i| @now + i.hours }
@now.map_each_hour(:include_start => true, :include_end => false).until(@now + 6.hours) { |time| time }.should == match
match = (0..6).map { |i| @now + i.hours }
@now.map_each_hour(:include_start => true, :include_end => true).until(@now + 6.hours) { |time| time }.should == match
end
it "should iterate and respect the beginning_of option" do
match = (1..6).map { |i| @now.beginning_of_hour + i.hours }
@now.map_beginning_of_each_hour.until(@now.beginning_of_hour + 6.hours) { |time| time }.should == match
@now.map_each_hour(:beginning_of => true).until(@now.beginning_of_hour + 6.hours) { |time| time }.should == match
end
end

24
time_ext.gemspec Normal file
View File

@@ -0,0 +1,24 @@
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
require "time_ext/version"
Gem::Specification.new do |s|
s.name = "time_ext"
s.version = TimeExt::VERSION
s.platform = Gem::Platform::RUBY
s.authors = ["Jim Myhrberg"]
s.email = ["contact@jimeh.me"]
s.homepage = "http://github.com/jimeh/time_ext"
s.summary = "Extends the abilities of Ruby's built-in Time class by building on top of ActiveSupport."
s.description = "Extends the abilities of Ruby's built-in Time class by building on top of ActiveSupport."
s.rubyforge_project = "time_ext"
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
s.add_runtime_dependency 'activesupport', '>= 2.3.0'
s.add_runtime_dependency 'i18n', '>= 0.4.2'
end