From a3936bbe21f4bff8247f890cacfd0fc882921003 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Fri, 26 Dec 2014 14:12:16 -0700 Subject: [PATCH] Change `PredicateBuilder` handler methods to instance methods This will allow us to pass the predicate builder into the constructor of these handlers. The procs had to be changed to objects, because the `PredicateBuilder` needs to be marshalable. If we ever decide to make `register_handler` part of the public API, we should come up with a better solution which allows procs. /cc @mrgilman [Sean Griffin & Melanie Gilman] --- .../relation/predicate_builder.rb | 55 +++++++++---------- .../predicate_builder/base_handler.rb | 9 +++ .../predicate_builder/basic_object_handler.rb | 9 +++ .../predicate_builder/class_handler.rb | 19 +++++++ .../predicate_builder/range_handler.rb | 9 +++ .../cases/relation/predicate_builder_test.rb | 4 +- 6 files changed, 74 insertions(+), 31 deletions(-) create mode 100644 activerecord/lib/active_record/relation/predicate_builder/base_handler.rb create mode 100644 activerecord/lib/active_record/relation/predicate_builder/basic_object_handler.rb create mode 100644 activerecord/lib/active_record/relation/predicate_builder/class_handler.rb create mode 100644 activerecord/lib/active_record/relation/predicate_builder/range_handler.rb diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 67e646bf18..71bb795d5b 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -1,13 +1,23 @@ module ActiveRecord class PredicateBuilder # :nodoc: - @handlers = [] - - autoload :RelationHandler, 'active_record/relation/predicate_builder/relation_handler' - autoload :ArrayHandler, 'active_record/relation/predicate_builder/array_handler' + require 'active_record/relation/predicate_builder/array_handler' + require 'active_record/relation/predicate_builder/base_handler' + require 'active_record/relation/predicate_builder/basic_object_handler' + require 'active_record/relation/predicate_builder/class_handler' + require 'active_record/relation/predicate_builder/range_handler' + require 'active_record/relation/predicate_builder/relation_handler' def initialize(klass, table) @klass = klass @table = table + @handlers = [] + + register_handler(BasicObject, BasicObjectHandler.new) + register_handler(Class, ClassHandler.new) + register_handler(Base, BaseHandler.new) + register_handler(Range, RangeHandler.new) + register_handler(Relation, RelationHandler.new) + register_handler(Array, ArrayHandler.new) end def resolve_column_aliases(hash) @@ -35,13 +45,13 @@ module ActiveRecord # PriceEstimate.where(estimate_of: treasure) if klass && reflection = klass._reflect_on_association(column) if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value) - queries << self.class.build(table[reflection.foreign_type], base_class.name) + queries << build(table[reflection.foreign_type], base_class.name) end column = reflection.foreign_key end - queries << self.class.build(table[column], value) + queries << build(table[column], value) queries end @@ -79,33 +89,10 @@ module ActiveRecord # ) # end # ActiveRecord::PredicateBuilder.register_handler(MyCustomDateRange, handler) - def self.register_handler(klass, handler) + def register_handler(klass, handler) @handlers.unshift([klass, handler]) end - register_handler(BasicObject, ->(attribute, value) { attribute.eq(value) }) - register_handler(Class, ->(attribute, value) { deprecate_class_handler; attribute.eq(value.name) }) - register_handler(Base, ->(attribute, value) { attribute.eq(value.id) }) - register_handler(Range, ->(attribute, value) { attribute.between(value) }) - register_handler(Relation, RelationHandler.new) - register_handler(Array, ArrayHandler.new) - - def self.build(attribute, value) - handler_for(value).call(attribute, value) - end - - def self.handler_for(object) - @handlers.detect { |klass, _| klass === object }.last - end - private_class_method :handler_for - - def self.deprecate_class_handler - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Passing a class as a value in an Active Record query is deprecated and - will be removed. Pass a string instead. - MSG - end - protected attr_reader :klass, :table @@ -141,5 +128,13 @@ module ActiveRecord attributes end + + def build(attribute, value) + handler_for(value).call(attribute, value) + end + + def handler_for(object) + @handlers.detect { |klass, _| klass === object }.last + end end end diff --git a/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb new file mode 100644 index 0000000000..d50ea519f9 --- /dev/null +++ b/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb @@ -0,0 +1,9 @@ +module ActiveRecord + class PredicateBuilder + class BaseHandler # :nodoc: + def call(attribute, value) + attribute.eq(value.id) + end + end + end +end diff --git a/activerecord/lib/active_record/relation/predicate_builder/basic_object_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/basic_object_handler.rb new file mode 100644 index 0000000000..79cde00303 --- /dev/null +++ b/activerecord/lib/active_record/relation/predicate_builder/basic_object_handler.rb @@ -0,0 +1,9 @@ +module ActiveRecord + class PredicateBuilder + class BasicObjectHandler # :nodoc: + def call(attribute, value) + attribute.eq(value) + end + end + end +end diff --git a/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb new file mode 100644 index 0000000000..3fe1642ed0 --- /dev/null +++ b/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb @@ -0,0 +1,19 @@ +module ActiveRecord + class PredicateBuilder + class ClassHandler # :nodoc: + def call(attribute, value) + print_deprecation_warning + attribute.eq(value.name) + end + + private + + def print_deprecation_warning + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Passing a class as a value in an Active Record query is deprecated and + will be removed. Pass a string instead. + MSG + end + end + end +end diff --git a/activerecord/lib/active_record/relation/predicate_builder/range_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/range_handler.rb new file mode 100644 index 0000000000..47dc46bb4b --- /dev/null +++ b/activerecord/lib/active_record/relation/predicate_builder/range_handler.rb @@ -0,0 +1,9 @@ +module ActiveRecord + class PredicateBuilder + class RangeHandler # :nodoc: + def call(attribute, value) + attribute.between(value) + end + end + end +end diff --git a/activerecord/test/cases/relation/predicate_builder_test.rb b/activerecord/test/cases/relation/predicate_builder_test.rb index 4057835688..0cc081fced 100644 --- a/activerecord/test/cases/relation/predicate_builder_test.rb +++ b/activerecord/test/cases/relation/predicate_builder_test.rb @@ -4,11 +4,13 @@ require 'models/topic' module ActiveRecord class PredicateBuilderTest < ActiveRecord::TestCase def test_registering_new_handlers - PredicateBuilder.register_handler(Regexp, proc do |column, value| + Topic.predicate_builder.register_handler(Regexp, proc do |column, value| Arel::Nodes::InfixOperation.new('~', column, Arel.sql(value.source)) end) assert_match %r{["`]topics["`].["`]title["`] ~ rails}i, Topic.where(title: /rails/).to_sql + ensure + Topic.reset_column_information end end end