diff --git a/lib/activerecord-bitemporal/bitemporal.rb b/lib/activerecord-bitemporal/bitemporal.rb index 5e43fc2..c0e3199 100644 --- a/lib/activerecord-bitemporal/bitemporal.rb +++ b/lib/activerecord-bitemporal/bitemporal.rb @@ -329,6 +329,10 @@ def _update_row(attribute_names, attempted_action = 'update') self.transaction_from = after_instance.transaction_from self.transaction_to = after_instance.transaction_to + if attempted_action == 'touch' + @_touch_attr_names += Set.new(['valid_from', 'valid_to', 'transaction_from', 'transaction_to']) + end + 1 # MEMO: Must return false instead of nil, if `#_update_row` failure. end || false diff --git a/spec/activerecord-bitemporal/bitemporal_spec.rb b/spec/activerecord-bitemporal/bitemporal_spec.rb index 7490b30..1cff43b 100644 --- a/spec/activerecord-bitemporal/bitemporal_spec.rb +++ b/spec/activerecord-bitemporal/bitemporal_spec.rb @@ -27,10 +27,13 @@ it { is_expected.to have_attributes( bitemporal_id: subject.id, + # changes: be_empty, previous_changes: include( "id" => [nil, subject.swapped_id], "valid_from" => [nil, be_present], "valid_to" => [nil, "2019/10/01".in_time_zone], + "transaction_from" => [nil, be_present], + "transaction_to" => [nil, ActiveRecord::Bitemporal::DEFAULT_TRANSACTION_TO], "name" => [nil, "Tom"] ) ) @@ -42,10 +45,13 @@ it { is_expected.to have_attributes( bitemporal_id: subject.id, + changes: be_empty, previous_changes: include( "id" => [nil, subject.id], "valid_from" => [nil, be_present], "valid_to" => [nil, ActiveRecord::Bitemporal::DEFAULT_VALID_TO], + "transaction_from" => [nil, be_present], + "transaction_to" => [nil, ActiveRecord::Bitemporal::DEFAULT_TRANSACTION_TO], "name" => [nil, "Tom"] ) ) @@ -653,7 +659,7 @@ def self.bitemporal_id_key let(:from) { time_current } let(:to) { from + 10.days } let(:finish) { Time.utc(9999, 12, 31).in_time_zone } - let!(:employee) { Employee.create!(name: "Jone", valid_from: from, valid_to: to) } + let!(:employee) { Timecop.freeze(from - 1.month) { Employee.create!(name: "Jone", valid_from: from, valid_to: to) } } let!(:swapped_id) { employee.swapped_id } let(:count) { -> { Employee.where(bitemporal_id: employee.id).ignore_valid_datetime.count } } let(:old_jone) { Employee.ignore_valid_datetime.within_deleted.find_by(bitemporal_id: employee.id, name: "Jone") } @@ -683,6 +689,13 @@ def self.bitemporal_id_key let(:now) { from + 5.days } it { expect { subject }.to change(&count).by(1) } it { expect { subject }.to change(employee, :name).from("Jone").to("Tom") } + it { expect { subject }.to change(employee, :valid_from).from(from).to(now) } + it { expect { subject }.not_to change(employee, :valid_to) } + it { expect { subject }.to change(employee, :transaction_from).to(now) } + it { expect { subject }.not_to change(employee, :transaction_to) } + it { expect(employee.changes).to be_empty } + it { expect { subject }.not_to change(employee, :changes) } + it { expect { subject }.to change { employee.saved_changes.keys }.to(contain_exactly("name", "valid_from", "transaction_from", "updated_at")) } it { expect { subject }.to change(employee, :swapped_id).from(swapped_id).to(kind_of(Integer)) } it { expect { subject }.to change(employee, :swapped_id_previously_was).from(nil).to(swapped_id) } it_behaves_like "updated Jone" do @@ -706,6 +719,13 @@ def self.bitemporal_id_key let(:now) { from - 5.days } it { expect { subject }.to change(&count).by(1) } it { expect { subject }.to change(employee, :name).from("Jone").to("Tom") } + it { expect { subject }.to change(employee, :valid_from).from(from).to(now) } + it { expect { subject }.to change(employee, :valid_to).from(to).to(from) } + it { expect { subject }.to change(employee, :transaction_from).to(now) } + it { expect { subject }.not_to change(employee, :transaction_to) } + it { expect(employee.changes).to be_empty } + it { expect { subject }.not_to change(employee, :changes) } + it { expect { subject }.to change { employee.saved_changes.keys }.to contain_exactly("name", "valid_from", "valid_to", "transaction_from", "updated_at") } it { expect { subject }.to change(employee, :swapped_id).from(swapped_id).to(kind_of(Integer)) } it { expect { subject }.to change(employee, :swapped_id_previously_was).from(nil).to(swapped_id) } it_behaves_like "updated Jone" do @@ -1052,6 +1072,8 @@ class EmployeeWithUniquness < Employee it { expect { subject }.to change { Employee.ignore_valid_datetime.within_deleted.count }.by(1) } it { expect { subject }.to change(employee, :swapped_id).from(@swapped_id_before_destroy).to(kind_of(Integer)) } it { expect { subject }.to change(employee, :swapped_id_previously_was).from(kind_of(Integer)).to(@swapped_id_before_destroy) } + xit { expect { subject }.to change(employee, :changes).to(be_empty) } + it { expect { subject }.not_to change(employee, :saved_changes) } it { expect(subject).to eq employee } it do @@ -1222,14 +1244,27 @@ class EmployeeWithUniquness < Employee end describe "#touch" do - let!(:employee) { Employee.create(name: "Jane").tap { |it| it.update!(name: "Tom") } } + let!(:employee) { Timecop.freeze(created_time) { Employee.create(name: "Tom") } } let(:employee_count) { -> { Employee.ignore_valid_datetime.bitemporal_for(employee.id).count } } - subject { employee.touch(:archived_at) } + let(:created_time) { time_current - 10.seconds } + let(:touched_time) { time_current - 5.seconds } + subject { Timecop.freeze(touched_time) { employee.touch(:archived_at) } } + + before { @swapped_id_before_touch = employee.swapped_id } it { expect(employee).to have_attributes(name: "Tom", id: employee.id) } it { expect(subject).to eq true } it { expect { subject }.to change(&employee_count).by(1) } it { expect { subject }.to change { employee.reload.archived_at }.from(nil) } + it { expect { subject }.to change(employee, :valid_from).from(created_time).to(touched_time) } + it { expect { subject }.not_to change(employee, :valid_to) } + it { expect { subject }.to change(employee, :transaction_from).from(created_time).to(touched_time) } + it { expect { subject }.not_to change(employee, :transaction_to) } + it { expect { subject }.to change(employee, :swapped_id).from(@swapped_id_before_touch).to(kind_of(Integer)) } + it { expect { subject }.to change(employee, :swapped_id_previously_was).from(nil).to(@swapped_id_before_touch) } + it { expect(employee.changes).to be_empty } + it { expect { subject }.not_to change(employee, :changes) } + it { expect { subject }.to change { employee.saved_changes.keys }.to(contain_exactly("archived_at", "valid_from", "transaction_from", "updated_at")) } end describe "validation" do