timespan 0.2.8 → 0.3.1
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.
- data/README.md +58 -0
- data/VERSION +1 -1
- data/lib/timespan/compare.rb +1 -1
- data/lib/timespan/mongoid.rb +60 -5
- data/lib/timespan.rb +6 -2
- data/spec/timespan/mongoid/account.rb +6 -2
- data/spec/timespan/mongoid/mongoid_search_spec.rb +40 -0
- data/spec/timespan/mongoid/mongoid_timespan_spec.rb +16 -4
- data/timespan.gemspec +3 -2
- metadata +4 -3
data/README.md
CHANGED
@@ -113,6 +113,43 @@ account.period.end_date
|
|
113
113
|
account.period.days
|
114
114
|
account.period.duration # => Duration
|
115
115
|
|
116
|
+
## Searching periods
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
Account.where(:'period.from'.lt => 6.days.ago.to_i)
|
120
|
+
Account.where(:'period.from'.gt => 3.days.ago.to_i)
|
121
|
+
|
122
|
+
# in range
|
123
|
+
Account.where(:'period.from'.gt => 3.days.ago.to_i, :'period.to'.lt => Time.now.utc.to_i)
|
124
|
+
```
|
125
|
+
|
126
|
+
Make it easier by introducing a class helper:
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
class Account
|
130
|
+
include Mongoid::Document
|
131
|
+
field :period, :type => TimeSpan
|
132
|
+
|
133
|
+
def self.between from, to
|
134
|
+
Account.where(:'period.from'.gt => from.to_i, :'period.to'.lte => to.to_i)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
`Account.between(6.days.ago, 1.day.ago)`
|
140
|
+
|
141
|
+
Alternatively auto-generate a `#between` helper for the field:
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
class Account
|
145
|
+
include Mongoid::Document
|
146
|
+
field :period, :type => TimeSpan, :between => true
|
147
|
+
```
|
148
|
+
|
149
|
+
`Account.period_between(6.days.ago, 1.day.ago)`
|
150
|
+
|
151
|
+
See the `mongoid_search_spec.rb` for examples:
|
152
|
+
|
116
153
|
## Chronic duration
|
117
154
|
|
118
155
|
Is used to parse duration strings if Spanner can't be handle it
|
@@ -148,6 +185,27 @@ Currently it also uses Duration, which conflicts with the 'ruby-duration' gem.
|
|
148
185
|
1.week.each(10.hours) {|ten_hour_segment| ...} #=> Using a custom iterator of 10 hours. There would be 17 of them, but notice that the last iteration will only be 8 hours.
|
149
186
|
``
|
150
187
|
|
188
|
+
## Configuration and overrides
|
189
|
+
|
190
|
+
Timespan by default uses `Time.now.utc` to set the current time, fx used when either `end_date` or `start_date` otherwise would be nil. This is used in order to work with Mongoid (see [issue #400](https://github.com/mongoid/mongoid/issues/400))
|
191
|
+
|
192
|
+
You can customize `now` to return fx `Time.now`, `Date.today` or whatever suits you.
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
class Timespan
|
196
|
+
def now
|
197
|
+
Time.now # or Date.today
|
198
|
+
end
|
199
|
+
end
|
200
|
+
```
|
201
|
+
|
202
|
+
By default the `TimeSpan` is stored using `:from` and `:to` for the start and end times. This can be customized as follows:
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
TimeSpan.start_field = :start
|
206
|
+
TimeSpan.end_field = :end
|
207
|
+
```
|
208
|
+
|
151
209
|
## Contributing to Timespan
|
152
210
|
|
153
211
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.1
|
data/lib/timespan/compare.rb
CHANGED
data/lib/timespan/mongoid.rb
CHANGED
@@ -1,12 +1,40 @@
|
|
1
1
|
require "timespan"
|
2
2
|
require "mongoid/fields"
|
3
3
|
|
4
|
+
# http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
|
5
|
+
class Object
|
6
|
+
def meta_def name, &blk
|
7
|
+
(class << self; self; end).instance_eval { define_method name, &blk }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
Mongoid::Fields.option :between do |model, field, options|
|
12
|
+
name = field.name.to_sym
|
13
|
+
model.class_eval do
|
14
|
+
meta_def :"#{name}_between" do |from, to|
|
15
|
+
self.where(:"#{name}.#{TimeSpan.start_field}".gte => from.to_i, :"#{name}.#{TimeSpan.end_field}".lte => to.to_i)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
4
20
|
# Mongoid serialization support for Timespan type.
|
5
21
|
module Mongoid
|
6
22
|
module Fields
|
7
23
|
class Timespan
|
8
24
|
include Mongoid::Fields::Serializable
|
9
25
|
|
26
|
+
class << self
|
27
|
+
attr_writer :start_field, :end_field
|
28
|
+
|
29
|
+
def start_field
|
30
|
+
@start_field || :from
|
31
|
+
end
|
32
|
+
|
33
|
+
def end_field
|
34
|
+
@end_field || :to
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
10
38
|
def self.instantiate(name, options = {})
|
11
39
|
super
|
12
40
|
end
|
@@ -15,9 +43,9 @@ module Mongoid
|
|
15
43
|
#
|
16
44
|
# @param [Hash] Timespan as hash
|
17
45
|
# @return [Timespan] deserialized Timespan
|
18
|
-
def deserialize(
|
19
|
-
return if !
|
20
|
-
::Timespan.new
|
46
|
+
def deserialize(hash)
|
47
|
+
return if !hash
|
48
|
+
::Timespan.new :from => from(hash), :to => to(hash)
|
21
49
|
end
|
22
50
|
|
23
51
|
# Serialize a Timespan or a Hash (with Timespan units) or a Duration in some form to
|
@@ -27,8 +55,35 @@ module Mongoid
|
|
27
55
|
# @return [Hash] Timespan in seconds
|
28
56
|
def serialize(value)
|
29
57
|
return if value.blank?
|
30
|
-
timespan =
|
31
|
-
|
58
|
+
timespan = case value
|
59
|
+
when ::Timespan
|
60
|
+
value
|
61
|
+
else
|
62
|
+
::Timespan.new(value)
|
63
|
+
end
|
64
|
+
{:from => serialize_time(timespan.start_time), :to => serialize_time(timespan.end_time.to_i), :duration => timespan.duration.total }
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def from hash
|
70
|
+
from_value = hash['from'] || hash[:from]
|
71
|
+
raise ArgumentError, ":from is nil, #{hash.inspect}" if from_value.nil?
|
72
|
+
deserialize_time from_value
|
73
|
+
end
|
74
|
+
|
75
|
+
def to hash
|
76
|
+
to_value = hash['to'] || hash[:to]
|
77
|
+
raise ArgumentError, ":to is nil, #{hash.inspect}" if to_value.nil?
|
78
|
+
deserialize_time to_value
|
79
|
+
end
|
80
|
+
|
81
|
+
def serialize_time time
|
82
|
+
time.to_i
|
83
|
+
end
|
84
|
+
|
85
|
+
def deserialize_time millisecs
|
86
|
+
Time.at millisecs
|
32
87
|
end
|
33
88
|
end
|
34
89
|
end
|
data/lib/timespan.rb
CHANGED
@@ -120,8 +120,12 @@ class Timespan
|
|
120
120
|
end
|
121
121
|
|
122
122
|
def default_from_now!
|
123
|
-
self.start_time =
|
124
|
-
self.end_time =
|
123
|
+
self.start_time = now unless start_time || (end_time && duration)
|
124
|
+
self.end_time = now unless end_time || (start_time && duration)
|
125
|
+
end
|
126
|
+
|
127
|
+
def now
|
128
|
+
Time.now.utc
|
125
129
|
end
|
126
130
|
|
127
131
|
def validate!
|
@@ -1,10 +1,14 @@
|
|
1
1
|
class Account
|
2
|
-
include Mongoid::Document
|
3
|
-
field :period, :type => TimeSpan
|
2
|
+
include Mongoid::Document
|
3
|
+
field :period, :type => TimeSpan, :between => true
|
4
4
|
|
5
5
|
def self.create_it! duration
|
6
6
|
s = self.new
|
7
7
|
s.period = {duration: duration}
|
8
8
|
s
|
9
9
|
end
|
10
|
+
|
11
|
+
def self.between from, to
|
12
|
+
Account.where(:'period.from'.gt => from.to_i, :'period.to'.lte => to.to_i)
|
13
|
+
end
|
10
14
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'timespan/mongoid/spec_helper'
|
2
|
+
|
3
|
+
describe TimeSpan do
|
4
|
+
subject { account }
|
5
|
+
|
6
|
+
context '3 Accounts with periods serialized' do
|
7
|
+
before do
|
8
|
+
Account.delete_all
|
9
|
+
@acc1 = Account.create :period => Timespan.new(:start_date => 2.days.ago)
|
10
|
+
@acc2 = Account.create :period => Timespan.new(:start_date => 5.days.ago, :end_date => 2.days.ago)
|
11
|
+
@acc3 = Account.create :period => Timespan.new(:start_date => 10.days.ago)
|
12
|
+
|
13
|
+
# puts [@acc1._id, @acc2._id, @acc3._id]
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'find accounts within specific period' do
|
17
|
+
it 'should find a single period less than 6 days old' do
|
18
|
+
Account.where(:'period.from'.lt => 6.days.ago.to_i).first.should == @acc3
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should find a single period later than 3 days old' do
|
22
|
+
Account.where(:'period.from'.gt => 3.days.ago.to_i).first.should == @acc1
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'find a single period that is within period from 6 days ago to tomorrow' do
|
26
|
+
it 'should use Account.where' do
|
27
|
+
Account.where(:'period.from'.gt => 6.days.ago.to_i, :'period.to'.lte => 1.day.ago.to_i).first.should == @acc2
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should use Account class helper #between' do
|
31
|
+
Account.between(6.days.ago, 1.day.ago).first.should == @acc2
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should use Account class generated helper #period_between' do
|
35
|
+
Account.period_between(6.days.ago, 1.day.ago).first.should == @acc2
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'timespan/mongoid/spec_helper'
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe TimeSpan do
|
4
4
|
subject { account }
|
5
5
|
|
6
6
|
let(:from) { Chronic.parse("1 day ago") }
|
@@ -18,7 +18,19 @@ describe Timespan do
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
context '2 days duration using
|
21
|
+
context '2 days duration using Timespan' do
|
22
|
+
let(:account) do
|
23
|
+
Account.create :period => Timespan.new(:duration => 2.days)
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '.start_date' do
|
27
|
+
it 'should default to today' do
|
28
|
+
DateTime.parse(subject.period.start_date.to_s).strftime('%d %b %Y').should == Date.today.strftime('%d %b %Y')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context '2 days duration using :duration => integer via ActiveSupport::Duration' do
|
22
34
|
let(:account) do
|
23
35
|
Account.create :period => {:duration => 2.days }
|
24
36
|
end
|
@@ -30,7 +42,7 @@ describe Timespan do
|
|
30
42
|
end
|
31
43
|
end
|
32
44
|
|
33
|
-
context '2 days using integer' do
|
45
|
+
context '2 days using integer via ActiveSupport::Duration' do
|
34
46
|
let(:account) do
|
35
47
|
Account.create :period => 2.days
|
36
48
|
end
|
@@ -42,7 +54,7 @@ describe Timespan do
|
|
42
54
|
end
|
43
55
|
end
|
44
56
|
|
45
|
-
context '2 days duration
|
57
|
+
context '2 days duration using string' do
|
46
58
|
let(:account) do
|
47
59
|
Account.create :period => {:duration => '2 days'}
|
48
60
|
end
|
data/timespan.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "timespan"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Kristian Mandrup"]
|
12
|
-
s.date = "2012-05-
|
12
|
+
s.date = "2012-05-30"
|
13
13
|
s.description = "Makes it easy to calculate time distance in different units"
|
14
14
|
s.email = "kmandrup@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -38,6 +38,7 @@ Gem::Specification.new do |s|
|
|
38
38
|
"spec/timespan/duration_macros_spec.rb",
|
39
39
|
"spec/timespan/locales/duration_da.yml",
|
40
40
|
"spec/timespan/mongoid/account.rb",
|
41
|
+
"spec/timespan/mongoid/mongoid_search_spec.rb",
|
41
42
|
"spec/timespan/mongoid/mongoid_timespan_spec.rb",
|
42
43
|
"spec/timespan/mongoid/spec_helper.rb",
|
43
44
|
"spec/timespan/printer_spec.rb",
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: timespan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: chronic
|
@@ -232,6 +232,7 @@ files:
|
|
232
232
|
- spec/timespan/duration_macros_spec.rb
|
233
233
|
- spec/timespan/locales/duration_da.yml
|
234
234
|
- spec/timespan/mongoid/account.rb
|
235
|
+
- spec/timespan/mongoid/mongoid_search_spec.rb
|
235
236
|
- spec/timespan/mongoid/mongoid_timespan_spec.rb
|
236
237
|
- spec/timespan/mongoid/spec_helper.rb
|
237
238
|
- spec/timespan/printer_spec.rb
|
@@ -255,7 +256,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
255
256
|
version: '0'
|
256
257
|
segments:
|
257
258
|
- 0
|
258
|
-
hash:
|
259
|
+
hash: -4280308012345220837
|
259
260
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
260
261
|
none: false
|
261
262
|
requirements:
|