timespan 0.4.6 → 0.4.9

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/Gemfile CHANGED
@@ -4,6 +4,7 @@ gem 'chronic'
4
4
  gem 'chronic_duration'
5
5
  gem 'activesupport', '>= 3.0.0'
6
6
  gem 'spanner'
7
+ gem 'sugar-high', '~> 0.7.2' # for range intersect
7
8
  gem 'xduration', '~> 2.2'
8
9
 
9
10
  group :test, :development do
@@ -12,10 +13,12 @@ group :test, :development do
12
13
  # gem 'mongoid', '>= 2.4'
13
14
  # gem 'bson', '>= 1.6'
14
15
 
15
- gem 'mongoid', '~> 3.0.0' #, :git => 'git://github.com/mongoid/mongoid.git'
16
+ gem 'mongoid', '~> 3.0'
17
+ gem 'origin-selectable_ext', '~> 0.1.1'
16
18
  # gem 'i18n'
17
19
  end
18
20
 
21
+
19
22
  group :development do
20
23
  gem "rdoc", ">= 3.12"
21
24
  gem "bundler", ">= 1.0.0"
data/Gemfile.lock CHANGED
@@ -58,7 +58,9 @@ GEM
58
58
  moped (1.2.0)
59
59
  multi_json (1.3.6)
60
60
  numerizer (0.1.1)
61
- origin (1.0.4)
61
+ origin (1.0.9)
62
+ origin-selectable_ext (0.1.1)
63
+ origin
62
64
  polyglot (0.3.3)
63
65
  rack (1.4.1)
64
66
  rack-cache (1.2)
@@ -102,6 +104,7 @@ GEM
102
104
  hike (~> 1.2)
103
105
  rack (~> 1.0)
104
106
  tilt (~> 1.1, != 1.3.0)
107
+ sugar-high (0.7.2)
105
108
  thor (0.15.4)
106
109
  tilt (1.3.3)
107
110
  treetop (1.4.10)
@@ -121,10 +124,12 @@ DEPENDENCIES
121
124
  chronic
122
125
  chronic_duration
123
126
  jeweler (>= 1.8.3)
124
- mongoid (~> 3.0.0)
127
+ mongoid (~> 3.0)
128
+ origin-selectable_ext (~> 0.1.1)
125
129
  rails (~> 3.2.7)
126
130
  rdoc (>= 3.12)
127
131
  rspec (>= 2.8.0)
128
132
  simplecov (>= 0.5)
129
133
  spanner
134
+ sugar-high (~> 0.7.2)
130
135
  xduration (~> 2.2)
data/README.md CHANGED
@@ -309,6 +309,23 @@ TimeSpan.start_field = :start
309
309
  TimeSpan.end_field = :end
