diff --git a/lib/activerecord-bitemporal/scope.rb b/lib/activerecord-bitemporal/scope.rb index 7cee4945..b45cb103 100644 --- a/lib/activerecord-bitemporal/scope.rb +++ b/lib/activerecord-bitemporal/scope.rb @@ -494,10 +494,25 @@ def _#{column}_#{op}(datetime,**) } # from <= valid_from AND valid_to <= to - scope :valid_allin, -> (from: nil, to: nil) { - ignore_valid_datetime - .tap { |relation| break relation.bitemporal_where_bind("valid_from", :gteq, from.in_time_zone.to_datetime) if from } - .tap { |relation| break relation.bitemporal_where_bind("valid_to", :lteq, to.in_time_zone.to_datetime) if to } + scope :valid_allin, -> (range = nil, from: nil, to: nil) { + return valid_allin(from..to) if range.nil? + + relation = ignore_valid_datetime + begin_, end_ = range.begin, range.end + + if begin_ + relation = relation.bitemporal_where_bind("valid_from", :gteq, begin_.in_time_zone.to_datetime) + end + + if end_ + if range.exclude_end? + raise 'Range with excluding end is not supported' + else + relation = relation.bitemporal_where_bind("valid_to", :lteq, end_.in_time_zone.to_datetime) + end + end + + relation } end diff --git a/spec/activerecord-bitemporal/scopes_spec.rb b/spec/activerecord-bitemporal/scopes_spec.rb index 129e22dd..21c58070 100644 --- a/spec/activerecord-bitemporal/scopes_spec.rb +++ b/spec/activerecord-bitemporal/scopes_spec.rb @@ -222,11 +222,72 @@ class EmployeeWithScope < Employee Time.zone = "Tokyo" end after { Time.zone = @old_time_zone } - let(:from) { "2019/1/20" } - let(:to) { "2019/1/30" } - subject { Employee.valid_allin(from: from, to: to).to_sql } - it { is_expected.to match %r/"employees"."valid_from" >= '2019-01-19 15:00:00'/ } - it { is_expected.to match %r/"employees"."valid_to" <= '2019-01-29 15:00:00'/ } + + let(:_01_20) { "2019/1/20" } + let(:_01_30) { "2019/1/30" } + + subject { relation.to_sql } + + context "with from: _01_20, to: _01_30" do + let(:relation) { Employee.valid_allin(from: _01_20, to: _01_30) } + + it { is_expected.to match %r/"employees"."valid_from" >= '2019-01-19 15:00:00'/ } + it { is_expected.to match %r/"employees"."valid_to" <= '2019-01-29 15:00:00'/ } + end + + context "with from: _01_30, to: _01_20" do + let(:relation) { Employee.valid_allin(from: _01_30, to: _01_20) } + + it { is_expected.to match %r/"employees"."valid_from" >= '2019-01-29 15:00:00'/ } + it { is_expected.to match %r/"employees"."valid_to" <= '2019-01-19 15:00:00'/ } + end + + context "with from: _01_20" do + let(:relation) { Employee.valid_allin(from: _01_20) } + + it { is_expected.to match %r/"employees"."valid_from" >= '2019-01-19 15:00:00'/ } + it { is_expected.not_to match %r/"employees"."valid_to"/ } + end + + context "with to: _01_30" do + let(:relation) { Employee.valid_allin(to: _01_30) } + + it { is_expected.not_to match %r/"employees"."valid_from"/ } + it { is_expected.to match %r/"employees"."valid_to" <= '2019-01-29 15:00:00'/ } + end + + context "with (_01_20.._01_30)" do + let(:relation) { Employee.valid_allin(_01_20.._01_30) } + + it { is_expected.to match %r/"employees"."valid_from" >= '2019-01-19 15:00:00'/ } + it { is_expected.to match %r/"employees"."valid_to" <= '2019-01-29 15:00:00'/ } + end + + context "with (_01_20..)" do + let(:relation) { Employee.valid_allin(_01_20..) } + + it { is_expected.to match %r/"employees"."valid_from" >= '2019-01-19 15:00:00'/ } + it { is_expected.not_to match %r/"employees"."valid_to"/ } + end + + context "with (.._01_30)" do + let(:relation) { Employee.valid_allin(.._01_30) } + + it { is_expected.not_to match %r/"employees"."valid_from"/ } + it { is_expected.to match %r/"employees"."valid_to" <= '2019-01-29 15:00:00'/ } + end + + context "with (_01_20..._01_30)" do + let(:relation) { Employee.valid_allin(_01_20..._01_30) } + + it { expect { subject }.to raise_error('Range with excluding end is not supported') } + end + + context "with (..._01_30)" do + let(:relation) { Employee.valid_allin(..._01_30) } + + it { expect { subject }.to raise_error('Range with excluding end is not supported') } + end end describe ".arel.to_sql" do