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,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