Extract query object: WhereObjectChanges

This is the first query object in PT, so I'll explain the rationale.

In my mind, the Query Object pattern is a subclass of the Method
Object pattern.

http://refactoring.com/catalog/replaceMethodWithMethodObject.html

The goal is simple, to break up a very large method. In this case,
there is another big advantage; it gives us a place to extract
methods that will actually be private. Better still, little private
methods will not pollut the namespace of classes that mix in
`VersionConcern`.
This commit is contained in:
Jared Beck 2016-12-11 03:33:04 -05:00
parent c7efd62fe2
commit 012533ad7d
3 changed files with 69 additions and 24 deletions

View File

@ -2,7 +2,7 @@
# one by one as the offenses are removed from the code base.
Metrics/AbcSize:
Max: 30 # Goal: 15
Max: 28 # Goal: 15
Metrics/CyclomaticComplexity:
Max: 8 # Goal: 6

View File

@ -0,0 +1,66 @@
module PaperTrail
module Queries
module Versions
# @api private
class WhereObjectChanges
# - version_model_class - The class that VersionConcern was mixed into.
# - attributes - A `Hash` of attributes and values. See the public API
# documentation for details.
# @api private
def initialize(version_model_class, attributes = {})
@version_model_class = version_model_class
# Currently, this `deep_dup` is necessary because the `jsonb` branch
# modifies `@attributes`, and that would be a nasty suprise for
# consumers of this class.
# TODO: Stop modifying `@attributes`, then remove `deep_dup`.
@attributes = attributes.deep_dup
end
# @api private
def execute
case @version_model_class.columns_hash["object_changes"].type
when :jsonb
jsonb
when :json
json
else
text
end
end
private
# @api private
def json
predicates = []
values = []
@attributes.each do |field, value|
predicates.push(
"((object_changes->>? ILIKE ?) OR (object_changes->>? ILIKE ?))"
)
values.concat([field, "[#{value.to_json},%", field, "[%,#{value.to_json}]%"])
end
sql = predicates.join(" and ")
@version_model_class.where(sql, *values)
end
# @api private
def jsonb
@attributes.each { |field, value| @attributes[field] = [value] }
@version_model_class.where("object_changes @> ?", @attributes.to_json)
end
# @api private
def text
arel_field = @version_model_class.arel_table[:object_changes]
where_conditions = @attributes.map { |field, value|
::PaperTrail.serializer.where_object_changes_condition(arel_field, field, value)
}
where_conditions = where_conditions.reduce { |a, e| a.and(e) }
@version_model_class.where(where_conditions)
end
end
end
end
end

View File

@ -1,5 +1,6 @@
require "active_support/concern"
require "paper_trail/attribute_serializers/object_changes_attribute"
require "paper_trail/queries/versions/where_object_changes"
module PaperTrail
# Originally, PaperTrail did not provide this module, and all of this
@ -167,29 +168,7 @@ module PaperTrail
# @api public
def where_object_changes(args = {})
raise ArgumentError, "expected to receive a Hash" unless args.is_a?(Hash)
if columns_hash["object_changes"].type == :jsonb
args.each { |field, value| args[field] = [value] }
where("object_changes @> ?", args.to_json)
elsif columns_hash["object_changes"].type == :json
predicates = []
values = []
args.each do |field, value|
predicates.push(
"((object_changes->>? ILIKE ?) OR (object_changes->>? ILIKE ?))"
)
values.concat([field, "[#{value.to_json},%", field, "[%,#{value.to_json}]%"])
end
sql = predicates.join(" and ")
where(sql, *values)
else
arel_field = arel_table[:object_changes]
where_conditions = args.map { |field, value|
PaperTrail.serializer.where_object_changes_condition(arel_field, field, value)
}
where_conditions = where_conditions.reduce { |a, e| a.and(e) }
where(where_conditions)
end
Queries::Versions::WhereObjectChanges.new(self, args).execute
end
def primary_key_is_int?