by_star 3.0.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) 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 +6 -5
  5. data/.travis.yml +92 -61
  6. data/CHANGELOG.md +63 -44
  7. data/Gemfile +18 -18
  8. data/MIT-LICENSE +20 -20
  9. data/README.md +616 -577
  10. data/Rakefile +18 -18
  11. data/UPGRADING +4 -6
  12. data/by_star.gemspec +34 -34
  13. data/cleaner.rb +24 -24
  14. data/lib/by_star/base.rb +69 -68
  15. data/lib/by_star/between.rb +185 -156
  16. data/lib/by_star/directional.rb +35 -37
  17. data/lib/by_star/kernel/date.rb +41 -50
  18. data/lib/by_star/kernel/in_time_zone.rb +20 -0
  19. data/lib/by_star/kernel/time.rb +41 -41
  20. data/lib/by_star/normalization.rb +156 -127
  21. data/lib/by_star/orm/active_record/by_star.rb +75 -61
  22. data/lib/by_star/orm/mongoid/by_star.rb +90 -76
  23. data/lib/by_star/orm/mongoid/reorder.rb +23 -23
  24. data/lib/by_star/version.rb +3 -3
  25. data/lib/by_star.rb +18 -17
  26. data/spec/database.yml +15 -15
  27. data/spec/fixtures/active_record/models.rb +12 -12
  28. data/spec/fixtures/active_record/schema.rb +19 -19
  29. data/spec/fixtures/mongoid/models.rb +31 -31
  30. data/spec/fixtures/shared/seeds.rb +36 -26
  31. data/spec/gemfiles/Gemfile.rails +5 -0
  32. data/spec/gemfiles/Gemfile.rails32 +7 -0
  33. data/spec/gemfiles/Gemfile.rails40 +7 -7
  34. data/spec/gemfiles/Gemfile.rails41 +7 -7
  35. data/spec/gemfiles/Gemfile.rails42 +7 -7
  36. data/spec/gemfiles/Gemfile.rails50 +7 -10
  37. data/spec/gemfiles/Gemfile.rails51 +7 -10
  38. data/spec/gemfiles/Gemfile.rails52 +7 -0
  39. data/spec/gemfiles/Gemfile.rails60 +7 -0
  40. data/spec/gemfiles/Gemfile.rails61 +7 -0
  41. data/spec/integration/active_record/active_record_spec.rb +41 -38
  42. data/spec/integration/mongoid/mongoid_spec.rb +39 -37
  43. data/spec/integration/shared/at_time.rb +53 -0
  44. data/spec/integration/shared/between_dates.rb +99 -0
  45. data/spec/integration/shared/between_times.rb +99 -82
  46. data/spec/integration/shared/by_calendar_month.rb +55 -55
  47. data/spec/integration/shared/by_cweek.rb +54 -54
  48. data/spec/integration/shared/by_day.rb +120 -96
  49. data/spec/integration/shared/by_direction.rb +126 -172
  50. data/spec/integration/shared/by_fortnight.rb +48 -48
  51. data/spec/integration/shared/by_month.rb +50 -50
  52. data/spec/integration/shared/by_quarter.rb +49 -49
  53. data/spec/integration/shared/by_week.rb +54 -54
  54. data/spec/integration/shared/by_weekend.rb +49 -49
  55. data/spec/integration/shared/by_year.rb +48 -48
  56. data/spec/integration/shared/index_scope_parameter.rb +111 -0
  57. data/spec/integration/shared/offset_parameter.rb +32 -32
  58. data/spec/integration/shared/order_parameter.rb +36 -36
  59. data/spec/integration/shared/relative.rb +174 -174
  60. data/spec/spec_helper.rb +33 -29
  61. data/spec/unit/kernel_date_spec.rb +113 -113
  62. data/spec/unit/kernel_time_spec.rb +57 -57
  63. data/spec/unit/normalization_spec.rb +384 -305
  64. data/tmp/.gitignore +1 -1
  65. metadata +33 -21
  66. data/spec/gemfiles/Gemfile.master +0 -6
  67. data/spec/integration/shared/scope_parameter.rb +0 -73
