Merge branch 'where_object_method'; close #377
This commit is contained in:
commit
a628c9c266
|
@ -12,6 +12,25 @@ module PaperTrail
|
|||
def dump(object)
|
||||
ActiveSupport::JSON.encode object
|
||||
end
|
||||
|
||||
# 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
|
||||
|
||||
# If the value is a number, we need to ensure that we find the next
|
||||
# character too, which is either `,` or `}`, to ensure that searching
|
||||
# for the value 12 doesn't yield false positives when the value is
|
||||
# 123.
|
||||
if value.is_a? Numeric
|
||||
arel_field.matches("%\"#{field}\":#{json_value},%").
|
||||
or(
|
||||
arel_field.matches("%\"#{field}\":#{json_value}}%"))
|
||||
else
|
||||
arel_field.matches("%\"#{field}\":#{json_value}%")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,6 +12,12 @@ module PaperTrail
|
|||
def dump(object)
|
||||
::YAML.dump object
|
||||
end
|
||||
|
||||
# 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
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -68,6 +68,21 @@ module PaperTrail
|
|||
end
|
||||
end
|
||||
|
||||
# Performs an attribute search on the serialized object by invoking the
|
||||
# identically-named method in the serializer being used.
|
||||
def where_object(args = {})
|
||||
raise ArgumentError, 'expected to receive a Hash' unless args.is_a?(Hash)
|
||||
arel_field = arel_table[:object]
|
||||
|
||||
where_conditions = args.map do |field, value|
|
||||
PaperTrail.serializer.where_object_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
|
||||
|
@ -190,7 +205,7 @@ module PaperTrail
|
|||
|
||||
def index
|
||||
table = self.class.arel_table unless @index
|
||||
@index ||=
|
||||
@index ||=
|
||||
if self.class.primary_key_is_int?
|
||||
sibling_versions.select(table[self.class.primary_key]).order(table[self.class.primary_key].asc).index(self)
|
||||
else
|
||||
|
|
|
@ -40,5 +40,49 @@ describe PaperTrail::Version do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Class" do
|
||||
describe :where_object do
|
||||
it { PaperTrail::Version.should respond_to(:where_object) }
|
||||
|
||||
context "invalid arguments" do
|
||||
it "should raise an error" do
|
||||
expect { PaperTrail::Version.where_object(:foo) }.to raise_error(ArgumentError)
|
||||
expect { PaperTrail::Version.where_object([]) }.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 => int)
|
||||
widget.update_attributes!(:name => 'foobar', :an_integer => 100)
|
||||
widget.update_attributes!(:name => Faker::Name.last_name, :an_integer => 15)
|
||||
end
|
||||
|
||||
context "`serializer == YAML`" do
|
||||
specify { PaperTrail.serializer == PaperTrail::Serializers::YAML }
|
||||
|
||||
it "should be able to locate versions according to their `object` contents" do
|
||||
PaperTrail::Version.where_object(:name => name).should == [widget.versions[1]]
|
||||
PaperTrail::Version.where_object(:an_integer => 100).should == [widget.versions[2]]
|
||||
end
|
||||
end
|
||||
|
||||
context "`serializer == JSON`" do
|
||||
before { PaperTrail.serializer = PaperTrail::Serializers::JSON }
|
||||
specify { PaperTrail.serializer == PaperTrail::Serializers::JSON }
|
||||
|
||||
it "should be able to locate versions according to their `object` contents" do
|
||||
PaperTrail::Version.where_object(:name => name).should == [widget.versions[1]]
|
||||
PaperTrail::Version.where_object(:an_integer => 100).should == [widget.versions[2]]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -37,4 +37,39 @@ class JSONTest < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
context '`where_object` class method' do
|
||||
context "when value is a string" do
|
||||
should 'construct correct WHERE query' do
|
||||
matches = PaperTrail::Serializers::JSON.where_object_condition(
|
||||
PaperTrail::Version.arel_table[:object], :arg1, "Val 1")
|
||||
|
||||
assert matches.instance_of?(Arel::Nodes::Matches)
|
||||
assert_equal matches.right, "%\"arg1\":\"Val 1\"%"
|
||||
end
|
||||
end
|
||||
|
||||
context "when value is `null`" do
|
||||
should 'construct correct WHERE query' do
|
||||
matches = PaperTrail::Serializers::JSON.where_object_condition(
|
||||
PaperTrail::Version.arel_table[:object], :arg1, nil)
|
||||
|
||||
assert matches.instance_of?(Arel::Nodes::Matches)
|
||||
assert_equal matches.right, "%\"arg1\":null%"
|
||||
end
|
||||
end
|
||||
|
||||
context "when value is a number" do
|
||||
should 'construct correct WHERE query' do
|
||||
grouping = PaperTrail::Serializers::JSON.where_object_condition(
|
||||
PaperTrail::Version.arel_table[:object], :arg1, -3.5)
|
||||
|
||||
assert grouping.instance_of?(Arel::Nodes::Grouping)
|
||||
matches = grouping.select { |v| v.instance_of?(Arel::Nodes::Matches) }
|
||||
# Numeric arguments need to ensure that they match for only the number, not the beginning
|
||||
# of a #, so it uses an Grouping matcher (See notes on `PaperTrail::Serializers::JSON`)
|
||||
assert_equal matches.first.right, "%\"arg1\":-3.5,%"
|
||||
assert_equal matches.last.right, "%\"arg1\":-3.5}%"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -37,4 +37,12 @@ class YamlTest < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
context '`where_object` class method' do
|
||||
should 'construct correct WHERE query' do
|
||||
matches = PaperTrail::Serializers::YAML.where_object_condition(
|
||||
PaperTrail::Version.arel_table[:object], :arg1, "Val 1")
|
||||
assert matches.instance_of?(Arel::Nodes::Matches)
|
||||
assert_equal matches.right, "%\narg1: Val 1\n%"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue