From 926fae7bc9d521c891ce1c2892576deac7316a46 Mon Sep 17 00:00:00 2001 From: Stephen Ierodiaconou Date: Fri, 4 Oct 2024 13:47:32 +0200 Subject: [PATCH] Instead of a configurable base class, you can specifty what class to use to construct new query objects on merge/compose --- lib/quo.rb | 11 ++++++++--- lib/quo/collection_backed_query.rb | 3 +-- lib/quo/composed_query.rb | 10 +++++----- lib/quo/query.rb | 5 +++-- lib/quo/relation_backed_query.rb | 3 +-- sig/generated/quo.rbs | 4 +++- sig/generated/quo/collection_backed_query.rbs | 3 +-- sig/generated/quo/query.rbs | 1 + sig/generated/quo/relation_backed_query.rbs | 3 +-- .../dummy/app/queries/application_collection_query.rb | 8 ++++++++ ...ication_query.rb => application_relation_query.rb} | 4 ++-- test/dummy/config/initializers/quo.rb | 3 ++- test/quo/composed_query_test.rb | 9 +++++---- test/quo/custom_base_class_test.rb | 8 ++++---- 14 files changed, 45 insertions(+), 30 deletions(-) create mode 100644 test/dummy/app/queries/application_collection_query.rb rename test/dummy/app/queries/{application_query.rb => application_relation_query.rb} (67%) diff --git a/lib/quo.rb b/lib/quo.rb index 960f92e..56f9326 100644 --- a/lib/quo.rb +++ b/lib/quo.rb @@ -17,11 +17,16 @@ module Quo autoload :ComposedQuery autoload :CollectionBackedQuery - mattr_accessor :base_query_class, default: "Quo::Query" + mattr_accessor :relation_backed_query_base_class, default: "Quo::RelationBackedQuery" + mattr_accessor :collection_backed_query_base_class, default: "Quo::CollectionBackedQuery" mattr_accessor :max_page_size, default: 200 mattr_accessor :default_page_size, default: 20 - def self.base_query_class #: Quo::Query - @@base_query_class.constantize + def self.relation_backed_query_base_class #: Quo::RelationBackedQuery + @@relation_backed_query_base_class.constantize + end + + def self.collection_backed_query_base_class #: Quo::CollectionBackedQuery + @@collection_backed_query_base_class.constantize end end diff --git a/lib/quo/collection_backed_query.rb b/lib/quo/collection_backed_query.rb index ec44340..d2b3111 100644 --- a/lib/quo/collection_backed_query.rb +++ b/lib/quo/collection_backed_query.rb @@ -3,8 +3,7 @@ # rbs_inline: enabled module Quo - # @rbs inherits Quo::Query - class CollectionBackedQuery < Quo.base_query_class + class CollectionBackedQuery < Query prop :total_count, _Nilable(Integer) # Wrap an enumerable collection or a block that returns an enumerable collection diff --git a/lib/quo/composed_query.rb b/lib/quo/composed_query.rb index 3d838a0..f92773f 100644 --- a/lib/quo/composed_query.rb +++ b/lib/quo/composed_query.rb @@ -30,7 +30,7 @@ class << self def inspect left_desc = quo_operand_desc(_left_query) right_desc = quo_operand_desc(_right_query) - klass_name = (self < Quo::RelationBackedQuery) ? Quo::RelationBackedQuery.name : Quo::CollectionBackedQuery.name + klass_name = (self < Quo::RelationBackedQuery) ? Quo.relation_backed_query_base_class.name : Quo.collection_backed_query_base_class.name "#{klass_name}[#{left_desc}, #{right_desc}]" end @@ -65,14 +65,14 @@ def merge_instances(left_instance, right_instance, joins: nil) raise ArgumentError, "Cannot merge, left has incompatible type #{left_instance.class}" unless left_instance.is_a?(Quo::Query) || left_instance.is_a?(::ActiveRecord::Relation) raise ArgumentError, "Cannot merge, right has incompatible type #{right_instance.class}" unless right_instance.is_a?(Quo::Query) || right_instance.is_a?(::ActiveRecord::Relation) if left_instance.is_a?(Quo::Query) && right_instance.is_a?(::ActiveRecord::Relation) - return composer(left_instance.is_a?(Quo::RelationBackedQuery) ? Quo::RelationBackedQuery : Quo::CollectionBackedQuery, left_instance.class, right_instance, joins: joins).new(**left_instance.to_h) + return composer(left_instance.is_a?(Quo::RelationBackedQuery) ? Quo.relation_backed_query_base_class : Quo.collection_backed_query_base_class, left_instance.class, right_instance, joins: joins).new(**left_instance.to_h) elsif right_instance.is_a?(Quo::Query) && left_instance.is_a?(::ActiveRecord::Relation) - return composer(right_instance.is_a?(Quo::RelationBackedQuery) ? Quo::RelationBackedQuery : Quo::CollectionBackedQuery, left_instance, right_instance.class, joins: joins).new(**right_instance.to_h) + return composer(right_instance.is_a?(Quo::RelationBackedQuery) ? Quo.relation_backed_query_base_class : Quo.collection_backed_query_base_class, left_instance, right_instance.class, joins: joins).new(**right_instance.to_h) elsif left_instance.is_a?(Quo::Query) && right_instance.is_a?(Quo::Query) props = left_instance.to_h.merge(right_instance.to_h.compact) - return composer((left_instance.is_a?(Quo::RelationBackedQuery) && right_instance.is_a?(Quo::RelationBackedQuery)) ? Quo::RelationBackedQuery : Quo::CollectionBackedQuery, left_instance.class, right_instance.class, joins: joins).new(**props) + return composer((left_instance.is_a?(Quo::RelationBackedQuery) && right_instance.is_a?(Quo::RelationBackedQuery)) ? Quo.relation_backed_query_base_class : Quo.collection_backed_query_base_class, left_instance.class, right_instance.class, joins: joins).new(**props) end - composer(Quo::RelationBackedQuery, left_instance, right_instance, joins: joins).new # Both are AR relations + composer(Quo.relation_backed_query_base_class, left_instance, right_instance, joins: joins).new # Both are AR relations end module_function :merge_instances diff --git a/lib/quo/query.rb b/lib/quo/query.rb index 70639f4..dbcf5be 100644 --- a/lib/quo/query.rb +++ b/lib/quo/query.rb @@ -24,15 +24,16 @@ def to_s inspect end + # TODO: put this in a module with the composer and merge_instances methods # Compose is aliased as `+`. Can optionally take `joins` parameters to add joins on merged relation. # @rbs right: Quo::Query | ActiveRecord::Relation | Object & Enumerable[untyped] # @rbs joins: Symbol | Hash[Symbol, untyped] | Array[Symbol | Hash[Symbol, untyped]] # @rbs return: Quo::Query & Quo::ComposedQuery def self.compose(right, joins: nil) super_class = if self < Quo::CollectionBackedQuery || right < Quo::CollectionBackedQuery - Quo::CollectionBackedQuery + Quo.collection_backed_query_base_class else - Quo::RelationBackedQuery + Quo.relation_backed_query_base_class end ComposedQuery.composer(super_class, self, right, joins: joins) end diff --git a/lib/quo/relation_backed_query.rb b/lib/quo/relation_backed_query.rb index c274bc9..793ef49 100644 --- a/lib/quo/relation_backed_query.rb +++ b/lib/quo/relation_backed_query.rb @@ -5,8 +5,7 @@ require "literal" module Quo - # @rbs inherits Quo::Query - class RelationBackedQuery < Quo.base_query_class + class RelationBackedQuery < Query # @rbs query: ActiveRecord::Relation | Quo::Query # @rbs props: Hash[Symbol, untyped] # @rbs &block: () -> ActiveRecord::Relation | Quo::Query | Object & Enumerable[untyped] diff --git a/sig/generated/quo.rbs b/sig/generated/quo.rbs index 01ae787..382cdeb 100644 --- a/sig/generated/quo.rbs +++ b/sig/generated/quo.rbs @@ -3,5 +3,7 @@ module Quo extend ActiveSupport::Autoload - def self.base_query_class: () -> Quo::Query + def self.relation_backed_query_base_class: () -> Quo::RelationBackedQuery + + def self.collection_backed_query_base_class: () -> Quo::CollectionBackedQuery end diff --git a/sig/generated/quo/collection_backed_query.rbs b/sig/generated/quo/collection_backed_query.rbs index a4670fb..b933413 100644 --- a/sig/generated/quo/collection_backed_query.rbs +++ b/sig/generated/quo/collection_backed_query.rbs @@ -1,8 +1,7 @@ # Generated from lib/quo/collection_backed_query.rb with RBS::Inline module Quo - # @rbs inherits Quo::Query - class CollectionBackedQuery < Quo::Query + class CollectionBackedQuery < Query # Wrap an enumerable collection or a block that returns an enumerable collection # @rbs data: untyped, props: Symbol => untyped, block: () -> untyped # @rbs return: Quo::CollectionBackedQuery diff --git a/sig/generated/quo/query.rbs b/sig/generated/quo/query.rbs index 9cc40b0..d6f55b1 100644 --- a/sig/generated/quo/query.rbs +++ b/sig/generated/quo/query.rbs @@ -12,6 +12,7 @@ module Quo def to_s: () -> untyped + # TODO: put this in a module with the composer and merge_instances methods # Compose is aliased as `+`. Can optionally take `joins` parameters to add joins on merged relation. # @rbs right: Quo::Query | ActiveRecord::Relation | Object & Enumerable[untyped] # @rbs joins: Symbol | Hash[Symbol, untyped] | Array[Symbol | Hash[Symbol, untyped]] diff --git a/sig/generated/quo/relation_backed_query.rbs b/sig/generated/quo/relation_backed_query.rbs index b18472f..5c13fe1 100644 --- a/sig/generated/quo/relation_backed_query.rbs +++ b/sig/generated/quo/relation_backed_query.rbs @@ -1,8 +1,7 @@ # Generated from lib/quo/relation_backed_query.rb with RBS::Inline module Quo - # @rbs inherits Quo::Query - class RelationBackedQuery < Quo::Query + class RelationBackedQuery < Query # @rbs query: ActiveRecord::Relation | Quo::Query # @rbs props: Hash[Symbol, untyped] # @rbs &block: () -> ActiveRecord::Relation | Quo::Query | Object & Enumerable[untyped] diff --git a/test/dummy/app/queries/application_collection_query.rb b/test/dummy/app/queries/application_collection_query.rb new file mode 100644 index 0000000..3a8ed2a --- /dev/null +++ b/test/dummy/app/queries/application_collection_query.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# This is configured as the base query class for the applications queries. See the Quo initializer +class ApplicationCollectionQuery < Quo::CollectionBackedQuery + def hello + "collection" + end +end diff --git a/test/dummy/app/queries/application_query.rb b/test/dummy/app/queries/application_relation_query.rb similarity index 67% rename from test/dummy/app/queries/application_query.rb rename to test/dummy/app/queries/application_relation_query.rb index 2c7af79..b60f59a 100644 --- a/test/dummy/app/queries/application_query.rb +++ b/test/dummy/app/queries/application_relation_query.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true # This is configured as the base query class for the applications queries. See the Quo initializer -class ApplicationQuery < Quo::Query +class ApplicationRelationQuery < Quo::RelationBackedQuery def hello - "world" + "relation" end end diff --git a/test/dummy/config/initializers/quo.rb b/test/dummy/config/initializers/quo.rb index 6efcbf6..39a440a 100644 --- a/test/dummy/config/initializers/quo.rb +++ b/test/dummy/config/initializers/quo.rb @@ -1 +1,2 @@ -Quo.base_query_class = "ApplicationQuery" +Quo.relation_backed_query_base_class = "ApplicationRelationQuery" +Quo.collection_backed_query_base_class = "ApplicationCollectionQuery" diff --git a/test/quo/composed_query_test.rb b/test/quo/composed_query_test.rb index e3d629e..80e4663 100644 --- a/test/quo/composed_query_test.rb +++ b/test/quo/composed_query_test.rb @@ -32,8 +32,8 @@ def setup test "merged result is a Quo Query and inherits from configured base class" do klass = Quo::RelationBackedQuery.wrap(::Comment.recent).compose(::Comment.not_spam) - assert_equal Quo::RelationBackedQuery, klass.superclass - assert_equal "world", klass.new.hello + assert_equal ApplicationRelationQuery, klass.superclass + assert_equal "relation", klass.new.hello end test "merges two Quo::Query objects" do @@ -72,6 +72,7 @@ def setup left = Quo::CollectionBackedQuery.wrap([1, 2, 3]) right = Quo::CollectionBackedQuery.wrap([4, 5, 6]) composed = left + right + assert_equal "collection", composed.new.hello assert_equal [1, 2, 3, 4, 5, 6], composed.new.results.to_a end @@ -130,7 +131,7 @@ def setup test "#inspect when 1 source is a query object subclass" do merged = CommentNotSpamQuery.compose(Quo::CollectionBackedQuery) - assert_equal "Quo::RelationBackedQuery[CommentNotSpamQuery, Quo::CollectionBackedQuery]", merged.inspect + assert_equal "ApplicationRelationQuery[CommentNotSpamQuery, Quo::CollectionBackedQuery]", merged.inspect end test "#inspect when 2 collection sources are provided" do @@ -142,7 +143,7 @@ def setup test "#inspect when 1 source is a merged query" do nested = CommentNotSpamQuery.compose(UnreadCommentsQuery) merged = nested.compose(Quo::CollectionBackedQuery) - assert_equal "Quo::RelationBackedQuery[Quo::RelationBackedQuery[CommentNotSpamQuery, UnreadCommentsQuery], Quo::CollectionBackedQuery]", merged.inspect + assert_equal "ApplicationRelationQuery[ApplicationRelationQuery[CommentNotSpamQuery, UnreadCommentsQuery], Quo::CollectionBackedQuery]", merged.inspect end test "#copy makes a copy of this query object with different options" do diff --git a/test/quo/custom_base_class_test.rb b/test/quo/custom_base_class_test.rb index 9a64e71..40d2b09 100644 --- a/test/quo/custom_base_class_test.rb +++ b/test/quo/custom_base_class_test.rb @@ -11,7 +11,7 @@ def setup Comment.create!(post: p1, body: "abc", read: false) Comment.create!(post: p2, body: "def", read: false, spam_score: 0.8) - @q1 = Quo::RelationBackedQuery.wrap(props: {since_date: Time}) do + @q1 = ApplicationRelationQuery.wrap(props: {since_date: Time}) do Comment.recent(since_date) end @q2 = Quo::RelationBackedQuery.wrap(props: {spam_score: Float}) do @@ -20,10 +20,10 @@ def setup end test "wrapped query inherits from custom base class" do - assert_kind_of ApplicationQuery, @q1.new(since_date: 1.day.ago) - assert_equal "world", @q1.new(since_date: 1.day.ago).hello + assert_kind_of ApplicationRelationQuery, @q1.new(since_date: 1.day.ago) + assert_equal "relation", @q1.new(since_date: 1.day.ago).hello klass = Quo::RelationBackedQuery.wrap(Comment.recent).compose(Comment.not_spam) - assert_kind_of ApplicationQuery, klass.new + assert_kind_of ApplicationRelationQuery, klass.new end end