ta_by_star 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +59 -0
  3. data/Gemfile +18 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +616 -0
  6. data/Rakefile +18 -0
  7. data/UPGRADING +4 -0
  8. data/by_star.gemspec +34 -0
  9. data/cleaner.rb +25 -0
  10. data/lib/by_star/base.rb +76 -0
  11. data/lib/by_star/between.rb +190 -0
  12. data/lib/by_star/directional.rb +35 -0
  13. data/lib/by_star/kernel/date.rb +41 -0
  14. data/lib/by_star/kernel/in_time_zone.rb +20 -0
  15. data/lib/by_star/kernel/time.rb +41 -0
  16. data/lib/by_star/normalization.rb +156 -0
  17. data/lib/by_star/orm/active_record/by_star.rb +75 -0
  18. data/lib/by_star/orm/mongoid/by_star.rb +90 -0
  19. data/lib/by_star/orm/mongoid/reorder.rb +23 -0
  20. data/lib/by_star/version.rb +3 -0
  21. data/lib/by_star.rb +18 -0
  22. data/spec/database.yml +15 -0
  23. data/spec/fixtures/active_record/models.rb +12 -0
  24. data/spec/fixtures/active_record/schema.rb +19 -0
  25. data/spec/fixtures/mongoid/models.rb +31 -0
  26. data/spec/fixtures/shared/seeds.rb +36 -0
  27. data/spec/gemfiles/Gemfile.rails +5 -0
  28. data/spec/gemfiles/Gemfile.rails32 +7 -0
  29. data/spec/gemfiles/Gemfile.rails40 +7 -0
  30. data/spec/gemfiles/Gemfile.rails41 +7 -0
  31. data/spec/gemfiles/Gemfile.rails42 +7 -0
  32. data/spec/gemfiles/Gemfile.rails50 +7 -0
  33. data/spec/gemfiles/Gemfile.rails51 +7 -0
  34. data/spec/gemfiles/Gemfile.rails52 +7 -0
  35. data/spec/gemfiles/Gemfile.rails60 +7 -0
  36. data/spec/gemfiles/Gemfile.rails61 +7 -0
  37. data/spec/integration/active_record/active_record_spec.rb +41 -0
  38. data/spec/integration/mongoid/mongoid_spec.rb +39 -0
  39. data/spec/integration/shared/at_time.rb +53 -0
  40. data/spec/integration/shared/between_dates.rb +99 -0
  41. data/spec/integration/shared/between_times.rb +99 -0
  42. data/spec/integration/shared/by_calendar_month.rb +55 -0
  43. data/spec/integration/shared/by_cweek.rb +54 -0
  44. data/spec/integration/shared/by_day.rb +120 -0
  45. data/spec/integration/shared/by_direction.rb +126 -0
  46. data/spec/integration/shared/by_fortnight.rb +48 -0
  47. data/spec/integration/shared/by_month.rb +50 -0
  48. data/spec/integration/shared/by_quarter.rb +49 -0
  49. data/spec/integration/shared/by_week.rb +54 -0
  50. data/spec/integration/shared/by_weekend.rb +49 -0
  51. data/spec/integration/shared/by_year.rb +48 -0
  52. data/spec/integration/shared/index_scope_parameter.rb +111 -0
  53. data/spec/integration/shared/offset_parameter.rb +32 -0
  54. data/spec/integration/shared/order_parameter.rb +36 -0
  55. data/spec/integration/shared/relative.rb +174 -0
  56. data/spec/spec_helper.rb +33 -0
  57. data/spec/unit/kernel_date_spec.rb +113 -0
  58. data/spec/unit/kernel_time_spec.rb +57 -0
  59. data/spec/unit/normalization_spec.rb +384 -0
  60. data/tmp/.gitignore +1 -0
  61. metadata +298 -0
