by_star 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/mysql.yml +92 -0
  3. data/.github/workflows/postgresql.yml +99 -0
  4. data/.gitignore +1 -0
  5. data/.travis.yml +67 -36
  6. data/CHANGELOG.md +15 -0
  7. data/README.md +172 -133
  8. data/UPGRADING +1 -3
  9. data/by_star.gemspec +2 -2
  10. data/lib/by_star/base.rb +16 -15
  11. data/lib/by_star/between.rb +81 -52
  12. data/lib/by_star/directional.rb +2 -4
  13. data/lib/by_star/kernel/date.rb +0 -9
  14. data/lib/by_star/kernel/in_time_zone.rb +20 -0
  15. data/lib/by_star/normalization.rb +52 -23
  16. data/lib/by_star/orm/active_record/by_star.rb +20 -6
  17. data/lib/by_star/orm/mongoid/by_star.rb +20 -6
  18. data/lib/by_star/version.rb +1 -1
  19. data/lib/by_star.rb +1 -0
  20. data/spec/database.yml +1 -1
  21. data/spec/fixtures/active_record/models.rb +2 -2
  22. data/spec/fixtures/mongoid/models.rb +1 -1
  23. data/spec/fixtures/shared/seeds.rb +10 -0
  24. data/spec/gemfiles/Gemfile.rails +5 -0
  25. data/spec/gemfiles/Gemfile.rails32 +7 -0
  26. data/spec/gemfiles/Gemfile.rails40 +1 -1
  27. data/spec/gemfiles/Gemfile.rails41 +1 -1
  28. data/spec/gemfiles/Gemfile.rails42 +2 -2
  29. data/spec/gemfiles/Gemfile.rails50 +1 -4
  30. data/spec/gemfiles/Gemfile.rails51 +1 -4
  31. data/spec/gemfiles/Gemfile.rails52 +7 -0
  32. data/spec/gemfiles/Gemfile.rails60 +7 -0
  33. data/spec/gemfiles/Gemfile.rails61 +7 -0
  34. data/spec/integration/active_record/active_record_spec.rb +6 -3
  35. data/spec/integration/mongoid/mongoid_spec.rb +3 -1
  36. data/spec/integration/shared/at_time.rb +53 -0
  37. data/spec/integration/shared/between_dates.rb +99 -0
  38. data/spec/integration/shared/between_times.rb +27 -10
  39. data/spec/integration/shared/by_day.rb +24 -0
  40. data/spec/integration/shared/by_direction.rb +11 -57
  41. data/spec/integration/shared/index_scope_parameter.rb +111 -0
  42. data/spec/spec_helper.rb +4 -0
  43. data/spec/unit/kernel_date_spec.rb +6 -6
  44. data/spec/unit/normalization_spec.rb +128 -49
  45. metadata +33 -21
  46. data/spec/gemfiles/Gemfile.master +0 -6
  47. data/spec/integration/shared/scope_parameter.rb +0 -73
data/lib/by_star/base.rb CHANGED
@@ -11,6 +11,7 @@ module ByStar
11
11
  @by_star_end_field ||= args[1]
12
12
  @by_star_offset ||= options[:offset]
13
13
  @by_star_scope ||= options[:scope]
14
+ @by_star_index_scope ||= options[:index_scope]
14
15
  end
15
16
 
16
17
  def by_star_offset(options = {})
@@ -33,21 +34,6 @@ module ByStar
33
34
  field.to_s
34
35
  end
35
36
 
36
- def by_star_scope(options={})
37
- scope = options[:scope] || @by_star_scope || self
38
- if scope.is_a?(Proc)
39
- if scope.arity == 0
40
- return instance_exec(&scope)
41
- elsif options[:scope_args]
42
- return instance_exec(*Array(options[:scope_args]), &scope)
43
- else
44
- raise 'ByStar :scope Proc requires :scope_args to be specified.'
45
- end
46
- else
47
- return scope
48
- end
49
- end
50
-
51
37
  protected
52
38
 
53
39
  # Wrapper function which extracts time and options for each by_star query.
@@ -64,5 +50,20 @@ module ByStar
64
50
  time = args.first || Time.zone.now
65
51
  block.call(time, options)
66
52
  end
53
+
54
+ def by_star_eval_index_scope(start_time, end_time, options)
55
+ value = options[:index_scope] || @by_star_index_scope
56
+ value = value.call(start_time, end_time, options) if value.is_a?(Proc)
57
+ case value
58
+ when nil, false then nil
59
+ when Time, DateTime, Date then value.in_time_zone
60
+ when ActiveSupport::Duration then start_time - value
61
+ when Numeric then start_time - value.seconds
62
+ when :beginning_of_day
63
+ offset = options[:offset] || 0
64
+ (start_time - offset).beginning_of_day + offset
65
+ else raise 'ByStar :index_scope option value is not a supported type.'
66
+ end
67
+ end
67
68
  end
68
69
  end
@@ -5,19 +5,25 @@ module ByStar
5
5
  def between_times(*args)
6
6
  options = args.extract_options!.symbolize_keys!
7
7
 
8
- start_time, end_time = case args[0]
9
- when Array, Range then [args[0].first, args[0].last]
10
- else args[0..1]
11
- end
8
+ start_time, end_time = ByStar::Normalization.extract_range(args)
9
+ offset = options[:offset] || 0
12
10
 
13
- offset = (options[:offset] || 0).seconds
14
- start_time += offset if start_time
15
- end_time += offset if end_time
11
+ if start_time.is_a?(Date)
12
+ start_time = ByStar::Normalization.apply_offset_start(start_time.in_time_zone, offset)
13
+ elsif start_time
14
+ start_time += offset.seconds
15
+ end
16
+
17
+ if end_time.is_a?(Date)
18
+ end_time = ByStar::Normalization.apply_offset_end(end_time.in_time_zone, offset)
19
+ elsif end_time
20
+ end_time += offset.seconds
21
+ end
16
22
 
17
23
  start_field = by_star_start_field(options)
18
24
  end_field = by_star_end_field(options)
19
- scope = by_star_scope(options)
20
25
 
26
+ scope = self
21
27
  scope = if !start_time && !end_time
22
28
  scope # do nothing
23
29
  elsif !end_time
@@ -29,26 +35,49 @@ module ByStar
29
35
  elsif options[:strict]
30
36
  by_star_span_strict_query(scope, start_field, end_field, start_time, end_time)
31
37
  else
32
- by_star_span_overlap_query(scope, start_field, end_field, start_time, end_time, options)
38
+ by_star_span_loose_query(scope, start_field, end_field, start_time, end_time, options)
33
39
  end
34
40
 
35
41
  scope = by_star_order(scope, options[:order]) if options[:order]
36
-
37
42
  scope
38
43
  end
39
44
 
45
+ def between_dates(*args)
46
+ options = args.extract_options!
47
+ start_date, end_date = ByStar::Normalization.extract_range(args)
48
+ start_date = ByStar::Normalization.date(start_date)
49
+ end_date = ByStar::Normalization.date(end_date)
50
+ between_times(start_date, end_date, options)
51
+ end
52
+
53
+ def at_time(*args)
54
+ with_by_star_options(*args) do |time, options|
55
+ start_field = by_star_start_field(options)
56
+ end_field = by_star_end_field(options)
57
+
58
+ scope = self
59
+ scope = if start_field == end_field
60
+ by_star_point_overlap_query(scope, start_field, time)
61
+ else
62
+ by_star_span_overlap_query(scope, start_field, end_field, time, options)
63
+ end
64
+ scope = by_star_order(scope, options[:order]) if options[:order]
65
+ scope
66
+ end
67
+ end
68
+
40
69
  def by_day(*args)
41
70
  with_by_star_options(*args) do |time, options|
42
- time = ByStar::Normalization.time(time)
43
- between_times(time.beginning_of_day, time.end_of_day, options)
71
+ date = ByStar::Normalization.date(time)
72
+ between_dates(date, date, options)
44
73
  end
45
74
  end
46
75
 
47
76
  def by_week(*args)
48
77
  with_by_star_options(*args) do |time, options|
49
- time = ByStar::Normalization.week(time, options)
78
+ date = ByStar::Normalization.week(time, options)
50
79
  start_day = Array(options[:start_day])
51
- between_times(time.beginning_of_week(*start_day), time.end_of_week(*start_day), options)
80
+ between_dates(date.beginning_of_week(*start_day), date.end_of_week(*start_day), options)
52
81
  end
53
82
  end
54
83
 
@@ -60,97 +89,97 @@ module ByStar
60
89
 
61
90
  def by_weekend(*args)
62
91
  with_by_star_options(*args) do |time, options|
63
- time = ByStar::Normalization.week(time, options)
64
- between_times(time.beginning_of_weekend, time.end_of_weekend, options)
92
+ date = ByStar::Normalization.week(time, options)
93
+ between_dates(date.beginning_of_weekend, date.end_of_weekend, options)
65
94
  end
66
95
  end
67
96
 
68
97
  def by_fortnight(*args)
69
98
  with_by_star_options(*args) do |time, options|
70
- time = ByStar::Normalization.fortnight(time, options)
71
- between_times(time.beginning_of_fortnight, time.end_of_fortnight, options)
99
+ date = ByStar::Normalization.fortnight(time, options)
100
+ between_dates(date.beginning_of_fortnight, date.end_of_fortnight, options)
72
101
  end
73
102
  end
74
103
 
75
104
  def by_month(*args)
76
105
  with_by_star_options(*args) do |time, options|
77
- time = ByStar::Normalization.month(time, options)
78
- between_times(time.beginning_of_month, time.end_of_month, options)
106
+ date = ByStar::Normalization.month(time, options)
107
+ between_dates(date.beginning_of_month, date.end_of_month, options)
79
108
  end
80
109
  end
81
110
 
82
111
  def by_calendar_month(*args)
83
112
  with_by_star_options(*args) do |time, options|
84
- time = ByStar::Normalization.month(time, options)
113
+ date = ByStar::Normalization.month(time, options)
85
114
  start_day = Array(options[:start_day])
86
- between_times(time.beginning_of_calendar_month(*start_day), time.end_of_calendar_month(*start_day), options)
115
+ between_dates(date.beginning_of_calendar_month(*start_day), date.end_of_calendar_month(*start_day), options)
87
116
  end
88
117
  end
89
118
 
90
119
  def by_quarter(*args)
91
120
  with_by_star_options(*args) do |time, options|
92
- time = ByStar::Normalization.quarter(time, options)
93
- between_times(time.beginning_of_quarter, time.end_of_quarter, options)
121
+ date = ByStar::Normalization.quarter(time, options)
122
+ between_dates(date.beginning_of_quarter, date.end_of_quarter, options)
94
123
  end
95
124
  end
96
125
 
97
126
  def by_year(*args)
98
127
  with_by_star_options(*args) do |time, options|
99
- time = ByStar::Normalization.year(time, options)
100
- between_times(time.beginning_of_year, time.end_of_year, options)
128
+ date = ByStar::Normalization.year(time, options)
129
+ between_dates(date.beginning_of_year, date.to_date.end_of_year, options)
101
130
  end
102
131
  end
103
132
 
104
- def today(options={})
105
- by_day(Time.zone.now, options)
133
+ def today(options = {})
134
+ by_day(Date.current, options)
106
135
  end
107
136
 
108
- def yesterday(options={})
109
- by_day(Time.zone.now.yesterday, options)
137
+ def yesterday(options = {})
138
+ by_day(Date.yesterday, options)
110
139
  end
111
140
 
112
- def tomorrow(options={})
113
- by_day(Time.zone.now.tomorrow, options)
141
+ def tomorrow(options = {})
142
+ by_day(Date.tomorrow, options)
114
143
  end
115
144
 
116
- def past_day(options={})
117
- between_times(Time.zone.now - 1.day, Time.zone.now, options)
145
+ def past_day(options = {})
146
+ between_times(Time.current - 1.day, Time.current, options)
118
147
  end
119
148
 
120
- def past_week(options={})
121
- between_times(Time.zone.now - 1.week, Time.zone.now, options)
149
+ def past_week(options = {})
150
+ between_times(Time.current - 1.week, Time.current, options)
122
151
  end
123
152
 
124
- def past_fortnight(options={})
125
- between_times(Time.zone.now - 2.weeks, Time.zone.now, options)
153
+ def past_fortnight(options = {})
154
+ between_times(Time.current - 2.weeks, Time.current, options)
126
155
  end
127
156
 
128
- def past_month(options={})
129
- between_times(Time.zone.now - 1.month, Time.zone.now, options)
157
+ def past_month(options = {})
158
+ between_times(Time.current - 1.month, Time.current, options)
130
159
  end
131
160
 
132
- def past_year(options={})
133
- between_times(Time.zone.now - 1.year, Time.zone.now, options)
161
+ def past_year(options = {})
162
+ between_times(Time.current - 1.year, Time.current, options)
134
163
  end
135
164
 
136
- def next_day(options={})
137
- between_times(Time.zone.now, Time.zone.now + 1.day, options)
165
+ def next_day(options = {})
166
+ between_times(Time.current, Time.current + 1.day, options)
138
167
  end
139
168
 
140
- def next_week(options={})
141
- between_times(Time.zone.now, Time.zone.now + 1.week, options)
169
+ def next_week(options = {})
170
+ between_times(Time.current, Time.current + 1.week, options)
142
171
  end
143
172
 
144
- def next_fortnight(options={})
145
- between_times(Time.zone.now, Time.zone.now + 2.weeks, options)
173
+ def next_fortnight(options = {})
174
+ between_times(Time.current, Time.current + 2.weeks, options)
146
175
  end
147
176
 
148
- def next_month(options={})
149
- between_times(Time.zone.now, Time.zone.now + 1.month, options)
177
+ def next_month(options = {})
178
+ between_times(Time.current, Time.current + 1.month, options)
150
179
  end
151
180
 
152
- def next_year(options={})
153
- between_times(Time.zone.now, Time.zone.now + 1.year, options)
181
+ def next_year(options = {})
182
+ between_times(Time.current, Time.current + 1.year, options)
154
183
  end
155
184
  end
156
185
  end
@@ -4,20 +4,18 @@ module ByStar
4
4
 
5
5
  def before(*args)
6
6
  with_by_star_options(*args) do |time, options|
7
- scope = by_star_scope(options)
8
7
  field = by_star_start_field(options)
9
8
  time = ByStar::Normalization.time(time)
10
- by_star_before_query(scope, field, time)
9
+ by_star_before_query(self, field, time)
11
10
  end
12
11
  end
13
12
  alias_method :before_now, :before
14
13
 
15
14
  def after(*args)
16
15
  with_by_star_options(*args) do |time, options|
17
- scope = by_star_scope(options)
18
16
  field = by_star_start_field(options)
19
17
  time = ByStar::Normalization.time(time)
20
- by_star_after_query(scope, field, time)
18
+ by_star_after_query(self, field, time)
21
19
  end
22
20
  end
23
21
  alias_method :after_now, :after
@@ -3,15 +3,6 @@ module ByStar
3
3
  module Kernel
4
4
 
5
5
  module Date
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
-
10
- # Shim to alias Rails 3 #to_time_in_current_zone to Rails 4 name #in_time_zone
11
- if method_defined?(:to_time_in_current_zone) && !method_defined?(:in_time_zone)
12
- alias_method :in_time_zone, :to_time_in_current_zone
13
- end
14
- end
15
6
 
16
7
  # A "Weekend" is defined as beginning of Saturday to end of Sunday.
17
8
  # The weekend for a given date will be the the next weekend if the day Mon-Thurs,
@@ -0,0 +1,20 @@
1
+ module ByStar
2
+
3
+ module Kernel
4
+
5
+ module InTimeZone
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ if method_defined?(:to_time_in_current_zone) && !method_defined?(:in_time_zone)
10
+ alias_method :in_time_zone, :to_time_in_current_zone
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ ::Date.__send__(:include, ByStar::Kernel::InTimeZone)
18
+ ::Time.__send__(:include, ByStar::Kernel::InTimeZone)
19
+ ::DateTime.__send__(:include, ByStar::Kernel::InTimeZone)
20
+ ::ActiveSupport::TimeWithZone.__send__(:include, ByStar::Kernel::InTimeZone)
@@ -6,33 +6,22 @@ module ByStar
6
6
 
7
7
  class << self
8
8
 
9
- def time(value)
10
- case value
11
- when String then time_string(value)
12
- when DateTime then value.to_time
13
- when Date then value.in_time_zone
14
- else value
15
- end
16
- end
17
-
18
- def time_string(value)
19
- defined?(Chronic) ? time_string_chronic(value) : time_string_fallback(value)
9
+ def date(value)
10
+ value = parse_time(value) if value.is_a?(String)
11
+ value = value.try(:in_time_zone) unless value.is_a?(Date)
12
+ value.try(:to_date)
20
13
  end
21
14
 
22
- def time_string_chronic(value)
23
- Chronic.time_class = Time.zone
24
- Chronic.parse(value) || raise(ByStar::ParseError, "Chronic could not parse String #{value.inspect}")
25
- end
26
-
27
- def time_string_fallback(value)
28
- Time.zone.parse(value) || raise(ByStar::ParseError, "Cannot parse String #{value.inspect}")
15
+ def time(value)
16
+ value = parse_time(value) if value.is_a?(String)
17
+ value.try(:in_time_zone)
29
18
  end
30
19
 
31
20
  def week(value, options={})
32
21
  value = try_string_to_int(value)
33
22
  case value
34
23
  when Integer then week_integer(value, options)
35
- else time(value)
24
+ else date(value)
36
25
  end
37
26
  end
38
27
 
@@ -55,7 +44,7 @@ module ByStar
55
44
  value = try_string_to_int(value)
56
45
  case value
57
46
  when Integer then fortnight_integer(value, options)
58
- else time(value)
47
+ else date(value)
59
48
  end
60
49
  end
61
50
 
@@ -69,7 +58,7 @@ module ByStar
69
58
  value = try_string_to_int(value)
70
59
  case value
71
60
  when Integer then quarter_integer(value, options)
72
- else time(value)
61
+ else date(value)
73
62
  end
74
63
  end
75
64
 
@@ -83,7 +72,7 @@ module ByStar
83
72
  value = try_string_to_int(value)
84
73
  case value
85
74
  when Integer, String then month_integer(value, options)
86
- else time(value)
75
+ else date(value)
87
76
  end
88
77
  end
89
78
 
@@ -98,7 +87,7 @@ module ByStar
98
87
  value = try_string_to_int(value)
99
88
  case value
100
89
  when Integer then year_integer(value)
101
- else time(value)
90
+ else date(value)
102
91
  end
103
92
  end
104
93
 
@@ -122,6 +111,46 @@ module ByStar
122
111
  rescue
123
112
  value
124
113
  end
114
+
115
+ def time_in_units(seconds)
116
+ days = seconds / 1.day
117
+ time = Time.at(seconds).utc
118
+ { days: days, hour: time.hour, min: time.min, sec: time.sec }
119
+ end
120
+
121
+ def apply_offset_start(time, offset)
122
+ units = time_in_units(offset)
123
+ time += units.delete(:days).days
124
+ time.change(units)
125
+ end
126
+
127
+ def apply_offset_end(time, offset)
128
+ units = time_in_units(offset)
129
+ time += units.delete(:days).days
130
+ (time + 1.day).change(units) - 1.second
131
+ end
132
+
133
+ def extract_range(args)
134
+ case args[0]
135
+ when Array, Range then [args[0].first, args[0].last]
136
+ else args[0..1]
137
+ end
138
+ end
139
+
140
+ private
141
+
142
+ def parse_time(value)
143
+ defined?(Chronic) ? parse_time_chronic(value) : parse_time_fallback(value)
144
+ end
145
+
146
+ def parse_time_chronic(value)
147
+ Chronic.time_class = Time.zone
148
+ Chronic.parse(value) || raise(ByStar::ParseError, "Chronic could not parse String #{value.inspect}")
149
+ end
150
+
151
+ def parse_time_fallback(value)
152
+ Time.zone.parse(value) || raise(ByStar::ParseError, "Cannot parse String #{value.inspect}")
153
+ end
125
154
  end
126
155
  end
127
156
  end
@@ -19,8 +19,22 @@ module ByStar
19
19
  scope.where("#{start_field} >= ? AND #{start_field} <= ? AND #{end_field} >= ? AND #{end_field} <= ?", start_time, end_time, start_time, end_time)
20
20
  end
21
21
 
22
- def by_star_span_overlap_query(scope, start_field, end_field, start_time, end_time, options)
23
- scope.where("#{end_field} > ? AND #{start_field} < ?", start_time, end_time)
22
+ def by_star_span_loose_query(scope, start_field, end_field, start_time, end_time, options)
23
+ index_scope = by_star_eval_index_scope(start_time, end_time, options)
24
+ scope = scope.where("#{end_field} > ? AND #{start_field} < ?", start_time, end_time)
25
+ scope = scope.where("#{start_field} >= ?", index_scope) if index_scope
26
+ scope
27
+ end
28
+
29
+ def by_star_point_overlap_query(scope, field, time)
30
+ scope.where("#{field} = ?", time)
31
+ end
32
+
33
+ def by_star_span_overlap_query(scope, start_field, end_field, time, options)
34
+ index_scope = by_star_eval_index_scope(time, time, options)
35
+ scope = scope.where("#{end_field} > ? AND #{start_field} <= ?", time, time)
36
+ scope = scope.where("#{start_field} >= ?", index_scope) if index_scope
37
+ scope
24
38
  end
25
39
 
26
40
  def by_star_before_query(scope, field, time)
@@ -37,25 +51,25 @@ module ByStar
37
51
 
38
52
  def oldest_query(options={})
39
53
  field = by_star_start_field(options)
40
- by_star_scope(options).reorder("#{field} ASC").first
54
+ reorder("#{field} ASC").first
41
55
  end
42
56
 
43
57
  def newest_query(options={})
44
58
  field = by_star_start_field(options)
45
- by_star_scope(options).reorder("#{field} DESC").first
59
+ reorder("#{field} DESC").first
46
60
  end
47
61
  end
48
62
 
49
63
  def previous(options={})
50
64
  field = self.class.by_star_start_field(options)
51
65
  value = self.send(field.split(".").last)
52
- self.class.by_star_scope(options.merge(scope_args: self)).where("#{field} < ?", value).reorder("#{field} DESC").first
66
+ self.class.where("#{field} < ?", value).reorder("#{field} DESC").first
53
67
  end
54
68
 
55
69
  def next(options={})
56
70
  field = self.class.by_star_start_field(options)
57
71
  value = self.send(field.split(".").last)
58
- self.class.by_star_scope(options.merge(scope_args: self)).where("#{field} > ?", value).reorder("#{field} ASC").first
72
+ self.class.where("#{field} > ?", value).reorder("#{field} ASC").first
59
73
  end
60
74
  end
61
75
  end
@@ -36,8 +36,22 @@ module Mongoid
36
36
  scope.where(start_field => range).where(end_field => range)
37
37
  end
38
38
 
39
- def by_star_span_overlap_query(scope, start_field, end_field, start_time, end_time, options)
40
- scope.gt(end_field => start_time).lt(start_field => end_time)
39
+ def by_star_span_loose_query(scope, start_field, end_field, start_time, end_time, options)
40
+ index_scope = by_star_eval_index_scope(start_time, end_time, options)
41
+ scope = scope.gt(end_field => start_time).lt(start_field => end_time)
42
+ scope = scope.gte(start_field => index_scope) if index_scope
43
+ scope
44
+ end
45
+
46
+ def by_star_point_overlap_query(scope, field, time)
47
+ scope.where(field => time)
48
+ end
49
+
50
+ def by_star_span_overlap_query(scope, start_field, end_field, time, options)
51
+ index_scope = by_star_eval_index_scope(time, time, options)
52
+ scope = scope.gt(end_field => time).lte(start_field => time)
53
+ scope = scope.gte(start_field => index_scope) if index_scope
54
+ scope
41
55
  end
42
56
 
43
57
  def by_star_before_query(scope, field, time)
@@ -54,23 +68,23 @@ module Mongoid
54
68
 
55
69
  def oldest_query(options={})
56
70
  field = by_star_start_field(options)
57
- by_star_scope(options).all.reorder(field => :asc).first
71
+ all.reorder(field => :asc).first
58
72
  end
59
73
 
60
74
  def newest_query(options={})
61
75
  field = by_star_start_field(options)
62
- by_star_scope(options).all.reorder(field => :desc).first
76
+ all.reorder(field => :desc).first
63
77
  end
64
78
  end
65
79
 
66
80
  def previous(options={})
67
81
  field = self.class.by_star_start_field(options)
68
- self.class.by_star_scope(options.merge(scope_args: self)).lt(field => self.send(field)).reorder(field => :desc).first
82
+ self.class.lt(field => self.send(field)).reorder(field => :desc).first
69
83
  end
70
84
 
71
85
  def next(options={})
72
86
  field = self.class.by_star_start_field(options)
73
- self.class.by_star_scope(options.merge(scope_args: self)).gt(field => self.send(field)).reorder(field => :asc).first
87
+ self.class.gt(field => self.send(field)).reorder(field => :asc).first
74
88
  end
75
89
  end
76
90
  end
@@ -1,3 +1,3 @@
1
1
  module ByStar
2
- VERSION = '3.0.0'
2
+ VERSION = '4.0.0'
3
3
  end
data/lib/by_star.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'by_star/kernel/in_time_zone'
1
2
  require 'by_star/kernel/time'
2
3
  require 'by_star/kernel/date'
3
4
  require 'by_star/normalization'
data/spec/database.yml CHANGED
@@ -5,7 +5,7 @@ sqlite:
5
5
  postgres:
6
6
  adapter: postgresql
7
7
  database: by_star_test
8
- username: postgres
8
+ username: <%= ENV.fetch("USER") || "postgres" %>
9
9
  min_messages: warning
10
10
 
11
11
  mysql:
@@ -2,11 +2,11 @@ class Post < ActiveRecord::Base
2
2
  end
3
3
 
4
4
  class Appointment < ActiveRecord::Base
5
- by_star_field scope: ->{ where(day_of_month: 1) }
5
+ by_star_field index_scope: ->(start){ start - 5.days }
6
6
  end
7
7
 
8
8
  class Event < ActiveRecord::Base
9
9
  by_star_field :start_time, :end_time, offset: 3.hours
10
10
 
11
- default_scope ->{ order(day_of_month: :asc) }
11
+ default_scope ->{ order('day_of_month ASC') }
12
12
  end
@@ -13,7 +13,7 @@ class Appointment
13
13
 
14
14
  field :day_of_month, type: Integer
15
15
 
16
- by_star_field scope: ->{ where(day_of_month: 1) }
16
+ by_star_field index_scope: ->(start){ start - 5.days }
17
17
  end
18
18
 
19
19
  class Event
@@ -24,3 +24,13 @@
24
24
  Appointment.create!(created_at: d, day_of_month: d.day)
25
25
  Event.create!(created_at: d, start_time: d - 5.days, end_time: d + 5.days, day_of_month: d.day)
26
26
  end
27
+
28
+ # Sydney timezone specific records
29
+ %w(
30
+ 2020-04-05
31
+ 2020-10-04
32
+ ).map{|d| Date.parse(d) }.each do |d|
33
+ [1, 4, 5, 10].each do |h|
34
+ Event.create!(start_time: d + h.hour, end_time: d + h.hour + 30.minutes, created_at: Date.parse('2011-01-01').in_time_zone)
35
+ end
36
+ end
@@ -0,0 +1,5 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec path: '../../'
4
+
5
+ gem 'activerecord', github: 'rails', branch: "main"
@@ -0,0 +1,7 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec path: '../../'
4
+
5
+ gem 'activerecord', '~> 3.2.0'
6
+ gem 'pg', '~> 0.11'
7
+ gem 'mongoid'
@@ -4,4 +4,4 @@ gemspec path: '../../'
4
4
 
5
5
  gem 'activerecord', '~> 4.0.0'
6
6
  gem 'pg', '~> 0.11'
7
- gem 'mongoid', '~> 4.0.0'
7
+ gem 'mongoid'
@@ -4,4 +4,4 @@ gemspec path: '../../'
4
4
 
5
5
  gem 'activerecord', '~> 4.1.0'
6
6
  gem 'pg', '~> 0.11'
7
- gem 'mongoid', '~> 4.0.0'
7
+ gem 'mongoid'
@@ -2,6 +2,6 @@ source 'http://rubygems.org'
2
2
 
3
3
  gemspec path: '../../'
4
4
 
5
- gem 'mongoid', '~> 4.0.0'
6
- gem 'pg', '~> 0.15'
7
5
  gem 'activerecord', '~> 4.2.0'
6
+ gem 'pg', '~> 0.15'
7
+ gem 'mongoid'