time_ext 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -20,3 +20,5 @@ pkg
20
20
 
21
21
  ## PROJECT::SPECIFIC
22
22
  .yardoc/*
23
+ *.gemspec
24
+ doc/*
data/README.md CHANGED
@@ -10,20 +10,54 @@ This gem extends the abilities of Ruby's built-in Time class by building on top
10
10
 
11
11
  ## Basic Usage
12
12
 
13
- require "time/ext"
14
- Time.now.round(:week)
13
+ ### Pure Ruby
14
+
15
+ require "time/ext" # or "time_ext"
16
+ Time.utc(2010, 6, 19).round(:month) #=> Thu Jul 01 00:00:00 UTC 2010
15
17
  #=> Beginning of this week, or next week depending on which date is closest
18
+ Time.now.each_hour.until(6.hours.from_now) { |t| puts t.to_s }
19
+ #=> Prints the time at one hour interals from now till 6 hours from now
20
+
21
+ ### Rails 2.x
22
+
23
+ config.gem "time_ext"
24
+
25
+ ### Rails 3.x
26
+
27
+ gem "time_ext"
16
28
 
17
29
 
18
30
  ## Documentation
19
31
 
20
- A decently informative documentation is available [here][docs] on [rdoc.info][].
32
+ Complete class and method documentation is available [here][docs] on [rdoc.info][].
33
+
34
+ ### Calculations
35
+
36
+ ActiveSupport includes some handy `#beginning_of_*` and `#end_of_*` methods among others for year, month, week, and day. TimeExt adds the same methods for quarter (3 months), hour, minute, and second. Additionally it also adds a set of familiar math methods, `#floor`, `#ceil`, and `#round`. Each of them takes a unit argument (day, month, etc.), and goes about it's operation to that unit type.
37
+
38
+ ### Iterations
39
+
40
+ You can easily iterate over specific units of time with the `#each`, `#beginning_of_each`, `#map_each`, and `#map_beginning_of_each` methods. Each method takes a unit input, just like the `#floor` method for example. But the iteration methods also have dynamic unit methods, like so:
41
+
42
+ time = Time.local(2010, 07, 10)
43
+ time.map_each(:hour).until(time + 6.hours) { |t| t.hour } #=> [1, 2, 3, 4, 5, 6]
44
+ time.map_each_hour.until(time + 6.hours) { |t| t.hour } #=> [1, 2, 3, 4, 5, 6]
45
+
46
+ The `#until` method defines when to stop the iteration. You can also use the `#from` method, which then iterates from the time passed into `#from` till the time of the object you're calling each on. If don't specify a end/start time, it'll assume you want all units within it's scope. Or in less gibberish, calling `#each_hour` will iterate every hour for a whole day, or 24 hours. If you're iterating by the minute, it'll stop after an hour.
47
+
48
+ Here's a couple of more examples:
49
+
50
+ time.map_each_hour.until(time + 6.hours) { |t| t.hour } #=> [1, 2, 3, 4, 5, 6]
51
+ time.until(time + 6.hours).map_each_hour { |t| t.hour } #=> [1, 2, 3, 4, 5, 6]
52
+ (time + 6.hours).map_each_hour.from(time) { |t| t.hour } #=> [1, 2, 3, 4, 5, 6]
53
+ (time + 6.hours).from(time).map_each_hour { |t| t.hour } #=> [1, 2, 3, 4, 5, 6]
21
54
 
22
- The main reason I created this gem was for the `round`, `floor`, and `ceil` methods. Each of them takes a unit argument, which can be one of the following: `:sec`, `:min`, `:hour`, `:day`, `:week`, `:month`, `:quarter`, and `:year`.
55
+ I recommend you read the complete class [documentation][docs] for the Time object to see all the new methods available.
23
56
 
24
57
 
25
58
  ## To-Do
26
59
 
60
+ * Some cleaner code.
27
61
  * Improve ReadMe file.
28
62
  * Improve documentation.
29
63
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -32,15 +32,25 @@ class Time
32
32
  end
33
33
  alias :beginning_of_closest :round
34
34
 
35
+ # Returns a new Time representing the previoius unit specified (defaults to second).
36
+ def prev(unit = :sec)
37
+ send("prev_#{unit}")
38
+ end
39
+
40
+ # Returns a new Time representing the next unit specified (defaults to second).
41
+ def next(unit = :sec)
42
+ send("next_#{unit}")
43
+ end
44
+
35
45
  # Short-hand for seconds_ago(1).
36
46
  def prev_second
37
- ago(1)
47
+ seconds_ago(1)
38
48
  end
39
49
  alias :prev_sec :prev_second
40
50
 
41
51
  # Short-hand for seconds_since(1).
42
52
  def next_second
43
- since(1)
53
+ seconds_since(1)
44
54
  end
45
55
  alias :next_sec :next_second
46
56
 
@@ -149,6 +159,11 @@ class Time
149
159
  since((quarters * 3).months)
150
160
  end
151
161
 
162
+ # Returns a new Time representing the end of the unit specified (defaults to second).
163
+ def end_of(unit = :sec)
164
+ send("end_of_#{unit}")
165
+ end
166
+
152
167
  # Returns a new Time representing the start of the second, XX:XX:XX.000000 (.000000000 in ruby1.9).
153
168
  def beginning_of_second
154
169
  change(:usec => 0)
@@ -0,0 +1,79 @@
1
+ class Time
2
+ include TimeExt::MethodChain
3
+
4
+ # Used by #each, #map_each and similar methods to iterate over ranges of time.
5
+ def iterate(unit, options = {}, &block)
6
+ options.reverse_merge!(:map_result => false, :beginning_of => false, :include_start => false)
7
+ if block_given?
8
+ units = [:year, :month, :day, :hour, :min, :sec, :usec]
9
+ parent_unit = units[units.index(unit)-1]
10
+ @until ||= (!parent_unit.nil?) ? self.send("#{parent_unit}s_since", 1) : self.send("#{unit}s_since", 1)
11
+ time = self.clone
12
+ direction = (self < @until) ? :f : :b
13
+ succ_method = (direction == :f) ? "next_#{unit}" : "prev_#{unit}"
14
+ time = time.beginning_of(unit) if options[:beginning_of]
15
+ time = time.send(succ_method) if !options[:include_start]
16
+ results = []
17
+ while (direction == :f && time <= @until) || (direction == :b && time >= @until)
18
+ options[:map_result] ? results << yield(time) : yield(time)
19
+ time = time.send(succ_method)
20
+ end
21
+ options[:map_result] ? results : self
22
+ else
23
+ add_to_chain(:iterate, unit, options)
24
+ self
25
+ end
26
+ end
27
+
28
+ # Used togeter with #each and other iteration methods to specify end of interation.
29
+ def until(time, &block)
30
+ time = time.to_time if time.is_a?(::Date)
31
+ @until = time
32
+ return call_chain(block) if block_given?
33
+ self
34
+ end
35
+ alias :till :until
36
+
37
+ # Used together with #each and other interation methods to specify start of iteration, and end will be current object.
38
+ def from(time, &block)
39
+ time = time.to_time if time.is_a?(::Date)
40
+ method, args = @method_chain.pop if block_given?
41
+ if !method.nil?
42
+ time.until(self).send(method, *args, &block)
43
+ else
44
+ time.until(self)
45
+ end
46
+ end
47
+
48
+ # Executes passed block for each "unit" of time specified, with a new time object for each interval passed to the block.
49
+ def each(unit, options = {}, &block)
50
+ iterate(unit, options.merge(:map_result => false), &block)
51
+ end
52
+
53
+ # 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.
54
+ def beginning_of_each(unit, options = {}, &block)
55
+ iterate(unit, options.merge(:map_result => false, :beginning_of => true), &block)
56
+ end
57
+
58
+ # Executes passed block for each "unit" of time specified, returning an array with the return values from the passed block.
59
+ def map_each(unit, options = {}, &block)
60
+ iterate(unit, options.merge(:map_result => true), &block)
61
+ end
62
+
63
+ # 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".
64
+ def map_beginning_of_each(unit, options = {}, &block)
65
+ iterate(unit, options.merge(:map_result => true, :beginning_of => true), &block)
66
+ end
67
+
68
+ # Dynamically define convenience methods, like #each_hour instead of #each(:hour).
69
+ [:year, :month, :day, :hour, :min, :sec].each do |unit|
70
+ [:each, :beginning_of_each, :map_each, :map_beginning_of_each].each do |method|
71
+ define_method "#{method}_#{unit}" do |*args, &block|
72
+ send(method, unit, *args, &block)
73
+ end
74
+ class_eval { alias :"#{method}_minute" :"#{method}_min" } if unit == :min
75
+ class_eval { alias :"#{method}_second" :"#{method}_sec" } if unit == :sec
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,18 @@
1
+ module TimeExt
2
+ module MethodChain
3
+
4
+ def add_to_chain(method, *args, &block)
5
+ @method_chain ||= []
6
+ @method_chain << [method.to_sym, args, block]
7
+ end
8
+
9
+ def call_chain(custom_block = nil, &block)
10
+ method, args, iblock = @method_chain.pop
11
+ return nil if method.nil?
12
+ iblock = custom_block if !custom_block.nil?
13
+ method, args, iblock = yield(method, args, iblock) if block_given?
14
+ self.send(method, *args, &iblock)
15
+ end
16
+
17
+ end
18
+ end
data/lib/time_ext.rb CHANGED
@@ -1,3 +1,13 @@
1
1
  require 'rubygems'
2
2
  require 'active_support'
3
- require 'time_ext/time'
3
+ require 'time_ext/method_chain'
4
+ require 'time_ext/core_ext/time/calculations'
5
+ require 'time_ext/core_ext/time/iterations'
6
+
7
+ module TimeExt
8
+
9
+ module Base
10
+ TIME_EXT_UNITS = [:year, :month, :day, :hour, :min, :sec, :usec]
11
+ end
12
+
13
+ end
@@ -1,6 +1,6 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
- describe "TimeExt" do
3
+ describe "Time Calculations" do
4
4
 
5
5
  before(:each) do
6
6
  @time = Time.local(2010, 8, 28, 15, 57, 17, 78430)
@@ -0,0 +1,69 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Time Iterations" do
4
+
5
+ before(:each) do
6
+ @now = Time.now
7
+ end
8
+
9
+ it "should iterate over time objects with #each" do
10
+ times = []
11
+ result = @now.each(:hour) { |time| times << time }
12
+ times.should have(24).items
13
+ times.should == (1..24).map { |i| @now + i.hours }
14
+ result.should == @now
15
+ end
16
+
17
+ it "should iterate over time objects with #beginning_of_each" do
18
+ times = []
19
+ result = @now.beginning_of_each_hour { |time| times << time }
20
+ times.should have(24).items
21
+ times.should == (1..24).map { |i| @now.beginning_of_hour + i.hours }
22
+ result.should == @now
23
+ end
24
+
25
+ it "should iterate over time objects with #map_each" do
26
+ result = @now.map_each_hour { |time| time }
27
+ result.should have(24).items
28
+ result.should == (1..24).map { |i| @now + i.hours }
29
+ end
30
+
31
+ it "should iterate over time objects with #map_beginning_of_each" do
32
+ result = @now.map_beginning_of_each(:hour) { |time| time }
33
+ result.should have(24).items
34
+ result.should == (1..24).map { |i| @now.beginning_of_hour + i.hours }
35
+ end
36
+
37
+ it "should iterate over time objects with #each and #until via method chaining" do
38
+ match = (1..6).map { |i| @now + i.hours }
39
+
40
+ times = []
41
+ result = @now.each(:hour).until(@now + 6.hours) { |time| times << time }
42
+ times.should == match
43
+ result.should == @now
44
+
45
+ times = []
46
+ result = @now.until(@now + 6.hours).each_hour { |time| times << time }
47
+ times.should == match
48
+ result.should == @now
49
+ end
50
+
51
+ it "should iterate over time objects with #map_each and #until via method chaining" do
52
+ match = (1..6).map { |i| @now + i.hours }
53
+ @now.map_each_hour.until(@now + 6.hours) { |time| time }.should == match
54
+ @now.until(@now + 6.hours).map_each(:hour) { |time| time }.should == match
55
+ end
56
+
57
+ it "should iterate over time objects backwards with #until set in the past" do
58
+ match = (1..6).map { |i| @now - i.hours }
59
+ @now.map_each_hour.until(@now - 6.hours) { |time| time }.should == match
60
+ @now.until(@now - 6.hours).map_each(:hour) { |time| time }.should == match
61
+ end
62
+
63
+ it "should iterate over time objects with #map_each and #from via method chaining" do
64
+ match = (1..6).map { |i| @now + i.hours }
65
+ (@now + 6.hours).map_each_hour.from(@now) { |time| time }.should == match
66
+ (@now + 6.hours).from(@now).map_each(:hour) { |time| time }.should == match
67
+ end
68
+
69
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: time_ext
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jim Myhrberg
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-07-27 00:00:00 +03:00
18
+ date: 2010-07-29 00:00:00 +03:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -80,27 +80,15 @@ files:
80
80
  - README.md
81
81
  - Rakefile
82
82
  - VERSION
83
- - doc/Time.html
84
- - doc/_index.html
85
- - doc/class_list.html
86
- - doc/css/common.css
87
- - doc/css/full_list.css
88
- - doc/css/style.css
89
- - doc/file.README.html
90
- - doc/file_list.html
91
- - doc/frames.html
92
- - doc/index.html
93
- - doc/js/app.js
94
- - doc/js/full_list.js
95
- - doc/js/jquery.js
96
- - doc/method_list.html
97
- - doc/top-level-namespace.html
98
83
  - lib/time/ext.rb
99
84
  - lib/time_ext.rb
100
- - lib/time_ext/time.rb
85
+ - lib/time_ext/core_ext/time/calculations.rb
86
+ - lib/time_ext/core_ext/time/iterations.rb
87
+ - lib/time_ext/method_chain.rb
101
88
  - spec/spec.opts
102
89
  - spec/spec_helper.rb
103
- - spec/time_ext_spec.rb
90
+ - spec/time_calculations_spec.rb
91
+ - spec/time_iterations_spec.rb
104
92
  has_rdoc: true
105
93
  homepage: http://github.com/jimeh/time_ext
106
94
  licenses: []
@@ -137,4 +125,5 @@ specification_version: 3
137
125
  summary: Extends the abilities of Ruby's built-in Time class by building on top of ActiveSupport.
138
126
  test_files:
139
127
  - spec/spec_helper.rb
140
- - spec/time_ext_spec.rb
128
+ - spec/time_calculations_spec.rb
129
+ - spec/time_iterations_spec.rb