diff --git a/lib/activerecord-bitemporal/bitemporal.rb b/lib/activerecord-bitemporal/bitemporal.rb index 52b484ab..175157b7 100644 --- a/lib/activerecord-bitemporal/bitemporal.rb +++ b/lib/activerecord-bitemporal/bitemporal.rb @@ -445,14 +445,14 @@ def _update_row(attribute_names, attempted_action = 'update') # 以前の履歴データは valid_to を詰めて保存 before_instance.valid_to = target_datetime - raise ActiveRecord::Rollback if before_instance.valid_from_cannot_be_greater_equal_than_valid_to + raise ActiveRecord::RecordInvalid.new(before_instance) if before_instance.valid_from_cannot_be_greater_equal_than_valid_to before_instance.transaction_from = current_time before_instance.save!(validate: false) # 以降の履歴データは valid_from と valid_to を調整して保存する after_instance.valid_from = target_datetime after_instance.valid_to = current_valid_record.valid_to - raise ActiveRecord::Rollback if after_instance.valid_from_cannot_be_greater_equal_than_valid_to + raise ActiveRecord::RecordInvalid.new(after_instance) if after_instance.valid_from_cannot_be_greater_equal_than_valid_to after_instance.transaction_from = current_time after_instance.save!(validate: false) @@ -460,6 +460,10 @@ def _update_row(attribute_names, attempted_action = 'update') else # 一番近い未来にある Instance を取ってきて、その valid_from を valid_to に入れる nearest_instance = self.class.where(bitemporal_id: bitemporal_id).bitemporal_where_bind("valid_from", :gt, target_datetime).ignore_valid_datetime.order(valid_from: :asc).first + if nearest_instance.nil? + message = "Update failed: Couldn't find #{self.class} with 'bitemporal_id'=#{self.bitemporal_id} and 'valid_from' < #{target_datetime}" + raise ActiveRecord::RecordNotFound.new(message, self.class, "bitemporal_id", self.bitemporal_id) + end # valid_from と valid_to を調整して保存する after_instance.valid_from = target_datetime diff --git a/spec/activerecord-bitemporal/bitemporal_spec.rb b/spec/activerecord-bitemporal/bitemporal_spec.rb index e10d9f9b..9ff83bfa 100644 --- a/spec/activerecord-bitemporal/bitemporal_spec.rb +++ b/spec/activerecord-bitemporal/bitemporal_spec.rb @@ -784,13 +784,12 @@ def employee.wrapped_name end context "failure" do - let(:company) { Company.create!(valid_from: "2019/2/1") } - let(:company_count) { -> { Company.ignore_valid_datetime.bitemporal_for(company.id).count } } - let(:company_deleted_at) { -> { Company.ignore_valid_datetime.within_deleted.bitemporal_for(company.id).first.deleted_at } } - subject { -> { company.valid_at(valid_datetime) { |c| c.update(name: "Company") } } } - context "`valid_datetime` is `company.valid_from`" do + let(:company) { Company.create!(valid_from: "2019/2/1") } + let(:company_count) { -> { Company.ignore_valid_datetime.bitemporal_for(company.id).count } } + let(:company_deleted_at) { -> { Company.ignore_valid_datetime.within_deleted.bitemporal_for(company.id).first.deleted_at } } let(:valid_datetime) { company.valid_from } + subject { -> { company.valid_at(valid_datetime) { |c| c.update(name: "Company") } } } it { expect(subject.call).to be_falsey } it { is_expected.not_to change(&company_count) } @@ -798,9 +797,23 @@ def employee.wrapped_name context "call `update!`" do subject { -> { company.valid_at(valid_datetime) { |c| c.update!(name: "Company") } } } - it { is_expected.to raise_error(ActiveRecord::RecordNotSaved) } + it { is_expected.to raise_error(ActiveRecord::RecordInvalid) } + it { is_expected.to raise_error { |e| + expect(e.message).to eq "Validation failed: Valid from can't be greater equal than valid_to" + } } end end + + context "update for deleted record" do + let(:datetime) { "2020/01/01".in_time_zone } + let(:company) { Company.create!(valid_from: "2019/02/01", valid_to: "2019/04/01") } + subject { -> { Timecop.freeze(datetime) { company.update!(name: "Company2") } } } + before { company.destroy } + it { is_expected.to raise_error(ActiveRecord::RecordNotFound) } + it { is_expected.to raise_error { |e| + expect(e.message).to eq "Update failed: Couldn't find Company with 'bitemporal_id'=#{company.bitemporal_id} and 'valid_from' < #{datetime}" + } } + end end end