@@ -1,37 +1,35 @@
1
- module ByStar
2
-
3
- module Directional
4
-
5
- def before(*args)
6
- with_by_star_options(*args) do |time, options|
7
- scope = by_star_scope(options)
8
- field = by_star_start_field(options)
9
- time = ByStar::Normalization.time(time)
10
- by_star_before_query(scope, field, time)
11
- end
12
- end
13
- alias_method :before_now, :before
14
-
15
- def after(*args)
16
- with_by_star_options(*args) do |time, options|
17
- scope = by_star_scope(options)
18
- field = by_star_start_field(options)
19
- time = ByStar::Normalization.time(time)
20
- by_star_after_query(scope, field, time)
21
- end
22
- end
23
- alias_method :after_now, :after
24
-
25
- def oldest(*args)
26
- with_by_star_options(*args) do |time, options|
27
- oldest_query(options)
28
- end
29
- end
30
-
31
- def newest(*args)
32
- with_by_star_options(*args) do |time, options|
33
- newest_query(options)
34
- end
35
- end
36
- end
37
- end
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
@@ -1,50 +1,41 @@
1
- module ByStar
2
-
3
- module Kernel
4
-
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
-
16
- # A "Weekend" is defined as beginning of Saturday to end of Sunday.
17
- # The weekend for a given date will be the the next weekend if the day Mon-Thurs,
18
- # otherwise the current weekend if the day is Fri-Sun.
19
- def beginning_of_weekend
20
- beginning_of_week(:monday).advance(days: 5)
21
- end
22
-
23
- def end_of_weekend
24
- beginning_of_weekend + 1
25
- end
26
-
27
- # A "Fortnight" is defined as a two week period, with the first fortnight of the
28
- # year beginning on 1st January.
29
- def beginning_of_fortnight
30
- beginning_of_year + 14 * ((self - beginning_of_year) / 14).to_i
31
- end
32
-
33
- def end_of_fortnight
34
- beginning_of_fortnight + 13
35
- end
36
-
37
- # A "Calendar Month" is defined as a month as it appears on a calendar, including days form
38
- # previous/following months which are part of the first/last weeks of the given month.
39
- def beginning_of_calendar_month(*args)
40
- beginning_of_month.beginning_of_week(*args)
41
- end
42
-
43
- def end_of_calendar_month(*args)
44
- end_of_month.end_of_week(*args)
45
- end
46
- end
47
- end
48
- end
49
-
50
- ::Date.__send__(:include, ByStar::Kernel::Date)
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)
@@ -1,41 +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)
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)
@@ -1,127 +1,156 @@
1
- module ByStar
2
-
3
- class ParseError < StandardError; end
4
-
5
- module Normalization
6
-
7
- class << self
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)
20
- end
21
-
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}")
29
- end
30
-
31
- def week(value, options={})
32
- value = try_string_to_int(value)
33
- case value
34
- when Integer then week_integer(value, options)
35
- else time(value)
36
- end
37
- end
38
-
39
- def week_integer(value, options={})
40
- raise ParseError, 'Week number must be between 0 and 52' unless value.in?(0..52)
41
- time = Time.zone.local(options[:year] || Time.zone.now.year)
42
- time.beginning_of_year + value.to_i.weeks
43
- end
44
-
45
- def cweek(value, options={})
46
- _value = value
47
- if _value.is_a?(Integer)
48
- raise ParseError, 'cweek number must be between 1 and 53' unless value.in?(1..53)
49
- _value -= 1
50
- end
51
- week(_value, options)
52
- end
53
-
54
- def fortnight(value, options={})
55
- value = try_string_to_int(value)
56
- case value
57
- when Integer then fortnight_integer(value, options)
58
- else time(value)
59
- end
60
- end
61
-
62
- def fortnight_integer(value, options={})
63
- raise ParseError, 'Fortnight number must be between 0 and 26' unless value.in?(0..26)
64
- time = Time.zone.local(options[:year] || Time.zone.now.year)
65
- time + (value * 2).weeks
66
- end
67
-
68
- def quarter(value, options={})
69
- value = try_string_to_int(value)
70
- case value
71
- when Integer then quarter_integer(value, options)
72
- else time(value)
73
- end
74
- end
75
-
76
- def quarter_integer(value, options={})
77
- raise ParseError, 'Quarter number must be between 1 and 4' unless value.in?(1..4)
78
- time = Time.zone.local(options[:year] || Time.zone.now.year)
79
- time.beginning_of_year + ((value - 1) * 3).months
80
- end
81
-
82
- def month(value, options={})
83
- value = try_string_to_int(value)
84
- case value
85
- when Integer, String then month_integer(value, options)
86
- else time(value)
87
- end
88
- end
89
-
90
- def month_integer(value, options={})
91
- year = options[:year] || Time.zone.now.year
92
- Time.zone.parse "#{year}-#{value}-01"
93
- rescue
94
- raise ParseError, 'Month must be a number between 1 and 12 or a month name'
95
- end
96
-
97
- def year(value, options={})
98
- value = try_string_to_int(value)
99
- case value
100
- when Integer then year_integer(value)
101
- else time(value)
102
- end
103
- end
104
-
105
- def year_integer(value)
106
- Time.zone.local(extrapolate_year(value))
107
- end
108
-
109
- def extrapolate_year(value)
110
- case value.to_i
111
- when 0..69
112
- 2000 + value
113
- when 70..99
114
- 1900 + value
115
- else
116
- value.to_i
117
- end
118
- end
119
-
120
- def try_string_to_int(value)
121
- value.is_a?(String) ? Integer(value) : value
122
- rescue
123
- value
124
- end
125
- end
126
- end
127
- end
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