@@ -0,0 +1,76 @@
1
+ module ByStar
2
+
3
+ module Base
4
+
5
+ include ByStar::Between
6
+ include ByStar::Directional
7
+
8
+ def by_star_field(*args)
9
+ options = args.extract_options!
10
+ @by_star_start_field ||= args[0]
11
+ @by_star_end_field ||= args[1]
12
+ @by_star_offset ||= options[:offset]
13
+ @by_star_scope ||= options[:scope]
14
+ @by_star_index_scope ||= options[:index_scope]
15
+ @by_star_field_type ||= options[:field_type]
16
+ end
17
+
18
+ def by_star_offset(options = {})
19
+ (options[:offset] || @by_star_offset || 0).seconds
20
+ end
21
+
22
+ def by_star_start_field(options={})
23
+ field = options[:field] ||
24
+ options[:start_field] ||
25
+ @by_star_start_field ||
26
+ by_star_default_field
27
+ field.to_s
28
+ end
29
+
30
+ def by_star_end_field(options={})
31
+ field = options[:field] ||
32
+ options[:end_field] ||
33
+ @by_star_end_field ||
34
+ by_star_start_field
35
+ field.to_s
36
+ end
37
+
38
+ def by_star_field_type(options={})
39
+ field = options[:field_type] ||
40
+ @by_star_field_type
41
+ field.to_s
42
+ end
43
+
44
+ protected
45
+
46
+ # Wrapper function which extracts time and options for each by_star query.
47
+ # Note the following syntax examples are valid:
48
+ #
49
+ # Post.by_month # defaults to current time
50
+ # Post.by_month(2, year: 2004) # February, 2004
51
+ # Post.by_month(Time.now)
52
+ # Post.by_month(Time.now, field: "published_at")
53
+ # Post.by_month(field: "published_at")
54
+ #
55
+ def with_by_star_options(*args, &block)
56
+ options = args.extract_options!.symbolize_keys!
57
+ time = args.first || Time.zone.now
58
+ block.call(time, options)
59
+ end
60
+
61
+ def by_star_eval_index_scope(start_time, end_time, options)
62
+ value = options[:index_scope] || @by_star_index_scope
63
+ value = value.call(start_time, end_time, options) if value.is_a?(Proc)
64
+ case value
65
+ when nil, false then nil
66
+ when Time, DateTime, Date then value.in_time_zone
67
+ when ActiveSupport::Duration then start_time - value
68
+ when Numeric then start_time - value.seconds
69
+ when :beginning_of_day
70
+ offset = options[:offset] || 0
71
+ (start_time - offset).beginning_of_day + offset
72
+ else raise 'ByStar :index_scope option value is not a supported type.'
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,190 @@
1
+ module ByStar
2
+
3
+ module Between
4
+
5
+ def between_times(*args)
6
+ options = args.extract_options!.symbolize_keys!
7
+
8
+ start_time, end_time = ByStar::Normalization.extract_range(args)
9
+ offset = options[:offset] || 0
10
+ field_type = by_star_field_type(options)
11
+
12
+ if start_time.is_a?(Date)
13
+ start_time = field_type == 'date' ?
14
+ start_time :
15
+ ByStar::Normalization.apply_offset_start(start_time.in_time_zone, offset)
16
+ elsif start_time
17
+ start_time += offset.seconds
18
+ end
19
+
20
+ if end_time.is_a?(Date)
21
+ end_time = field_type == 'date' ?
22
+ end_time :
23
+ ByStar::Normalization.apply_offset_end(end_time.in_time_zone, offset)
24
+ elsif end_time
25
+ end_time += offset.seconds
26
+ end
27
+
28
+ start_field = by_star_start_field(options)
29
+ end_field = by_star_end_field(options)
30
+
31
+ scope = self
32
+ scope = if !start_time && !end_time
33
+ scope # do nothing
34
+ elsif !end_time
35
+ by_star_after_query(scope, start_field, start_time)
36
+ elsif !start_time
37
+ by_star_before_query(scope, start_field, end_time)
38
+ elsif start_field == end_field
39
+ by_star_point_query(scope, start_field, start_time, end_time)
40
+ elsif options[:strict]
41
+ by_star_span_strict_query(scope, start_field, end_field, start_time, end_time)
42
+ else
43
+ by_star_span_loose_query(scope, start_field, end_field, start_time, end_time, options)
44
+ end
45
+
46
+ scope = by_star_order(scope, options[:order]) if options[:order]
47
+ scope
48
+ end
49
+
50
+ def between_dates(*args)
51
+ options = args.extract_options!
52
+ start_date, end_date = ByStar::Normalization.extract_range(args)
53
+ start_date = ByStar::Normalization.date(start_date)
54
+ end_date = ByStar::Normalization.date(end_date)
55
+ between_times(start_date, end_date, options)
56
+ end
57
+
58
+ def at_time(*args)
59
+ with_by_star_options(*args) do |time, options|
60
+ start_field = by_star_start_field(options)
61
+ end_field = by_star_end_field(options)
62
+
63
+ scope = self
64
+ scope = if start_field == end_field
65
+ by_star_point_overlap_query(scope, start_field, time)
66
+ else
67
+ by_star_span_overlap_query(scope, start_field, end_field, time, options)
68
+ end
69
+ scope = by_star_order(scope, options[:order]) if options[:order]
70
+ scope
71
+ end
72
+ end
73
+
74
+ def by_day(*args)
75
+ with_by_star_options(*args) do |time, options|
76
+ date = ByStar::Normalization.date(time)
77
+ between_dates(date, date, options)
78
+ end
79
+ end
80
+
81
+ def by_week(*args)
82
+ with_by_star_options(*args) do |time, options|
83
+ date = ByStar::Normalization.week(time, options)
84
+ start_day = Array(options[:start_day])
85
+ between_dates(date.beginning_of_week(*start_day), date.end_of_week(*start_day), options)
86
+ end
87
+ end
88
+
89
+ def by_cweek(*args)
90
+ with_by_star_options(*args) do |time, options|
91
+ by_week(ByStar::Normalization.cweek(time, options), options)
92
+ end
93
+ end
94
+
95
+ def by_weekend(*args)
96
+ with_by_star_options(*args) do |time, options|
97
+ date = ByStar::Normalization.week(time, options)
98
+ between_dates(date.beginning_of_weekend, date.end_of_weekend, options)
99
+ end
100
+ end
101
+
102
+ def by_fortnight(*args)
103
+ with_by_star_options(*args) do |time, options|
104
+ date = ByStar::Normalization.fortnight(time, options)
105
+ between_dates(date.beginning_of_fortnight, date.end_of_fortnight, options)
106
+ end
107
+ end
108
+
109
+ def by_month(*args)
110
+ with_by_star_options(*args) do |time, options|
111
+ date = ByStar::Normalization.month(time, options)
112
+ between_dates(date.beginning_of_month, date.end_of_month, options)
113
+ end
114
+ end
115
+
116
+ def by_calendar_month(*args)
117
+ with_by_star_options(*args) do |time, options|
118
+ date = ByStar::Normalization.month(time, options)
119
+ start_day = Array(options[:start_day])
120
+ between_dates(date.beginning_of_calendar_month(*start_day), date.end_of_calendar_month(*start_day), options)
121
+ end
122
+ end
123
+
124
+ def by_quarter(*args)
125
+ with_by_star_options(*args) do |time, options|
126
+ date = ByStar::Normalization.quarter(time, options)
127
+ between_dates(date.beginning_of_quarter, date.end_of_quarter, options)
128
+ end
129
+ end
130
+
131
+ def by_year(*args)
132
+ with_by_star_options(*args) do |time, options|
133
+ date = ByStar::Normalization.year(time, options)
134
+ between_dates(date.beginning_of_year, date.to_date.end_of_year, options)
135
+ end
136
+ end
137
+
138
+ def today(options = {})
139
+ by_day(Date.current, options)
140
+ end
141
+
142
+ def yesterday(options = {})
143
+ by_day(Date.yesterday, options)
144
+ end
145
+
146
+ def tomorrow(options = {})
147
+ by_day(Date.tomorrow, options)
148
+ end
149
+
150
+ def past_day(options = {})
151
+ between_times(Time.current - 1.day, Time.current, options)
152
+ end
153
+
154
+ def past_week(options = {})
155
+ between_times(Time.current - 1.week, Time.current, options)
156
+ end
157
+
158
+ def past_fortnight(options = {})
159
+ between_times(Time.current - 2.weeks, Time.current, options)
160
+ end
161
+
162
+ def past_month(options = {})
163
+ between_times(Time.current - 1.month, Time.current, options)
164
+ end
165
+
166
+ def past_year(options = {})
167
+ between_times(Time.current - 1.year, Time.current, options)
168
+ end
169
+
170
+ def next_day(options = {})
171
+ between_times(Time.current, Time.current + 1.day, options)
172
+ end
173
+
174
+ def next_week(options = {})
175
+ between_times(Time.current, Time.current + 1.week, options)
176
+ end
177
+
178
+ def next_fortnight(options = {})
179
+ between_times(Time.current, Time.current + 2.weeks, options)
180
+ end
181
+
182
+ def next_month(options = {})
183
+ between_times(Time.current, Time.current + 1.month, options)
184
+ end
185
+
186
+ def next_year(options = {})
187
+ between_times(Time.current, Time.current + 1.year, options)
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,35 @@
1
+ module ByStar
2
+
3
+ module Directional
4
+
5
+ def before(*args)
6
+ with_by_star_options(*args) do |time, options|
7
+ field = by_star_start_field(options)
8
+ time = ByStar::Normalization.time(time)
9
+ by_star_before_query(self, field, time)
10
+ end
11
+ end
12
+ alias_method :before_now, :before
13
+
14
+ def after(*args)
15
+ with_by_star_options(*args) do |time, options|
16
+ field = by_star_start_field(options)
17
+ time = ByStar::Normalization.time(time)
18
+ by_star_after_query(self, field, time)
19
+ end
20
+ end
21
+ alias_method :after_now, :after
22
+
23
+ def oldest(*args)
24
+ with_by_star_options(*args) do |time, options|
25
+ oldest_query(options)
26
+ end
27
+ end
28
+
29
+ def newest(*args)
30
+ with_by_star_options(*args) do |time, options|
31
+ newest_query(options)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,41 @@
1
+ module ByStar
2
+
3
+ module Kernel
4
+
5
+ module Date
6
+
7
+ # A "Weekend" is defined as beginning of Saturday to end of Sunday.
8
+ # The weekend for a given date will be the the next weekend if the day Mon-Thurs,
9
+ # otherwise the current weekend if the day is Fri-Sun.
10
+ def beginning_of_weekend
11
+ beginning_of_week(:monday).advance(days: 5)
12
+ end
13
+
14
+ def end_of_weekend
15
+ beginning_of_weekend + 1
16
+ end
17
+
18
+ # A "Fortnight" is defined as a two week period, with the first fortnight of the
19
+ # year beginning on 1st January.
20
+ def beginning_of_fortnight
21
+ beginning_of_year + 14 * ((self - beginning_of_year) / 14).to_i
22
+ end
23
+
24
+ def end_of_fortnight
25
+ beginning_of_fortnight + 13
26
+ end
27
+
28
+ # A "Calendar Month" is defined as a month as it appears on a calendar, including days form
29
+ # previous/following months which are part of the first/last weeks of the given month.
30
+ def beginning_of_calendar_month(*args)
31
+ beginning_of_month.beginning_of_week(*args)
32
+ end
33
+
34
+ def end_of_calendar_month(*args)
35
+ end_of_month.end_of_week(*args)
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ ::Date.__send__(:include, ByStar::Kernel::Date)
@@ -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)
@@ -0,0 +1,41 @@
1
+ module ByStar
2
+
3
+ module Kernel
4
+
5
+ module Time
6
+
7
+ # A "Weekend" is defined as beginning of Saturday to end of Sunday.
8
+ # The weekend for a given date will be the the next weekend if the day Mon-Thurs,
9
+ # otherwise the current weekend if the day is Fri-Sun.
10
+ def beginning_of_weekend
11
+ beginning_of_week(:monday).advance(days: 5)
12
+ end
13
+
14
+ def end_of_weekend
15
+ (beginning_of_weekend + 47.hours).end_of_hour
16
+ end
17
+
18
+ # A "Fortnight" is defined as a two week period, with the first fortnight of the
19
+ # year beginning on 1st January.
20
+ def beginning_of_fortnight
21
+ (beginning_of_year + 1.fortnight * ((self - beginning_of_year) / 1.fortnight).to_i).beginning_of_day
22
+ end
23
+
24
+ def end_of_fortnight
25
+ (beginning_of_fortnight + 13.days).end_of_day
26
+ end
27
+
28
+ # A "Calendar Month" is defined as a month as it appears on a calendar, including days form
29
+ # previous/following months which are part of the first/last weeks of the given month.
30
+ def beginning_of_calendar_month(*args)
31
+ beginning_of_month.beginning_of_week(*args)
32
+ end
33
+
34
+ def end_of_calendar_month(*args)
35
+ end_of_month.end_of_week(*args)
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ ::Time.__send__(:include, ByStar::Kernel::Time)
@@ -0,0 +1,156 @@
1
+ module ByStar
2
+
3
+ class ParseError < StandardError; end
4
+
5
+ module Normalization
6
+
7
+ class << self
8
+
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)
13
+ end
14
+
15
+ def time(value)
16
+ value = parse_time(value) if value.is_a?(String)
17
+ value.try(:in_time_zone)
18
+ end
19
+
20
+ def week(value, options={})
21
+ value = try_string_to_int(value)
22
+ case value
23
+ when Integer then week_integer(value, options)
24
+ else date(value)
25
+ end
26
+ end
27
+
28
+ def week_integer(value, options={})
29
+ raise ParseError, 'Week number must be between 0 and 52' unless value.in?(0..52)
30
+ time = Time.zone.local(options[:year] || Time.zone.now.year)
31
+ time.beginning_of_year + value.to_i.weeks
32
+ end
33
+
34
+ def cweek(value, options={})
35
+ _value = value
36
+ if _value.is_a?(Integer)
37
+ raise ParseError, 'cweek number must be between 1 and 53' unless value.in?(1..53)
38
+ _value -= 1
39
+ end
40
+ week(_value, options)
41
+ end
42
+
43
+ def fortnight(value, options={})
44
+ value = try_string_to_int(value)
45
+ case value
46
+ when Integer then fortnight_integer(value, options)
47
+ else date(value)
48
+ end
49
+ end
50
+
51
+ def fortnight_integer(value, options={})
52
+ raise ParseError, 'Fortnight number must be between 0 and 26' unless value.in?(0..26)
53
+ time = Time.zone.local(options[:year] || Time.zone.now.year)
54
+ time + (value * 2).weeks
55
+ end
56
+
57
+ def quarter(value, options={})
58
+ value = try_string_to_int(value)
59
+ case value
60
+ when Integer then quarter_integer(value, options)
61
+ else date(value)
62
+ end
63
+ end
64
+
65
+ def quarter_integer(value, options={})
66
+ raise ParseError, 'Quarter number must be between 1 and 4' unless value.in?(1..4)
67
+ time = Time.zone.local(options[:year] || Time.zone.now.year)
68
+ time.beginning_of_year + ((value - 1) * 3).months
69
+ end
70
+
71
+ def month(value, options={})
72
+ value = try_string_to_int(value)
73
+ case value
74
+ when Integer, String then month_integer(value, options)
75
+ else date(value)
76
+ end
77
+ end
78
+
79
+ def month_integer(value, options={})
80
+ year = options[:year] || Time.zone.now.year
81
+ Time.zone.parse "#{year}-#{value}-01"
82
+ rescue
83
+ raise ParseError, 'Month must be a number between 1 and 12 or a month name'
84
+ end
85
+
86
+ def year(value, options={})
87
+ value = try_string_to_int(value)
88
+ case value
89
+ when Integer then year_integer(value)
90
+ else date(value)
91
+ end
92
+ end
93
+
94
+ def year_integer(value)
95
+ Time.zone.local(extrapolate_year(value))
96
+ end
97
+
98
+ def extrapolate_year(value)
99
+ case value.to_i
100
+ when 0..69
101
+ 2000 + value
102
+ when 70..99
103
+ 1900 + value
104
+ else
105
+ value.to_i
106
+ end
107
+ end
108
+
109
+ def try_string_to_int(value)
110
+ value.is_a?(String) ? Integer(value) : value
111
+ rescue
112
+ value
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
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,75 @@
1
+ module ByStar
2
+ module ActiveRecord
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ include ::ByStar::Base
7
+
8
+ protected
9
+
10
+ def by_star_default_field
11
+ "#{self.table_name}.created_at"
12
+ end
13
+
14
+ def by_star_point_query(scope, field, start_time, end_time)
15
+ scope.where("#{field} >= ? AND #{field} <= ?", start_time, end_time)
16
+ end
17
+
18
+ def by_star_span_strict_query(scope, start_field, end_field, start_time, end_time)
19
+ scope.where("#{start_field} >= ? AND #{start_field} <= ? AND #{end_field} >= ? AND #{end_field} <= ?", start_time, end_time, start_time, end_time)
20
+ end
21
+
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
38
+ end
39
+
40
+ def by_star_before_query(scope, field, time)
41
+ scope.where("#{field} <= ?", time)
42
+ end
43
+
44
+ def by_star_after_query(scope, field, time)
45
+ scope.where("#{field} >= ?", time)
46
+ end
47
+
48
+ def by_star_order(scope, order)
49
+ scope.order(order)
50
+ end
51
+
52
+ def oldest_query(options={})
53
+ field = by_star_start_field(options)
54
+ reorder("#{field} ASC").first
55
+ end
56
+
57
+ def newest_query(options={})
58
+ field = by_star_start_field(options)
59
+ reorder("#{field} DESC").first
60
+ end
61
+ end
62
+
63
+ def previous(options={})
64
+ field = self.class.by_star_start_field(options)
65
+ value = self.send(field.split(".").last)
66
+ self.class.where("#{field} < ?", value).reorder("#{field} DESC").first
67
+ end
68
+
69
+ def next(options={})
70
+ field = self.class.by_star_start_field(options)
71
+ value = self.send(field.split(".").last)
72
+ self.class.where("#{field} > ?", value).reorder("#{field} ASC").first
73
+ end
74
+ end
75
+ end