310
310
  ```
311
311
 
312
+ ## Ranges
313
+
314
+ A Range can be converted into either a `Timespan` or a `DurationRange`
315
+
316
+ ### DurationRange
317
+
318
+ ```ruby
319
+ dr = (1..5).days # => DurationRange 1..5, :days
320
+ ts =(1..5).days(:timespan) # => Timespan start_date: 1.day.from_now, end_date: 5.days.from_now
321
+
322
+ dr.between?(4.days) # => true
323
+ ```
324
+
325
+ You can also use Range#intersect from *sugar-high* gem to test intersection of time ranges ;)
326
+
327
+ ### Timespan
328
+
312
329
  ## Contributing to Timespan
313
330
 
314
331
  * 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.4.6
1
+ 0.4.9
@@ -0,0 +1,12 @@
1
+ class Hash
2
+ def __evolve_to_timespan__
3
+ serializer = Mongoid::Fields::Timespan
4
+ object = self
5
+ ::Timespan.new :from => serializer.from(object), :to => serializer.to(object), asap: serializer.asap(object)
6
+ end
7
+
8
+ def __evolve_to_duration_range__
9
+ range = Range.new self['from'], self['to']
10
+ ::DurationRange.new range, :seconds
11
+ end
12
+ end
@@ -0,0 +1,119 @@
1
+ class TimespanRange < DelegateDecorator
2
+ attr_accessor :unit, :range
3
+
4
+ def initialize range, unit
5
+ super(range)
6
+ @range = Timespan.new between: range
7
+ @unit = unit.to_s.singularize.to_sym
8
+ end
9
+ end
10
+
11
+ class DurationRange < DelegateDecorator
12
+ attr_accessor :unit, :range
13
+
14
+ def initialize range, unit
15
+ super(range)
16
+ @unit = unit.to_s.singularize.to_sym
17
+ @range = range
18
+ end
19
+
20
+ def __evolve_to_duration_range__
21
+ self
22
+ end
23
+
24
+ def mongoize
25
+ {:from => range.min.to_i, :to => range.max.to_i}
26
+ end
27
+
28
+ def between? duration
29
+ obj = case duration
30
+ when Duration
31
+ duration
32
+ else
33
+ Duration.new duration
34
+ end
35
+ obj.total >= min && obj.total <= max
36
+ end
37
+
38
+ class << self
39
+ # See http://mongoid.org/en/mongoid/docs/upgrading.html
40
+
41
+ # Serialize a Hash (with DurationRange keys) or a DurationRange to
42
+ # a BSON serializable type.
43
+ #
44
+ # @param [Timespan, Hash, Integer, String] value
45
+ # @return [Hash] Timespan in seconds
46
+ def mongoize object
47
+ mongoized = case object
48
+ when DurationRange then object.mongoize
49
+ when Hash
50
+ object
51
+ when Range
52
+ object.send(:seconds).mongoize
53
+ else
54
+ object
55
+ end
56
+ # puts "mongoized: #{mongoized} - Hash"
57
+ mongoized
58
+ end
59
+
60
+ # Deserialize a Timespan given the hash stored by Mongodb
61
+ #
62
+ # @param [Hash] Timespan as hash
63
+ # @return [Timespan] deserialized Timespan
64
+ def demongoize(object)
65
+ return if !object
66
+
67
+ demongoized = case object
68
+ when Hash
69
+ object.__evolve_to_duration_range__
70
+ else
71
+ raise "Unable to demongoize DurationRange from: #{object}"
72
+ end
73
+ # puts "demongoized: #{demongoized} - DurationRange"
74
+ demongoized
75
+ end
76
+
77
+ # Converts the object that was supplied to a criteria and converts it
78
+ # into a database friendly form.
79
+ def evolve(object)
80
+ object.__evolve_to_duration_range__.mongoize
81
+ end
82
+
83
+ protected
84
+
85
+ def parse duration
86
+ if duration.kind_of? Numeric
87
+ return Duration.new duration
88
+ else
89
+ case duration
90
+ when Timespan
91
+ duration.duration
92
+ when Duration
93
+ duration
94
+ when Hash
95
+ Duration.new duration
96
+ when Time
97
+ duration.to_i
98
+ when DateTime, Date
99
+ duration.to_time.to_i
100
+ when String
101
+ Duration.new parse_duration(duration)
102
+ else
103
+ raise ArgumentError, "Unsupported duration type: #{duration.inspect} of class #{duration.class}"
104
+ end
105
+ end
106
+ end
107
+
108
+ end
109
+ end
110
+
111
+
112
+ class Range
113
+ [:seconds, :minutes, :hours, :days, :weeks, :months, :years].each do |unit|
114
+ define_method unit do |type = :duration|
115
+ timerange = Range.new self.min.send(unit), self.max.send(unit)
116
+ type == :timespan ? TimespanRange.new(timerange, unit) : DurationRange.new(timerange, unit)
117
+ end
118
+ end
119
+ end
@@ -0,0 +1 @@
1
+ require 'timespan/core_ext/range'
@@ -1,6 +1,14 @@
1
+ require 'origin-selectable_ext'
2
+ require 'timespan/core_ext/hash'
3
+
1
4
  class Timespan
2
5
  Serializer = Mongoid::Fields::Timespan
3
6
 
7
+ def __evolve_to_timespan__
8
+ self
9
+ end
10
+
11
+
4
12
  # See http://mongoid.org/en/mongoid/docs/upgrading.html
5
13
 
6
14
  # Serialize a Timespan or a Hash (with Timespan units) or a Duration in some form to
@@ -9,7 +17,7 @@ class Timespan
9
17
  # @param [Timespan, Hash, Integer, String] value
10
18
  # @return [Hash] Timespan in seconds
11
19
  def mongoize
12
- {:from => Serializer.serialize_time(start_time), :to => Serializer.serialize_time(end_time), :duration => duration.total }
20
+ {:from => Serializer.serialize_time(start_time), :to => Serializer.serialize_time(end_time), :duration => duration.total, :asap => asap? }
13
21
  end
14
22
 
15
23
  class << self
@@ -38,7 +46,7 @@ class Timespan
38
46
  return if !object
39
47
  case object
40
48
  when Hash
41
- ::Timespan.new :from => Serializer.from(object), :to => Serializer.to(object)
49
+ object.__evolve_to_timespan__
42
50
  else
43
51
  ::Timespan.new object
44
52
  end
@@ -47,10 +55,43 @@ class Timespan
47
55
  # Converts the object that was supplied to a criteria and converts it
48
56
  # into a database friendly form.
49
57
  def evolve(object)
50
- case object
51
- when Timespan then object.mongoize
52
- else object
58
+ object.__evolve_to_timespan__.mongoize
59
+ end
60
+
61
+ def custom_serialization?(operator)
62
+ return false unless operator
63
+ case operator
64
+ when '$gte', '$gt', '$lt', '$lte', '$eq', '$between', '$btw'
65
+ true
66
+ else
67
+ false
68
+ end
69
+ end
70
+
71
+ def custom_specify(name, operator, value, options = {})
72
+ puts "custom_specify: #{name}"
73
+ timespan = value.__evolve_to_timespan__
74
+ case operator
75
+ when '$gte', '$gt', '$lt', '$lte', '$eq', '$between', '$btw'
76
+ specify_with_asap(name, operator, timespan, options)
77
+ else
78
+ raise RuntimeError, "Unsupported operator"
79
+ end
80
+ end
81
+
82
+ def specify_with_asap(name, operator, timespan, options)
83
+ query = { 'from' => {}, 'to' => {} }
84
+ case operator
85
+ when '$gte', '$gt'
86
+ query['from'][operator] = Serializer.serialize_time(timespan.min)
87
+ when '$lte', '$lt', '$eq'
88
+ query['to'][operator] = Serializer.serialize_time(timespan.min)
89
+ when '$between', '$btw'
90
+ query['from']['$gte'] = Serializer.serialize_time(timespan.min)
91
+ query['to']['$lte'] = Serializer.serialize_time(timespan.max)
53
92
  end
93
+ puts "query: #{query}"
94
+ query
54
95
  end
55
96
  end
56
97
  end
@@ -39,6 +39,12 @@ module Mongoid
39
39
  deserialize_time to_value
40
40
  end
41
41
 
42
+ def asap hash
43
+ asap_value = hash['asap'] || hash[:asap]
44
+ raise ArgumentError, ":asap is nil, #{hash.inspect}" if ![true, false, nil].include? asap_value
45
+ asap_value
46
+ end
47
+
42
48
  def serialize_time time
43
49
  raise ArgumentError, "Can't serialize time from nil" if time.nil?
44
50
  time.to_i
data/lib/timespan.rb CHANGED
@@ -3,6 +3,13 @@ require 'chronic'
3
3
  require 'chronic_duration'
4
4
  require 'spanner'
5
5
 
6
+ # Range intersection that works with dates!
7
+ require 'sugar-high/range'
8
+ require 'sugar-high/delegate'
9
+ require 'sugar-high/kind_of'
10
+
11
+ require 'timespan/core_ext'
12
+
6
13
  require 'timespan/units'
7
14
  require 'timespan/compare'
8
15
  require 'timespan/printer'
@@ -44,7 +51,6 @@ class Timespan
44
51
 
45
52
  def initialize options = {}
46
53
  @is_new = true
47
-
48
54
  @init_options = options
49
55
  validate! if options == {}
50
56
 
@@ -60,41 +66,70 @@ class Timespan
60
66
  @is_new = false
61
67
  end
62
68
 
63
- def self.from start, duration, options = {}
64
- start = case start.to_sym
65
- when :now, :asap
66
- Time.now
67
- when :today
68
- Date.today
69
- when :tomorrow
70
- Date.today + 1.day
71
- when :next_week # requires active_support
72
- date = Date.today.next_week
73
- options[:start] ? date.beginning_of_week : date
74
- when :next_month
75
- date = Date.today.next_month
76
- options[:start] ? date.at_beginning_of_month.next_month : date
77
- else
78
- start
69
+ class << self
70
+ def max_date
71
+ @max_date ||= Time.now + 10.years
79
72
  end
80
73
 
81
- self.new start_date: start, duration: duration
82
- end
74
+ def min_date
75
+ @min_date ||= Time.now
76
+ end
83
77
 
84
- def self.untill ending
85
- ending = case ending.to_sym
86
- when :tomorrow
87
- Date.today + 1.day
88
- when :next_week # requires active_support
89
- date = Date.today.next_week
90
- options[:start] ? date.beginning_of_week : date
91
- when :next_month
92
- date = Date.today.next_month
93
- options[:start] ? date.at_beginning_of_month.next_month : date
94
- else
95
- ending
78
+ def max_date= date
79
+ @max_date = date if valid_date?(date)
80
+ end
81
+
82
+ def min_date= date
83
+ @max_date = date if valid_date?(date)
84
+ end
85
+
86
+ def asap options = {}
87
+ self.new options.merge(asap: true)
88
+ end
89
+
90
+ def from start, duration, options = {}
91
+ start = case start.to_sym
92
+ when :now, :asap
93
+ Time.now
94
+ when :today
95
+ Date.today
96
+ when :tomorrow
97
+ Date.today + 1.day
98
+ when :next_week # requires active_support
99
+ date = Date.today.next_week
100
+ options[:start] ? date.beginning_of_week : date
101
+ when :next_month
102
+ date = Date.today.next_month
103
+ options[:start] ? date.at_beginning_of_month.next_month : date
104
+ else
105
+ start
106
+ end
107
+
108
+ self.new start_date: start, duration: duration
109
+ end
110
+
111
+ def untill ending
112
+ ending = case ending.to_sym
113
+ when :tomorrow
114
+ Date.today + 1.day
115
+ when :next_week # requires active_support
116
+ date = Date.today.next_week
117
+ options[:start] ? date.beginning_of_week : date
118
+ when :next_month
119
+ date = Date.today.next_month
120
+ options[:start] ? date.at_beginning_of_month.next_month : date
121
+ else
122
+ ending
123
+ end
124
+ self.new start_date: Date.now, end_date: ending
125
+ end
126
+ alias_method :until, :untill
127
+
128
+ protected
129
+
130
+ def valid_date? date
131
+ date.any_kind_of?(Date, Time, DateTime)
96
132
  end
97
- self.new start_date: Date.now, end_date: ending
98
133
  end
99
134
 
100
135
  def start_time= time
@@ -122,21 +157,37 @@ class Timespan
122
157
  end
123
158
  alias_method :end_date=, :end_time=
124
159
 
125
- def until time
160
+ def asap?
161
+ @asap
162
+ end
163
+
164
+ def min
165
+ asap ? Time.now : start_time
166
+ end
167
+
168
+ def max
169
+ end_time
170
+ end
171
+
172
+ def untill time
126
173
  self.end_time = time
127
174
  self
128
175
  end
176
+ alias_method :until, :untill
129
177
 
130
178
  def convert_to_time time
179
+ time = Duration.new time if time.kind_of? Numeric
131
180
  case time
132
181
  when String
133
182
  Chronic.parse(time)
134
183
  when Date, DateTime
135
184
  time.to_time
185
+ when Duration
186
+ (Time.now + time).to_time
136
187
  when Time
137
188
  time
138
189
  else
139
- raise ArgumentError, "A valid time must be either a String, Date, Time or DateTime, was: #{time.inspect}"
190
+ raise ArgumentError, "A valid time must be either a String, Duration, Date, Time or DateTime, was: #{time.inspect} (#{time.class})"
140
191
  end
141
192
  end
142
193
 
@@ -153,18 +204,40 @@ class Timespan
153
204
  from = options[first_from(START_KEYS, options)]
154
205
  to = options[first_from(END_KEYS, options)]
155
206
  dur = options[first_from(DURATION_KEYS, options)]
207
+ asap = options[:asap]
208
+
209
+ if options[:at_least]
210
+ to = Timespan.max_date
211
+ from = Time.now + options[:at_least]
212
+ end
213
+
214
+ if options[:at_most]
215
+ to = Time.now + options[:at_most]
216
+ from = Time.now
217
+ end
218
+
219
+ if options[:between]
220
+ from = Time.now + options[:between].min
221
+ to = Time.now + options[:between].max
222
+ end
223
+
224
+ # puts "configure: to:#{to}, from:#{from}, dur:#{dur}, asap:#{asap}"
156
225
 
226
+ @asap = asap if asap
157
227
  self.duration = dur if dur
158
228
  self.start_time = from if from
159
229
  self.end_time = to if to
160
230
 
231
+ # puts "configured: start:#{self.start_time}, end:#{self.end_time}, duration:#{self.duration}, asap:#{self.asap?}"
232
+
161
233
  default_from_now!
162
234
  calculate_miss!
163
235
  rescue ArgumentError => e
164
236
  raise TimeParseError, e.message
165
- rescue Exception => e
166
- calculate_miss!
167
- validate!
237
+ # rescue Exception => e
238
+ # puts "Exception: #{e}"
239
+ # calculate_miss!
240
+ # validate!
168
241
  end
169
242
 
170
243
  def default_from_now!
@@ -0,0 +1,60 @@
1
+ require 'timespan/mongoid/spec_helper'
2
+
3
+ describe Range do
4
+ subject { timerange }
5
+
6
+ describe 'create DurationRange' do
7
+ let(:range) { (1..5) }
8
+ let (:timerange) { range.days }
9
+
10
+ specify { subject.should be_a DurationRange }
11
+
12
+ its(:min) { should be_a Fixnum }
13
+ its(:max) { should be_a Fixnum }
14
+
15
+ specify { subject.min.should == 1.day }
16
+ specify { subject.max.should == 5.days }
17
+ end
18
+ end
19
+
20
+ describe DurationRange do
21
+ subject { timerange }
22
+
23
+ let(:range) { (1..5) }
24
+
25
+ context 'day range' do
26
+ let (:timerange) { range.days }
27
+
28
+ its(:range) { should be_a Range }
29
+ its(:min) { should == 1.day }
30
+ its(:max) { should == 5.days }
31
+ its(:unit) { should == :day }
32
+
33
+ specify { subject.between?(4.days).should be_true }
34
+ end
35
+
36
+ context 'week range' do
37
+ let (:timerange) { range.weeks }
38
+
39
+ its(:range) { should be_a Range }
40
+ its(:min) { should == 1.week }
41
+ its(:max) { should == 5.weeks }
42
+ its(:unit) { should == :week }
43
+ end
44
+
45
+ context 'month range' do
46
+ let (:timerange) { range.months }
47
+
48
+ its(:min) { should == 1.month }
49
+ its(:max) { should == 5.months }
50
+ its(:unit) { should == :month }
51
+ end
52
+
53
+ context 'year range' do
54
+ let (:timerange) { range.years }
55
+
56
+ its(:min) { should == 1.year }
57
+ its(:max) { should == 5.years }
58
+ its(:unit) { should == :year }
59
+ end
60
+ end
@@ -0,0 +1,57 @@
1
+ require 'timespan/mongoid/spec_helper'
2
+
3
+ describe Range do
4
+ subject { timerange }
5
+
6
+ let(:range) { (1..5) }
7
+
8
+ describe 'create TimespanRange' do
9
+
10
+ let (:timerange) { range.months(:timespan) }
11
+
12
+ specify { subject.should be_a TimespanRange }
13
+ its(:range) { should be_a Timespan }
14
+ end
15
+ end
16
+
17
+ describe TimespanRange do
18
+ subject { timerange }
19
+
20
+ let(:range) { (1..5) }
21
+
22
+ context 'day range' do
23
+ let (:timerange) { range.days(:timespan) }
24
+
25
+ its(:range) { should be_a Timespan }
26
+ its(:min) { should == 1.day }
27
+ its(:max) { should == 5.days }
28
+ its(:unit) { should == :day }
29
+ end
30
+
31
+ context 'week range' do
32
+ let (:timerange) { range.weeks(:timespan) }
33
+
34
+ its(:range) { should be_a Timespan }
35
+ its(:min) { should == 1.week }
36
+ its(:max) { should == 5.weeks }
37
+ its(:unit) { should == :week }
38
+ end
39
+
40
+ context 'month range' do
41
+ let (:timerange) { range.months(:timespan) }
42
+
43
+ its(:range) { should be_a Timespan }
44
+ its(:min) { should == 1.month }
45
+ its(:max) { should == 5.months }
46
+ its(:unit) { should == :month }
47
+ end
48
+
49
+ context 'year range' do
50
+ let (:timerange) { range.years(:timespan) }
51
+
52
+ its(:range) { should be_a Timespan }
53
+ its(:min) { should == 1.year }
54
+ its(:max) { should == 5.years }
55
+ its(:unit) { should == :year }
56
+ end
57
+ end
@@ -0,0 +1,216 @@
1
+ require 'timespan/mongoid/spec_helper'
2
+
3
+ describe TimeSpan do
4
+ subject { account }
5
+
6
+ before :all do
7
+ # Mongoid.logger.level = Logger::DEBUG
8
+ # Moped.logger.level = Logger::DEBUG
9
+ # Mongoid.logger = Logger.new($stdout)
10
+ # Moped.logger = Logger.new($stdout)
11
+ end
12
+
13
+ def max_asap
14
+ 10.days.from_now.to_i
15
+ end
16
+
17
+ context '10 Accounts with period and duration-range' do
18
+
19
+ # TODO:
20
+ # Doesn't handle dynamic ASAP as attempted
21
+ # implemented via custom_specify
22
+
23
+ before do
24
+ Account.delete_all
25
+
26
+ @acc1 = Account.create name: '1', period: Timespan.from(:today, 5.days),
27
+ time_period: TimePeriod.new(flex: (2..5).minutes)
28
+
29
+ @acc2 = Account.create name: '2', period: Timespan.from(:today, 5.days),
30
+ time_period: TimePeriod.new(flex: (2..4).minutes)
31
+
32
+ @acc3 = Account.create name: '3', period: Timespan.from(:today, 5.days),
33
+ time_period: TimePeriod.new(flex: (0..4).minutes)
34
+
35
+ @acc4 = Account.create name: '4', period: Timespan.from(:today, 5.days),
36
+ time_period: TimePeriod.new(flex: (0..3).minutes)
37
+
38
+ @acc5 = Account.create name: '5', period: Timespan.from(:today, 5.days),
39
+ time_period: TimePeriod.new(flex: (0..2).minutes)
40
+
41
+ @acc6 = Account.create name: '6', period: Timespan.from(:next_month, 100.days),
42
+ time_period: TimePeriod.new(flex: (0..2).minutes)
43
+
44
+ @acc7 = Account.create name: '7', period: Timespan.from(:next_week, 100.days),
45
+ time_period: TimePeriod.new(flex: (0..2).minutes)
46
+
47
+ @acc8 = Account.create name: '8', period: Timespan.new(start_date: 9.days.from_now, duration: 100.days),
48
+ time_period: TimePeriod.new(flex: (5..10).minutes)
49
+
50
+ @acc9 = Account.create name: '9', period: Timespan.new(start_date: 40.days.from_now, duration: 100.days),
51
+ time_period: TimePeriod.new(flex: (0..3).minutes)
52
+
53
+ @acc8 = Account.create name: '10', period: Timespan.new(start_date: 1.week.from_now, duration: 100.days),
54
+ time_period: TimePeriod.new(flex: (3..6).minutes)
55
+ end
56
+
57
+ def yesterday
58
+ 1.day.ago.to_i
59
+ end
60
+
61
+ def where_hash
62
+ {:'period.from'.gte => yesterday, :'period.from'.lte => max_asap}
63
+ end
64
+
65
+ def exactly
66
+ [{:'time_period.flex.from' => period}, {:'time_period.flex.to' => period}]
67
+ end
68
+
69
+ def in_between
70
+ [{:'time_period.flex.from' => min_period}, {:'time_period.flex.to' => max_period}]
71
+ end
72
+
73
+ def at_least type = :min
74
+ {:'time_period.flex.to'.gte => send("#{type}_period") }
75
+ end
76
+
77
+ def at_most type = :max
78
+ {:'time_period.flex.to'.lte => send("#{type}_period") }
79
+ end
80
+
81
+ describe 'ASAP 2 minutes' do
82
+ let(:period) { 2.minutes.to_i }
83
+
84
+ let(:criteria) do
85
+ Account.where(where_hash).or(exactly)
86
+ end
87
+
88
+ # it 'should have a nice criteria' do
89
+ # criteria.selector['$or'].should == [{"time_period.flex.from"=>120}, {"time_period.flex.to"=>120}]
90
+ # criteria.selector["period.from"].should be_a Hash
91
+ # end
92
+
93
+ it 'should find #1, #2, #3, #4, #5' do
94
+ criteria.to_a.map(&:name).should include '1', '2', '5', '7'
95
+ end
96
+
97
+ let(:time_period) { criteria.first.time_period }
98
+
99
+ specify do
100
+ time_period.flex.should be_a DurationRange
101
+ time_period.flex.min.should == 2.minutes
102
+ time_period.flex.max.should == 5.minutes
103
+ end
104
+ end
105
+
106
+ describe 'ASAP 3 minutes' do
107
+ let(:period) { 3.minutes.to_i }
108
+
109
+ let(:criteria) do
110
+ Account.where(where_hash).or(exactly)
111
+ end
112
+
113
+ it 'should find #4, #10' do
114
+ # puts criteria.to_a.map(&:name)
115
+ criteria.to_a.map(&:name).should include '4', '10'
116
+ end
117
+ end
118
+
119
+ describe 'ASAP 4 minutes' do
120
+ let(:period) { 4.minutes.to_i }
121
+
122
+ let(:criteria) do
123
+ Account.where(where_hash).or(exactly)
124
+ end
125
+
126
+ it 'should find #2, #3' do
127
+ criteria.to_a.should include(@acc2, @acc3)
128
+ end
129
+ end
130
+
131
+ describe 'ASAP 1-4 minutes' do
132
+ let(:min_period) { 1.minute }
133
+ let(:max_period) { 4.minutes }
134
+
135
+ let(:criteria) do
136
+ Account.where(where_hash).or(in_between)
137
+ end
138
+
139
+ it 'should find #2, #3' do
140
+ # puts "ASAP 1-4 minutes"
141
+ # puts criteria.selector
142
+ # puts criteria.to_a.map(&:name)
143
+ criteria.to_a.map(&:name).should_not include('8', '9')
144
+ criteria.to_a.map(&:name).should include('2', '3')
145
+ end
146
+ end
147
+
148
+ describe 'ASAP 3-5 minutes' do
149
+ let(:min_period) { 3.minutes }
150
+ let(:max_period) { 5.minutes }
151
+
152
+ it 'should have a nice criteria' do
153
+ # puts "ASAP 3-5 minutes"
154
+ # puts criteria.selector
155
+ criteria.selector["time_period.flex.from"] = 180
156
+ criteria.selector["time_period.flex.to"] = 300
157
+ criteria.selector["period.from"].should be_a Hash
158
+ end
159
+
160
+ let(:criteria) do
161
+ Account.where(where_hash).or(in_between)
162
+ end
163
+
164
+ it 'should find #2, #3' do
165
+ # puts criteria.to_a.map(&:name)
166
+ criteria.to_a.map(&:name).should_not include('8', '9')
167
+ criteria.to_a.map(&:name).should include('10')
168
+ end
169
+ end
170
+
171
+ describe 'ASAP 5-7 minutes' do
172
+ let(:min_period) { 5.minutes }
173
+ let(:max_period) { 7.minutes }
174
+
175
+ let(:criteria) do
176
+ Account.where(where_hash).or(in_between)
177
+ end
178
+
179
+ it 'should find #2, #3' do
180
+ # puts criteria.to_a.map(&:name)
181
+ criteria.to_a.map(&:name).should_not include('6', '9', '10')
182
+ criteria.to_a.map(&:name).should include('8')
183
+ end
184
+ end
185
+
186
+ describe 'ASAP at least 4 minutes' do
187
+ let(:min_period) { 4.minutes }
188
+
189
+ let(:criteria) do
190
+ Account.where where_hash.merge(at_least)
191
+ end
192
+
193
+ it 'should find #2, #3' do
194
+ # puts criteria.selector
195
+ # puts criteria.to_a.map(&:name)
196
+ criteria.to_a.map(&:name).should_not include '9'
197
+ criteria.to_a.map(&:name).should include '1', '2', '3', '8', '10'
198
+ end
199
+ end
200
+
201
+ describe 'ASAP at most 3 minutes' do
202
+ let(:max_period) { 3.minutes }
203
+
204
+ let(:criteria) do
205
+ Account.where where_hash.merge(at_most)
206
+ end
207
+
208
+ it 'should find #2, #3' do
209
+ # puts criteria.selector
210
+ # puts criteria.to_a.map(&:name)
211
+ criteria.to_a.map(&:name).should_not include '8', '9', '10'
212
+ criteria.to_a.map(&:name).should include '4', '5', '7'
213
+ end
214
+ end
215
+ end
216
+ end
@@ -4,12 +4,15 @@ class Account
4
4
  include Mongoid::Document
5
5
  include Mongoid::Timespanned
6
6
 
7
+ field :name
7
8
  field :period, :type => ::Timespan, :between => true
8
9
 
9
10
  timespan_methods :period
10
11
 
11
12
  embeds_one :time_period
12
- timespan_container_delegates :time_period, :dates, :all #:start, :end
13
+
14
+ timespan_container_delegates :time_period, :dates, :all
15
+ timespan_container_delegates :time_period, :flex, :all
13
16
 
14
17
  def self.create_it! duration
15
18
  acc = self.new period: ::Timespan.new(duration: duration), time_period: ::TimePeriod.new
@@ -17,6 +20,12 @@ class Account
17
20
  acc
18
21
  end
19
22
 
23
+ # def to_s
24
+ # %Q{
25
+ # name: #{name}
26
+ # }
27
+ # end
28
+
20
29
  def self.between from, to
21
30
  Account.where(:'period.from'.gt => from.to_i, :'period.to'.lte => to.to_i)
22
31
  end
@@ -2,7 +2,8 @@ class TimePeriod
2
2
  include Mongoid::Document
3
3
  include Mongoid::Timespanned
4
4
 
5
- field :dates, :type => ::Timespan, :between => true
5
+ field :dates, :type => ::Timespan, :between => true
6
+ field :flex, :type => ::DurationRange
6
7
 
7
8
  embedded_in :account
8
9
 
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.4.6"
8
+ s.version = "0.4.9"
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-09-24"
12
+ s.date = "2012-09-25"
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 = [
@@ -28,6 +28,9 @@ Gem::Specification.new do |s|
28
28
  "config/locales/timespan/da.yml",
29
29
  "lib/timespan.rb",
30
30
  "lib/timespan/compare.rb",
31
+ "lib/timespan/core_ext.rb",
32
+ "lib/timespan/core_ext/hash.rb",
33
+ "lib/timespan/core_ext/range.rb",
31
34
  "lib/timespan/mongoid.rb",
32
35
  "lib/timespan/mongoid/mongoid_2x.rb",
33
36
  "lib/timespan/mongoid/mongoid_3x.rb",
@@ -38,8 +41,11 @@ Gem::Specification.new do |s|
38
41
  "lib/timespan/units.rb",
39
42
  "spec/spec_helper.rb",
40
43
  "spec/timespan/compare_spec.rb",
44
+ "spec/timespan/core_ext/duration_range_spec.rb",
45
+ "spec/timespan/core_ext/timespan_range_spec.rb",
41
46
  "spec/timespan/duration_macros_spec.rb",
42
47
  "spec/timespan/locales/duration_da.yml",
48
+ "spec/timespan/mongoid/advanced_search_spec.rb",
43
49
  "spec/timespan/mongoid/models/account_2x.rb",
44
50
  "spec/timespan/mongoid/models/account_3x.rb",
45
51
  "spec/timespan/mongoid/models/time_period.rb",
@@ -70,10 +76,12 @@ Gem::Specification.new do |s|
70
76
  s.add_runtime_dependency(%q<chronic_duration>, [">= 0"])
71
77
  s.add_runtime_dependency(%q<activesupport>, [">= 3.0.0"])
72
78
  s.add_runtime_dependency(%q<spanner>, [">= 0"])
79
+ s.add_runtime_dependency(%q<sugar-high>, ["~> 0.7.2"])
73
80
  s.add_runtime_dependency(%q<xduration>, ["~> 2.2"])
74
81
  s.add_development_dependency(%q<rspec>, [">= 2.8.0"])
75
82
  s.add_development_dependency(%q<rails>, ["~> 3.2.7"])
76
- s.add_development_dependency(%q<mongoid>, ["~> 3.0.0"])
83
+ s.add_development_dependency(%q<mongoid>, ["~> 3.0"])
84
+ s.add_development_dependency(%q<origin-selectable_ext>, ["~> 0.1.1"])
77
85
  s.add_development_dependency(%q<rdoc>, [">= 3.12"])
78
86
  s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
79
87
  s.add_development_dependency(%q<jeweler>, [">= 1.8.3"])
@@ -83,10 +91,12 @@ Gem::Specification.new do |s|
83
91
  s.add_dependency(%q<chronic_duration>, [">= 0"])
84
92
  s.add_dependency(%q<activesupport>, [">= 3.0.0"])
85
93
  s.add_dependency(%q<spanner>, [">= 0"])
94
+ s.add_dependency(%q<sugar-high>, ["~> 0.7.2"])
86
95
  s.add_dependency(%q<xduration>, ["~> 2.2"])
87
96
  s.add_dependency(%q<rspec>, [">= 2.8.0"])
88
97
  s.add_dependency(%q<rails>, ["~> 3.2.7"])
89
- s.add_dependency(%q<mongoid>, ["~> 3.0.0"])
98
+ s.add_dependency(%q<mongoid>, ["~> 3.0"])
99
+ s.add_dependency(%q<origin-selectable_ext>, ["~> 0.1.1"])
90
100
  s.add_dependency(%q<rdoc>, [">= 3.12"])
91
101
  s.add_dependency(%q<bundler>, [">= 1.0.0"])
92
102
  s.add_dependency(%q<jeweler>, [">= 1.8.3"])
@@ -97,10 +107,12 @@ Gem::Specification.new do |s|
97
107
  s.add_dependency(%q<chronic_duration>, [">= 0"])
98
108
  s.add_dependency(%q<activesupport>, [">= 3.0.0"])
99
109
  s.add_dependency(%q<spanner>, [">= 0"])
110
+ s.add_dependency(%q<sugar-high>, ["~> 0.7.2"])
100
111
  s.add_dependency(%q<xduration>, ["~> 2.2"])
101
112
  s.add_dependency(%q<rspec>, [">= 2.8.0"])
102
113
  s.add_dependency(%q<rails>, ["~> 3.2.7"])
103
- s.add_dependency(%q<mongoid>, ["~> 3.0.0"])
114
+ s.add_dependency(%q<mongoid>, ["~> 3.0"])
115
+ s.add_dependency(%q<origin-selectable_ext>, ["~> 0.1.1"])
104
116
  s.add_dependency(%q<rdoc>, [">= 3.12"])
105
117
  s.add_dependency(%q<bundler>, [">= 1.0.0"])
106
118
  s.add_dependency(%q<jeweler>, [">= 1.8.3"])
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.6
4
+ version: 0.4.9
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-09-24 00:00:00.000000000 Z
12
+ date: 2012-09-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: chronic
@@ -75,6 +75,22 @@ dependencies:
75
75
  - - ! '>='
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: sugar-high
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 0.7.2
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 0.7.2
78
94
  - !ruby/object:Gem::Dependency
79
95
  name: xduration
80
96
  requirement: !ruby/object:Gem::Requirement
@@ -130,7 +146,7 @@ dependencies:
130
146
  requirements:
131
147
  - - ~>
132
148
  - !ruby/object:Gem::Version
133
- version: 3.0.0
149
+ version: '3.0'
134
150
  type: :development
135
151
  prerelease: false
136
152
  version_requirements: !ruby/object:Gem::Requirement
@@ -138,7 +154,23 @@ dependencies:
138
154
  requirements:
139
155
  - - ~>
140
156
  - !ruby/object:Gem::Version
141
- version: 3.0.0
157
+ version: '3.0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: origin-selectable_ext
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ~>
164
+ - !ruby/object:Gem::Version
165
+ version: 0.1.1
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ~>
172
+ - !ruby/object:Gem::Version
173
+ version: 0.1.1
142
174
  - !ruby/object:Gem::Dependency
143
175
  name: rdoc
144
176
  requirement: !ruby/object:Gem::Requirement
@@ -222,6 +254,9 @@ files:
222
254
  - config/locales/timespan/da.yml
223
255
  - lib/timespan.rb
224
256
  - lib/timespan/compare.rb
257
+ - lib/timespan/core_ext.rb
258
+ - lib/timespan/core_ext/hash.rb
259
+ - lib/timespan/core_ext/range.rb
225
260
  - lib/timespan/mongoid.rb
226
261
  - lib/timespan/mongoid/mongoid_2x.rb
227
262
  - lib/timespan/mongoid/mongoid_3x.rb
@@ -232,8 +267,11 @@ files:
232
267
  - lib/timespan/units.rb
233
268
  - spec/spec_helper.rb
234
269
  - spec/timespan/compare_spec.rb
270
+ - spec/timespan/core_ext/duration_range_spec.rb
271
+ - spec/timespan/core_ext/timespan_range_spec.rb
235
272
  - spec/timespan/duration_macros_spec.rb
236
273
  - spec/timespan/locales/duration_da.yml
274
+ - spec/timespan/mongoid/advanced_search_spec.rb
237
275
  - spec/timespan/mongoid/models/account_2x.rb
238
276
  - spec/timespan/mongoid/models/account_3x.rb
239
277
  - spec/timespan/mongoid/models/time_period.rb
@@ -264,7 +302,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
264
302
  version: '0'
265
303
  segments:
266
304
  - 0
267
- hash: -1422999725425630205
305
+ hash: -4405468294374256560
268
306
  required_rubygems_version: !ruby/object:Gem::Requirement
269
307
  none: false
270
308
  requirements: