From f33a768fa982c3189983259a822c40b3ba919901 Mon Sep 17 00:00:00 2001 From: Nick LaMuro Date: Wed, 23 May 2018 17:37:52 -0500 Subject: [PATCH] Add has_one scope support to virtual_delegate This enhances virtual_delegates so that scopes added as part of a `has_one` relation are respected. Because of the changes, an implicit `limit = 1` was necessary to support the `has_one`, and the easiest way to accomplish that while still keeping `select_from_alias` in a similar form was to allow passing a block so the arel (prior to being converted to a string) could be updated. Also of note, `select_from_alias` also had a few changes to it beyond that: - The "query" is now built up from the scope on the model, not a raw arel_table from the to_ref model. - Instead of a `project`, we call a `.select` on the "query" (scope from the relation), which already has a similar arg structure, but allows us to remove the select_values properly before converting it to arel (happens right after the `.select`). We work with raw `Arel` after that. - The `table_alias` functionality is now done through the `from` clause, `from(to_table)` allows us to still keep the same alias functionality we had. Under the covers, this does the same thing, it is just previously the starting point was an `Arel::Table`, and now it is a `Arel::SelectManager`. The `Arel::Table` version ends up returning a `SelectManager` anyway, so what we are doing is functionally equivalent. --- lib/extensions/ar_virtual.rb | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/extensions/ar_virtual.rb b/lib/extensions/ar_virtual.rb index 60254a098cb..48df80c8a34 100644 --- a/lib/extensions/ar_virtual.rb +++ b/lib/extensions/ar_virtual.rb @@ -198,7 +198,9 @@ def virtual_delegate_arel(col, to_ref) if to_ref.macro == :has_one lambda do |t| src_model_id = arel_attribute(to_ref.association_primary_key, t) - VirtualDelegates.select_from_alias(to_ref, col, to_ref.foreign_key, src_model_id) + VirtualDelegates.select_from_alias(to_ref, col, to_ref.foreign_key, src_model_id) do |arel| + arel.limit = 1 + end end elsif to_ref.macro == :belongs_to lambda do |t| @@ -287,10 +289,22 @@ def virtual_delegate_arel(col, to_ref) # def self.select_from_alias(to_ref, col, to_model_col_name, src_model_id) - to_table = select_from_alias_table(to_ref.klass, src_model_id.relation) + query = if to_ref.scope + to_ref.klass.instance_exec(nil, &to_ref.scope) + else + to_ref.klass.all + end + + to_table = select_from_alias_table(to_ref.klass, src_model_id.relation) to_model_id = to_ref.klass.arel_attribute(to_model_col_name, to_table) - to_column = to_ref.klass.arel_attribute(col, to_table) - Arel.sql("(#{to_table.project(to_column).where(to_model_id.eq(src_model_id)).to_sql})") + to_column = to_ref.klass.arel_attribute(col, to_table) + arel = query.select(to_column).arel + .from(to_table) + .where(to_model_id.eq(src_model_id)) + + yield arel if block_given? + + Arel.sql("(#{arel.to_sql})") end # determine table reference to use for a sub query