by_star 3.0.0 → 4.0.0

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.
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'