ta_by_star 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 (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,90 @@
1
+ # In keeping with Mongoid standards, this module must be included into your model class, i.e.
2
+ #
3
+ # include Mongoid::ByStar
4
+ #
5
+ module Mongoid
6
+ module ByStar
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+ include ::ByStar::Base
11
+
12
+ alias_method :original_by_star_end_field, :by_star_end_field
13
+ alias_method :original_by_star_start_field, :by_star_start_field
14
+
15
+ def by_star_end_field(options = {})
16
+ database_field_name original_by_star_end_field(options)
17
+ end
18
+
19
+ def by_star_start_field(options = {})
20
+ database_field_name original_by_star_start_field(options)
21
+ end
22
+
23
+ def by_star_default_field
24
+ :created_at
25
+ end
26
+
27
+ protected
28
+
29
+ def by_star_point_query(scope, field, start_time, end_time)
30
+ range = start_time..end_time
31
+ scope.where(field => range)
32
+ end
33
+
34
+ def by_star_span_strict_query(scope, start_field, end_field, start_time, end_time)
35
+ range = start_time..end_time
36
+ scope.where(start_field => range).where(end_field => range)
37
+ end
38
+
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
55
+ end
56
+
57
+ def by_star_before_query(scope, field, time)
58
+ scope.lte(field => time)
59
+ end
60
+
61
+ def by_star_after_query(scope, field, time)
62
+ scope.gte(field => time)
63
+ end
64
+
65
+ def by_star_order(scope, order)
66
+ scope.order_by(order)
67
+ end
68
+
69
+ def oldest_query(options={})
70
+ field = by_star_start_field(options)
71
+ all.reorder(field => :asc).first
72
+ end
73
+
74
+ def newest_query(options={})
75
+ field = by_star_start_field(options)
76
+ all.reorder(field => :desc).first
77
+ end
78
+ end
79
+
80
+ def previous(options={})
81
+ field = self.class.by_star_start_field(options)
82
+ self.class.lt(field => self.send(field)).reorder(field => :desc).first
83
+ end
84
+
85
+ def next(options={})
86
+ field = self.class.by_star_start_field(options)
87
+ self.class.gt(field => self.send(field)).reorder(field => :asc).first
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,23 @@
1
+ # Backport of `reorder` method from Origin 2.1.0+
2
+ if defined?(Origin::Optional) && !Origin::Optional.method_defined?(:reorder)
3
+ module Origin
4
+ module Optional
5
+
6
+ # Instead of merging the order criteria, use this method to completely
7
+ # replace the existing ordering with the provided.
8
+ #
9
+ # @example Replace the ordering.
10
+ # optional.reorder(name: :asc)
11
+ #
12
+ # @param [ Array, Hash, String ] spec The sorting specification.
13
+ #
14
+ # @return [ Optional ] The cloned optional.
15
+ #
16
+ # @since 2.1.0
17
+ def reorder(*spec)
18
+ options.delete(:sort)
19
+ order_by(*spec)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module ByStar
2
+ VERSION = '4.0.0'
3
+ end
data/lib/by_star.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'by_star/kernel/in_time_zone'
2
+ require 'by_star/kernel/time'
3
+ require 'by_star/kernel/date'
4
+ require 'by_star/normalization'
5
+ require 'by_star/between'
6
+ require 'by_star/directional'
7
+ require 'by_star/base'
8
+
9
+ if defined?(ActiveRecord)
10
+ require 'by_star/orm/active_record/by_star'
11
+ ActiveRecord::Base.send :include, ByStar::ActiveRecord
12
+ ActiveRecord::Relation.send :extend, ByStar::ActiveRecord::ClassMethods
13
+ end
14
+
15
+ if defined?(Mongoid)
16
+ require 'by_star/orm/mongoid/reorder'
17
+ require 'by_star/orm/mongoid/by_star'
18
+ end
data/spec/database.yml ADDED
@@ -0,0 +1,15 @@
1
+ sqlite:
2
+ adapter: sqlite3
3
+ database: by_star.sqlite3
4
+
5
+ postgres:
6
+ adapter: postgresql
7
+ database: by_star_test
8
+ username: <%= ENV.fetch("USER") || "postgres" %>
9
+ min_messages: warning
10
+
11
+ mysql:
12
+ adapter: mysql2
13
+ database: by_star_test
14
+ username: root
15
+ encoding: utf8
@@ -0,0 +1,12 @@
1
+ class Post < ActiveRecord::Base
2
+ end
3
+
4
+ class Appointment < ActiveRecord::Base
5
+ by_star_field index_scope: ->(start){ start - 5.days }
6
+ end
7
+
8
+ class Event < ActiveRecord::Base
9
+ by_star_field :start_time, :end_time, offset: 3.hours
10
+
11
+ default_scope ->{ order('day_of_month ASC') }
12
+ end
@@ -0,0 +1,19 @@
1
+ ActiveRecord::Schema.define do
2
+ self.verbose = false
3
+
4
+ create_table :posts, force: true do |t|
5
+ t.timestamps
6
+ t.integer :day_of_month
7
+ end
8
+
9
+ create_table :events, force: true do |t|
10
+ t.timestamps
11
+ t.datetime :start_time, :end_time
12
+ t.integer :day_of_month
13
+ end
14
+
15
+ create_table :appointments, force: true do |t|
16
+ t.timestamps
17
+ t.integer :day_of_month
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ class Post
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+ include Mongoid::ByStar
5
+
6
+ field :day_of_month, type: Integer
7
+ end
8
+
9
+ class Appointment
10
+ include Mongoid::Document
11
+ include Mongoid::Timestamps
12
+ include Mongoid::ByStar
13
+
14
+ field :day_of_month, type: Integer
15
+
16
+ by_star_field index_scope: ->(start){ start - 5.days }
17
+ end
18
+
19
+ class Event
20
+ include Mongoid::Document
21
+ include Mongoid::Timestamps
22
+ include Mongoid::ByStar
23
+
24
+ field :st, as: :start_time, type: Time
25
+ field :end_time, type: Time
26
+ field :day_of_month, type: Integer
27
+
28
+ by_star_field :start_time, :end_time, offset: 3.hours
29
+
30
+ default_scope ->{ order_by(day_of_month: :asc) }
31
+ end
@@ -0,0 +1,36 @@
1
+ %w(2013-11-01
2
+ 2013-11-30
3
+ 2013-12-01
4
+ 2013-12-05
5
+ 2013-12-08
6
+ 2013-12-16
7
+ 2013-12-22
8
+ 2013-12-25
9
+ 2013-12-28
10
+ 2013-12-31
11
+ 2014-01-01
12
+ 2014-01-01
13
+ 2014-01-05
14
+ 2014-01-10
15
+ 2014-01-12
16
+ 2014-01-20
17
+ 2014-02-01
18
+ 2014-02-15
19
+ 2014-03-01
20
+ 2014-03-15
21
+ 2014-04-01
22
+ 2014-04-15).map{|d| Time.zone.parse(d) + 17.hours }.each_with_index do |d, index|
23
+ Post.create!(created_at: d, updated_at: d + index.days, day_of_month: d.day)
24
+ Appointment.create!(created_at: d, day_of_month: d.day)
25
+ Event.create!(created_at: d, start_time: d - 5.days, end_time: d + 5.days, day_of_month: d.day)
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'
@@ -0,0 +1,7 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec path: '../../'
4
+
5
+ gem 'activerecord', '~> 4.0.0'
6
+ gem 'pg', '~> 0.11'
7
+ gem 'mongoid'
@@ -0,0 +1,7 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec path: '../../'
4
+
5
+ gem 'activerecord', '~> 4.1.0'
6
+ gem 'pg', '~> 0.11'
7
+ gem 'mongoid'
@@ -0,0 +1,7 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec path: '../../'
4
+
5
+ gem 'activerecord', '~> 4.2.0'
6
+ gem 'pg', '~> 0.15'
7
+ gem 'mongoid'
@@ -0,0 +1,7 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec path: '../../'
4
+
5
+ gem 'activerecord', '~> 5.0.0'
6
+ gem 'pg', '~> 0.18'
7
+ gem 'mongoid'
@@ -0,0 +1,7 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec path: '../../'
4
+
5
+ gem 'activerecord', '~> 5.1.0'
6
+ gem 'pg', '~> 0.18'
7
+ gem 'mongoid'
@@ -0,0 +1,7 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec path: '../../'
4
+
5
+ gem 'activerecord', '~> 5.2.0'
6
+ gem 'pg', '~> 0.18'
7
+ gem 'mongoid'
@@ -0,0 +1,7 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec path: '../../'
4
+
5
+ gem 'activerecord', '~> 6.0'
6
+ gem 'pg'
7
+ gem 'mongoid'
@@ -0,0 +1,7 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec path: '../../'
4
+
5
+ gem 'activerecord', '~> 6.1'
6
+ gem 'pg'
7
+ gem 'mongoid'
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+ Dir[File.dirname(__FILE__) + '/../shared/*.rb'].each {|file| require file }
3
+
4
+ describe ActiveRecord do
5
+ before(:all) do
6
+ ActiveRecord::Base.default_timezone = :utc
7
+ # ActiveRecord::Base.logger = Logger.new(STDOUT)
8
+ database_file = File.dirname(__FILE__) + '/../../database.yml'
9
+ parsed_config = ERB.new(File.read(database_file)).result
10
+ db_config = YAML.safe_load(parsed_config)
11
+ if db_config.has_key?('sqlite') && db_config['sqlite'].has_key?('database')
12
+ db_config['sqlite']['database'] = File.dirname(__FILE__) + '/../../tmp/' + db_config['sqlite']['database']
13
+ end
14
+
15
+ ActiveRecord::Base.configurations = db_config
16
+ ActiveRecord::Base.establish_connection(ENV['DB'].try(:to_sym) || :sqlite)
17
+ load File.dirname(__FILE__) + '/../../fixtures/active_record/schema.rb'
18
+ load File.dirname(__FILE__) + '/../../fixtures/active_record/models.rb'
19
+ load File.dirname(__FILE__) + '/../../fixtures/shared/seeds.rb'
20
+
21
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + '/../../tmp/activerecord.log')
22
+ end
23
+
24
+ it_behaves_like 'between_times'
25
+ it_behaves_like 'between_dates'
26
+ it_behaves_like 'at_time'
27
+ it_behaves_like 'offset parameter'
28
+ it_behaves_like 'order parameter'
29
+ it_behaves_like 'index_scope parameter'
30
+ it_behaves_like 'by day'
31
+ it_behaves_like 'by direction'
32
+ it_behaves_like 'by fortnight'
33
+ it_behaves_like 'by month'
34
+ it_behaves_like 'by calendar month'
35
+ it_behaves_like 'by quarter'
36
+ it_behaves_like 'by week'
37
+ it_behaves_like 'by cweek'
38
+ it_behaves_like 'by weekend'
39
+ it_behaves_like 'by year'
40
+ it_behaves_like 'relative'
41
+ end if testing_active_record?
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+ Dir[File.dirname(__FILE__) + '/../shared/*.rb'].each {|file| require file }
3
+
4
+ describe 'Mongoid' do
5
+
6
+ before(:all) do
7
+ DATABASE_NAME = "mongoid_#{Process.pid}"
8
+ # Moped.logger = Logger.new(STDOUT)
9
+
10
+ Mongoid.configure do |config|
11
+ config.connect_to DATABASE_NAME
12
+ end
13
+
14
+ load File.dirname(__FILE__) + '/../../fixtures/mongoid/models.rb'
15
+ load File.dirname(__FILE__) + '/../../fixtures/shared/seeds.rb'
16
+ end
17
+
18
+ after(:all) do
19
+ Mongoid.purge!
20
+ end
21
+
22
+ it_behaves_like 'between_times'
23
+ it_behaves_like 'between_dates'
24
+ it_behaves_like 'at_time'
25
+ it_behaves_like 'offset parameter'
26
+ it_behaves_like 'order parameter'
27
+ it_behaves_like 'index_scope parameter'
28
+ it_behaves_like 'by day'
29
+ it_behaves_like 'by direction'
30
+ it_behaves_like 'by fortnight'
31
+ it_behaves_like 'by month'
32
+ it_behaves_like 'by calendar month'
33
+ it_behaves_like 'by quarter'
34
+ it_behaves_like 'by week'
35
+ it_behaves_like 'by cweek'
36
+ it_behaves_like 'by weekend'
37
+ it_behaves_like 'by year'
38
+ it_behaves_like 'relative'
39
+ end if testing_mongoid?
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'at_time' do
4
+
5
+ describe '#at_time' do
6
+
7
+ context 'point object' do
8
+
9
+ context 'exactly equal' do
10
+ subject { Post.at_time(Time.zone.parse('2013-12-28 17:00:00')) }
11
+ it { expect(subject.count).to eql(1) }
12
+ end
13
+
14
+ context 'not exactly equal' do
15
+ subject { Post.at_time(Time.zone.parse('2013-12-28 17:00:01')) }
16
+ it { expect(subject.count).to eql(0) }
17
+ end
18
+ end
19
+
20
+ context 'timespan object' do
21
+
22
+ context 'before start time' do
23
+ subject { Event.at_time(Time.zone.parse('2013-12-23 16:59:59')) }
24
+ it { expect(subject.count).to eql(2) }
25
+ end
26
+
27
+ context 'at start time' do
28
+ subject { Event.at_time(Time.zone.parse('2013-12-23 17:00:00')) }
29
+ it { expect(subject.count).to eql(3) }
30
+ end
31
+
32
+ context 'after start time' do
33
+ subject { Event.at_time(Time.zone.parse('2013-12-23 17:00:01')) }
34
+ it { expect(subject.count).to eql(3) }
35
+ end
36
+
37
+ context 'before end time' do
38
+ subject { Event.at_time(Time.zone.parse('2013-11-06 16:59:59')) }
39
+ it { expect(subject.count).to eql(1) }
40
+ end
41
+
42
+ context 'at end time' do
43
+ subject { Event.at_time(Time.zone.parse('2013-11-06 17:00:00')) }
44
+ it { expect(subject.count).to eql(0) }
45
+ end
46
+
47
+ context 'after end time' do
48
+ subject { Event.at_time(Time.zone.parse('2013-11-06 17:00:01')) }
49
+ it { expect(subject.count).to eql(0) }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'between_dates' do
4
+
5
+ describe '#between_dates' do
6
+ subject { Post.between_dates(Time.zone.parse('2014-01-01'), Time.zone.parse('2014-01-06')) }
7
+
8
+ if testing_active_record?
9
+ it { is_expected.to be_a(ActiveRecord::Relation) }
10
+ else testing_mongoid?
11
+ it { is_expected.to be_a(Mongoid::Criteria) }
12
+ end
13
+
14
+ it { expect(subject.count).to eql(3) }
15
+
16
+ context 'one-sided query' do
17
+
18
+ context 'point query' do
19
+
20
+ context 'only start time' do
21
+ subject { Post.between_dates(Time.zone.parse('2014-01-01'), nil) }
22
+ it { expect(subject.count).to eql(12) }
23
+ end
24
+
25
+ context 'only end time' do
26
+ subject { Post.between_dates(nil, Time.zone.parse('2014-01-01')) }
27
+ it { expect(subject.count).to eql(12) }
28
+
29
+ context 'neither start nor end time' do
30
+ subject { Post.between_dates(nil, nil) }
31
+ it { expect(subject.count).to eql(22) }
32
+ end
33
+ end
34
+ end
35
+
36
+ context 'timespan loose query' do
37
+
38
+ context 'only start time' do
39
+ subject { Event.between_dates(Time.zone.parse('2014-01-01'), nil, strict: false) }
40
+ it { expect(subject.count).to eql(17) }
41
+ end
42
+
43
+ context 'only end time' do
44
+ subject { Event.between_dates(nil, Time.zone.parse('2014-01-01'), strict: false) }
45
+ it { expect(subject.count).to eql(13) }
46
+
47
+ context 'neither start nor end time' do
48
+ subject { Event.between_dates(nil, nil) }
49
+ it { expect(subject.count).to eql(30) }
50
+ end
51
+ end
52
+ end
53
+
54
+ context 'timespan strict query' do
55
+
56
+ context 'only start time' do
57
+ subject { Event.between_dates(Time.zone.parse('2014-01-01'), nil) }
58
+ it { expect(subject.count).to eql(17) }
59
+ end
60
+
61
+ context 'only end time' do
62
+ subject { Event.between_dates(nil, Time.zone.parse('2014-01-01')) }
63
+ it { expect(subject.count).to eql(13) }
64
+
65
+ context 'neither start nor end time' do
66
+ subject { Event.between_dates(nil, nil) }
67
+ it { expect(subject.count).to eql(30) }
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ context 'two-sided query' do
74
+ context 'DST starts (Sydney)', sydney: true do
75
+ context 'day before' do
76
+ subject { Event.between_dates(Date.parse('2020-04-04'), Date.parse('2020-04-04'), offset: 5.hours) }
77
+ it { expect(subject.count).to eql(3) }
78
+ end
79
+
80
+ context 'same day' do
81
+ subject { Event.between_dates(Date.parse('2020-04-05'), Date.parse('2020-04-05'), offset: 5.hours) }
82
+ it { expect(subject.count).to eql(1) }
83
+ end
84
+ end
85
+
86
+ context 'when DST ends (Sydney)', sydney: true do
87
+ context 'day before' do
88
+ subject { Event.between_dates(Date.parse('2020-10-03'), Date.parse('2020-10-03'), offset: 5.hours) }
89
+ it { expect(subject.count).to eql(1) }
90
+ end
91
+
92
+ context 'same day' do
93
+ subject { Event.between_dates(Date.parse('2020-10-04'), Date.parse('2020-10-04'), offset: 5.hours) }
94
+ it { expect(subject.count).to eql(3) }
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'between_times' do
4
+
5
+ describe '#between_times' do
6
+ subject { Post.between_times(Time.zone.parse('2014-01-01'), Time.zone.parse('2014-01-06')) }
7
+
8
+ if testing_active_record?
9
+ it { is_expected.to be_a(ActiveRecord::Relation) }
10
+ else testing_mongoid?
11
+ it { is_expected.to be_a(Mongoid::Criteria) }
12
+ end
13
+
14
+ it { expect(subject.count).to eql(3) }
15
+
16
+ context 'one-sided query' do
17
+
18
+ context 'point query' do
19
+
20
+ context 'only start time' do
21
+ subject { Post.between_times(Time.zone.parse('2014-01-01'), nil) }
22
+ it { expect(subject.count).to eql(12) }
23
+ end
24
+
25
+ context 'only end time' do
26
+ subject { Post.between_times(nil, Time.zone.parse('2014-01-01')) }
27
+ it { expect(subject.count).to eql(10) }
28
+
29
+ context 'neither start nor end time' do
30
+ subject { Post.between_times(nil, nil) }
31
+ it { expect(subject.count).to eql(22) }
32
+ end
33
+ end
34
+ end
35
+
36
+ context 'timespan loose query' do
37
+
38
+ context 'only start time' do
39
+ subject { Event.between_times(Time.zone.parse('2014-01-01'), nil, strict: false) }
40
+ it { expect(subject.count).to eql(17) }
41
+ end
42
+
43
+ context 'only end time' do
44
+ subject { Event.between_times(nil, Time.zone.parse('2014-01-01'), strict: false) }
45
+ it { expect(subject.count).to eql(13) }
46
+
47
+ context 'neither start nor end time' do
48
+ subject { Event.between_times(nil, nil) }
49
+ it { expect(subject.count).to eql(30) }
50
+ end
51
+ end
52
+ end
53
+
54
+ context 'timespan strict query' do
55
+
56
+ context 'only start time' do
57
+ subject { Event.between_times(Time.zone.parse('2014-01-01'), nil) }
58
+ it { expect(subject.count).to eql(17) }
59
+ end
60
+
61
+ context 'only end time' do
62
+ subject { Event.between_times(nil, Time.zone.parse('2014-01-01')) }
63
+ it { expect(subject.count).to eql(13) }
64
+
65
+ context 'neither start nor end time' do
66
+ subject { Event.between_times(nil, nil) }
67
+ it { expect(subject.count).to eql(30) }
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ context 'two-sided query' do
74
+ context 'DST starts (Sydney)', sydney: true do
75
+ context 'day before' do
76
+ subject { Event.between_times(Date.parse('2020-04-04'), Date.parse('2020-04-04'), offset: 5.hours) }
77
+ it { expect(subject.count).to eql(3) }
78
+ end
79
+
80
+ context 'same day' do
81
+ subject { Event.between_times(Date.parse('2020-04-05'), Date.parse('2020-04-05'), offset: 5.hours) }
82
+ it { expect(subject.count).to eql(1) }
83
+ end
84
+ end
85
+
86
+ context 'when DST ends (Sydney)', sydney: true do
87
+ context 'day before' do
88
+ subject { Event.between_times(Date.parse('2020-10-03'), Date.parse('2020-10-03'), offset: 5.hours) }
89
+ it { expect(subject.count).to eql(1) }
90
+ end
91
+
92
+ context 'same day' do
93
+ subject { Event.between_times(Date.parse('2020-10-04'), Date.parse('2020-10-04'), offset: 5.hours) }
94
+ it { expect(subject.count).to eql(3) }
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end