Class: LcpRuby::VirtualColumns::Builder
- Inherits:
-
Object
- Object
- LcpRuby::VirtualColumns::Builder
- Defined in:
- lib/lcp_ruby/virtual_columns/builder.rb
Class Method Summary collapse
-
.apply(scope, model_definition, vc_names, current_user: nil) ⇒ Array(ActiveRecord::Relation, Array<String>, Boolean)
Inject virtual column subqueries/expressions into a scope.
Class Method Details
.apply(scope, model_definition, vc_names, current_user: nil) ⇒ Array(ActiveRecord::Relation, Array<String>, Boolean)
Inject virtual column subqueries/expressions into a scope.
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/lcp_ruby/virtual_columns/builder.rb', line 11 def self.apply(scope, model_definition, vc_names, current_user: nil) return [ scope, [], false ] if vc_names.empty? conn = ActiveRecord::Base.connection parent_table = conn.quote_table_name(model_definition.table_name) subqueries = [] service_only = [] joins_to_apply = [] needs_group_by = false vc_names.each do |vc_name| vc_def = model_definition.virtual_column(vc_name) next unless vc_def if vc_def.join.present? joins_to_apply << vc_def.join end needs_group_by = true if vc_def.group sql = build_subquery(vc_def, model_definition, parent_table, conn, current_user: current_user) if sql subqueries << "#{sql} AS #{conn.quote_column_name(vc_name)}" elsif vc_def.service_type? service_only << vc_name end end applied_vc_names = vc_names - service_only if subqueries.any? scope = scope.select("#{parent_table}.*", *subqueries) end # Apply deduplicated JOINs if joins_to_apply.any? seen = Set.new joins_to_apply.each do |join_sql| normalized = join_sql.gsub("%{table}", parent_table).gsub(/\s+/, " ").strip dedup_key = normalized.downcase next if seen.include?(dedup_key) seen << dedup_key scope = scope.joins(Arel.sql(normalized)) end end # Apply GROUP BY if any virtual column requires it if needs_group_by scope = scope.group("#{parent_table}.#{conn.quote_column_name('id')}") end # Wrap scope with extending module for loaded-tracking guard. # Pushes VC names onto thread-local stack around exec_queries so # after_initialize callbacks can capture which VCs were loaded. if applied_vc_names.any? && scope.klass.respond_to?(:_virtual_columns_stack) vc_name_set = Set.new(applied_vc_names.map(&:to_s)).freeze scope = scope.extending(Module.new do define_method(:exec_queries) do klass._virtual_columns_stack ||= [] klass._virtual_columns_stack.push(vc_name_set) begin super() ensure klass._virtual_columns_stack.pop end end end) end [ scope, service_only, needs_group_by ] end |