From 75c669187248d0a92f1cba09fb2f9784c80c5510 Mon Sep 17 00:00:00 2001 From: Bruno Vezoli Date: Thu, 15 Apr 2021 23:24:02 -0300 Subject: [PATCH] [1] Wharel: exposed arel, block with argument --- activerecord/lib/active_record/querying.rb | 2 +- .../active_record/relation/query_composer.rb | 31 +++++++++++++++++++ .../active_record/relation/query_methods.rb | 6 ++++ activerecord/test/cases/relations_test.rb | 16 ++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 activerecord/lib/active_record/relation/query_composer.rb diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb index ceded67d57b2d..a7c894ea22f10 100644 --- a/activerecord/lib/active_record/querying.rb +++ b/activerecord/lib/active_record/querying.rb @@ -13,7 +13,7 @@ module Querying :destroy_all, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by, :find_each, :find_in_batches, :in_batches, :select, :reselect, :order, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins, - :where, :rewhere, :invert_where, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly, + :where, :wharel, :rewhere, :invert_where, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly, :and, :or, :annotate, :optimizer_hints, :extending, :having, :create_with, :distinct, :references, :none, :unscope, :merge, :except, :only, :count, :average, :minimum, :maximum, :sum, :calculate, diff --git a/activerecord/lib/active_record/relation/query_composer.rb b/activerecord/lib/active_record/relation/query_composer.rb new file mode 100644 index 0000000000000..422099222fe89 --- /dev/null +++ b/activerecord/lib/active_record/relation/query_composer.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module ActiveRecord + class QueryComposer + def initialize(model) + @model = model + @arel_table = model.arel_table + @reflections = model._reflections + define_attribute_accessors + end + + def method_missing(name, *_args) + if reflections.key?(name.to_s) + self.class.new(reflections[name.to_s].klass) + else + super + end + end + + private + attr_reader :model, :arel_table, :reflections + + def define_attribute_accessors + model.attribute_names.each do |attr| + define_singleton_method attr do + arel_table[attr] + end + end + end + end +end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index c9da47482493a..ab6cf85fb37be 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -2,6 +2,7 @@ require "active_record/relation/from_clause" require "active_record/relation/query_attribute" +require "active_record/relation/query_composer" require "active_record/relation/where_clause" require "active_model/forbidden_attributes_protection" require "active_support/core_ext/array/wrap" @@ -671,6 +672,11 @@ def where!(opts, *rest) # :nodoc: self end + def wharel(&block) + conditions = yield QueryComposer.new(self) + where(conditions) + end + # Allows you to change a previously set where condition for a given attribute, instead of appending to that condition. # # Post.where(trashed: true).where(trashed: false) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index ef3338c0f8804..0c1aec5acd1fc 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -63,6 +63,16 @@ def test_multivalue_where assert_equal 1, posts.to_a.size end + def test_wharel + posts = Post.wharel { |post| post.author_id.not_eq(1).and(post.id.gt(2)) } + assert_equal 6, posts.to_a.size + end + + def test_wharel_can_chain + posts = Post.wharel { |post| post.author_id.gteq(1) }.wharel { |post| post.id.lteq(4) } + assert_equal 3, posts.to_a.size + end + def test_scoped topics = Topic.all assert_kind_of ActiveRecord::Relation, topics @@ -514,6 +524,12 @@ def test_finding_with_hash_conditions_on_joined_table assert_equal companies(:rails_core), firms.first end + def tests_finding_with_block_on_joined_table + firms = DependentFirm.joins(:account).wharel { |firm| firm.name.eq("RailsCore").and(firm.accounts.credit_limit.in(55..60)) }.to_a + assert_equal 1, firms.size + assert_equal companies(:rails_core), firms.first + end + def test_find_all_with_join developers_on_project_one = Developer.joins("LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id"). where("project_id=1").to_a