mirror of
https://github.com/jimeh/time_ext.git
synced 2026-02-19 05:16:40 +00:00
Interation methods #each, #map_each and more are
implemented and working. Also restructured files again.
This commit is contained in:
@@ -1,3 +1,13 @@
|
|||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
require 'active_support'
|
require 'active_support'
|
||||||
require 'time_ext/time'
|
require 'time_ext/method_chain'
|
||||||
|
require 'time_ext/core_ext/time/calculations'
|
||||||
|
require 'time_ext/core_ext/time/iterations'
|
||||||
|
|
||||||
|
module TimeExt
|
||||||
|
|
||||||
|
module Base
|
||||||
|
TIME_EXT_UNITS = [:year, :month, :day, :hour, :min, :sec, :usec]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
class Time
|
|
||||||
|
|
||||||
# Executes passed block for each unit of time specified
|
|
||||||
def each(unit, &block)
|
|
||||||
if block_given?
|
|
||||||
@each_until ||= self.tomorrow #TODO should be next larger unit
|
|
||||||
time = self.clone
|
|
||||||
while time <= @each_until
|
|
||||||
yield(time)
|
|
||||||
time = time.send("next_#{unit}")
|
|
||||||
end
|
|
||||||
self
|
|
||||||
else
|
|
||||||
@method_chain ||= []
|
|
||||||
@method_chain << [:each, [unit]]
|
|
||||||
self
|
|
||||||
end
|
|
||||||
end
|
|
||||||
alias :iterate :each
|
|
||||||
alias :beginning_of_each :each
|
|
||||||
|
|
||||||
# Executes passed block for each unit of time specified
|
|
||||||
def map(unit, &block)
|
|
||||||
if block_given?
|
|
||||||
@each_until ||= self.tomorrow #TODO should be next larger unit
|
|
||||||
time = self.clone
|
|
||||||
result = []
|
|
||||||
while time <= @each_until
|
|
||||||
result << yield(time)
|
|
||||||
time = time.send("next_#{unit}")
|
|
||||||
end
|
|
||||||
result
|
|
||||||
else
|
|
||||||
@method_chain ||= []
|
|
||||||
@method_chain << [:map, [unit]]
|
|
||||||
self
|
|
||||||
end
|
|
||||||
end
|
|
||||||
alias :iterate :each
|
|
||||||
|
|
||||||
# Used togeter with #each to specify end of interation
|
|
||||||
def until(time, &block)
|
|
||||||
time = time.to_time if time.is_a?(::Date)
|
|
||||||
@each_until = time
|
|
||||||
if block_given?
|
|
||||||
method, args = @method_chain.pop
|
|
||||||
self.send(method, *args, &block)
|
|
||||||
else
|
|
||||||
@method_chain ||= []
|
|
||||||
@method_chain << [:until, [time]]
|
|
||||||
self
|
|
||||||
end
|
|
||||||
end
|
|
||||||
alias :till :until
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -32,6 +32,16 @@ class Time
|
|||||||
end
|
end
|
||||||
alias :beginning_of_closest :round
|
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).
|
# Short-hand for seconds_ago(1).
|
||||||
def prev_second
|
def prev_second
|
||||||
ago(1)
|
ago(1)
|
||||||
@@ -149,6 +159,16 @@ class Time
|
|||||||
since((quarters * 3).months)
|
since((quarters * 3).months)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns a new Time representing the start of the unit specified (defaults to second).
|
||||||
|
def beginning_of(unit = :sec)
|
||||||
|
send("beginning_of_#{unit}")
|
||||||
|
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).
|
# Returns a new Time representing the start of the second, XX:XX:XX.000000 (.000000000 in ruby1.9).
|
||||||
def beginning_of_second
|
def beginning_of_second
|
||||||
change(:usec => 0)
|
change(:usec => 0)
|
||||||
71
lib/time_ext/core_ext/time/iterations.rb
Normal file
71
lib/time_ext/core_ext/time/iterations.rb
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Used togeter with #each to specify end of interation.
|
||||||
|
def until(time, &block)
|
||||||
|
time = time.to_time if time.is_a?(::Date)
|
||||||
|
@until = time
|
||||||
|
if block_given?
|
||||||
|
call_chain(block)
|
||||||
|
else
|
||||||
|
add_to_chain(:until, time)
|
||||||
|
self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
alias :till :until
|
||||||
|
|
||||||
|
# Dynamically define convenience methods, like #each_hour instead of #each(:hour).
|
||||||
|
[:year, :month, :day, :hour, :min, :minute, :sec, :second].each do |unit|
|
||||||
|
[:each, :beginning_of_each, :map_each, :map_beginning_of_each].each do |method|
|
||||||
|
called_unit = (unit == :minute) ? :min : (unit == :second) ? :sec : unit
|
||||||
|
define_method "#{method}_#{unit}" do |*args, &block|
|
||||||
|
send(method, called_unit, *args, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
18
lib/time_ext/method_chain.rb
Normal file
18
lib/time_ext/method_chain.rb
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
||||||
|
|
||||||
describe "TimeExt" do
|
describe "Time Calculations" do
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
@time = Time.local(2010, 8, 28, 15, 57, 17, 78430)
|
@time = Time.local(2010, 8, 28, 15, 57, 17, 78430)
|
||||||
63
spec/time_iterations_spec.rb
Normal file
63
spec/time_iterations_spec.rb
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
||||||
|
|
||||||
|
describe "Time Iterations" do
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
@now = Time.now
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should iterate over time objects with #each" do
|
||||||
|
times = []
|
||||||
|
result = @now.each(:hour) { |time| times << time }
|
||||||
|
times.should have(24).items
|
||||||
|
times.should == (1..24).map { |i| @now + i.hours }
|
||||||
|
result.should == @now
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should iterate over time objects with #beginning_of_each" do
|
||||||
|
times = []
|
||||||
|
result = @now.beginning_of_each_hour { |time| times << time }
|
||||||
|
times.should have(24).items
|
||||||
|
times.should == (1..24).map { |i| @now.beginning_of_hour + i.hours }
|
||||||
|
result.should == @now
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should iterate over time objects with #map_each" do
|
||||||
|
result = @now.map_each_hour { |time| time }
|
||||||
|
result.should have(24).items
|
||||||
|
result.should == (1..24).map { |i| @now + i.hours }
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should iterate over time objects with #map_beginning_of_each" do
|
||||||
|
result = @now.map_beginning_of_each(:hour) { |time| time }
|
||||||
|
result.should have(24).items
|
||||||
|
result.should == (1..24).map { |i| @now.beginning_of_hour + i.hours }
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should iterate over time objects with #each and #until via method chaining" do
|
||||||
|
match = (1..6).map { |i| @now + i.hours }
|
||||||
|
|
||||||
|
times = []
|
||||||
|
result = @now.each(:hour).until(@now + 6.hours) { |time| times << time }
|
||||||
|
times.should == match
|
||||||
|
result.should == @now
|
||||||
|
|
||||||
|
times = []
|
||||||
|
result = @now.until(@now + 6.hours).each_hour { |time| times << time }
|
||||||
|
times.should == match
|
||||||
|
result.should == @now
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should iterate over time objects with #map_each and #until via method chaining" do
|
||||||
|
match = (1..6).map { |i| @now + i.hours }
|
||||||
|
@now.map_each_hour.until(@now + 6.hours) { |time| time }.should == match
|
||||||
|
@now.until(@now + 6.hours).map_each(:hour) { |time| time }.should == match
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should iterate over time objects backwards with #until set in the past" do
|
||||||
|
match = (1..6).map { |i| @now - i.hours }
|
||||||
|
@now.map_each_hour.until(@now - 6.hours) { |time| time }.should == match
|
||||||
|
@now.until(@now - 6.hours).map_each(:hour) { |time| time }.should == match
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user