close #420; Add VersionConcern#where_object_changes

This commit is contained in:
Ben Atkins 2015-01-07 16:57:45 -05:00
parent a5334ed3b3
commit 4a92ed7ea5
6 changed files with 97 additions and 6 deletions

View File

@ -5,16 +5,18 @@
PaperTrail::Rails::Engine.eager_load!
```
Also
*Also*
If you depend on the `RSpec` or `Cucumber` helpers, you will need to [manually load them into your test helper](https://github.com/airblade/paper_trail#testing).
- [#440](https://github.com/airblade/paper_trail/pull/440) - `versions` association should clear/reload after a transaction rollback.
- [#439](https://github.com/airblade/paper_trail/pull/439) / [#12](https://github.com/airblade/paper_trail/issues/12) -
Support for versioning of associations (Has Many, Has One, HABTM, etc.)
- [#440](https://github.com/airblade/paper_trail/pull/440) - `versions` association should clear/reload after a transaction rollback.
- [#438](https://github.com/airblade/paper_trail/issues/438) - `Model.paper_trail_enabled_for_model?` should return `false` if
`has_paper_trail` has not been declared on the class.
- [#427](https://github.com/airblade/paper_trail/pull/427) - Fix `reify` method in context of model where a column has been removed.
- [#420](https://github.com/airblade/paper_trail/issues/420) - Add `VersionConcern#where_object_changes` instance method;
acts as a helper for querying against the `object_changes` column in versions table.
- [#416](https://github.com/airblade/paper_trail/issues/416) - Added a `config` option for enabling/disabling
utilization of `serialized_attributes` for `ActiveRecord`, necessary because `serialized_attributes` has been
deprecated in `ActiveRecord` version `4.2` and will be removed in version `5.0`

View File

@ -173,6 +173,9 @@ version.event
# Query versions objects by attributes.
PaperTrail::Version.where_object(attr1: val1, attr2: val2)
# Query versions object_changes field by attributes (requires [`object_changes`](https://github.com/airblade/paper_trail#diffing-versions) column on versions table)
PaperTrail::Version.where_object_changes(attr1: val1)
```
In your controllers you can override these methods:

View File

@ -13,8 +13,8 @@ module PaperTrail
ActiveSupport::JSON.encode object
end
# Returns a SQL condition to be used to match the given field and value in
# the serialized object.
# Returns a SQL condition to be used to match the given field and value
# in the serialized object
def where_object_condition(arel_field, field, value)
# Convert to JSON to handle strings and nulls correctly.
json_value = value.to_json
@ -31,6 +31,17 @@ module PaperTrail
arel_field.matches("%\"#{field}\":#{json_value}%")
end
end
# Returns a SQL condition to be used to match the given field and value
# in the serialized object_changes
def where_object_changes_condition(arel_field, field, value)
# Convert to JSON to handle strings and nulls correctly.
json_value = value.to_json
# Need to check first (before) and secondary (after) fields
arel_field.matches("%\"#{field}\":[#{json_value},%").
or(arel_field.matches("%\"#{field}\":[%,#{json_value}]%"))
end
end
end
end

View File

@ -13,11 +13,19 @@ module PaperTrail
::YAML.dump object
end
# Returns a SQL condition to be used to match the given field and value in
# the serialized object.
# Returns a SQL condition to be used to match the given field and value
# in the serialized object
def where_object_condition(arel_field, field, value)
arel_field.matches("%\n#{field}: #{value}\n%")
end
# Returns a SQL condition to be used to match the given field and value
# in the serialized object_changes
def where_object_changes_condition(arel_field, field, value)
# Need to check first (before) and secondary (after) fields
arel_field.matches("%\n#{field}:\n- #{value}\n%").
or(arel_field.matches("%\n#{field}:\n- %\n- #{value}\n%"))
end
end
end
end

View File

@ -88,6 +88,19 @@ module PaperTrail
where(where_conditions)
end
def where_object_changes(args = {})
raise ArgumentError, 'expected to receive a Hash' unless args.is_a?(Hash)
arel_field = arel_table[:object_changes]
where_conditions = args.map do |field, value|
PaperTrail.serializer.where_object_changes_condition(arel_field, field, value)
end.reduce do |condition1, condition2|
condition1.and(condition2)
end
where(where_conditions)
end
def primary_key_is_int?
@primary_key_is_int ||= columns_hash[primary_key].type == :integer
rescue

View File

@ -108,6 +108,60 @@ describe PaperTrail::Version, :type => :model do
end
end
end
describe '#where_object_changes' do
it { expect(PaperTrail::Version).to respond_to(:where_object_changes) }
context "invalid arguments" do
it "should raise an error" do
expect { PaperTrail::Version.where_object_changes(:foo) }.to raise_error(ArgumentError)
expect { PaperTrail::Version.where_object_changes([]) }.to raise_error(ArgumentError)
end
end
context "valid arguments", :versioning => true do
let(:widget) { Widget.new }
let(:name) { Faker::Name.first_name }
let(:int) { rand(10) + 1 }
before do
widget.update_attributes!(:name => name, :an_integer => 0)
widget.update_attributes!(:name => 'foobar', :an_integer => 100)
widget.update_attributes!(:name => Faker::Name.last_name, :an_integer => int)
end
context "`serializer == YAML`" do
specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::YAML }
it "should be able to locate versions according to their `object_changes` contents" do
expect(PaperTrail::Version.where_object_changes(:name => name)).to eq(widget.versions[0..1])
expect(PaperTrail::Version.where_object_changes(:an_integer => 100)).to eq(widget.versions[1..2])
expect(PaperTrail::Version.where_object_changes(:an_integer => int)).to eq([widget.versions.last])
end
it "should be able to handle queries for multiple attributes" do
expect(PaperTrail::Version.where_object_changes(:an_integer => 100, :name => 'foobar')).to eq(widget.versions[1..2])
end
end
context "`serializer == JSON`" do
before(:all) { PaperTrail.serializer = PaperTrail::Serializers::JSON }
specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::JSON }
it "should be able to locate versions according to their `object_changes` contents" do
expect(PaperTrail::Version.where_object_changes(:name => name)).to eq(widget.versions[0..1])
expect(PaperTrail::Version.where_object_changes(:an_integer => 100)).to eq(widget.versions[1..2])
expect(PaperTrail::Version.where_object_changes(:an_integer => int)).to eq([widget.versions.last])
end
it "should be able to handle queries for multiple attributes" do
expect(PaperTrail::Version.where_object_changes(:an_integer => 100, :name => 'foobar')).to eq(widget.versions[1..2])
end
after(:all) { PaperTrail.serializer = PaperTrail::Serializers::YAML }
end
end
end
end
end
end