lib/torque/postgresql/auxiliary_statement/settings.rb in torque-postgresql-2.3.0 vs lib/torque/postgresql/auxiliary_statement/settings.rb in torque-postgresql-2.4.0
- old
+ new
@@ -2,33 +2,67 @@
module Torque
module PostgreSQL
class AuxiliaryStatement
class Settings < Collector.new(:attributes, :join, :join_type, :query, :requires,
- :polymorphic, :through)
+ :polymorphic, :through, :union_all, :connect)
- attr_reader :base, :source
+ attr_reader :base, :source, :depth, :path
alias_method :select, :attributes
alias_method :cte, :source
delegate :relation_query?, to: Torque::PostgreSQL::AuxiliaryStatement
delegate :table, :table_name, to: :@source
delegate :sql, to: ::Arel
- def initialize(base, source)
+ def initialize(base, source, recursive = false)
@base = base
@source = source
+ @recursive = recursive
end
def base_name
@base.name
end
def base_table
@base.arel_table
end
+
+ def recursive?
+ @recursive
+ end
+
+ def depth?
+ defined?(@depth)
+ end
+
+ def path?
+ defined?(@path)
+ end
+
+ # Add an attribute to the result showing the depth of each iteration
+ def with_depth(name = 'depth', start: 0, as: nil)
+ @depth = [name.to_s, start, as&.to_s] if recursive?
+ end
+
+ # Add an attribute to the result showing the path of each record
+ def with_path(name = 'path', source: nil, as: nil)
+ @path = [name.to_s, source&.to_s, as&.to_s] if recursive?
+ end
+
+ # Set recursive operation to use union all
+ def union_all!
+ @union_all = true if recursive?
+ end
+
+ # Add both depth and path to the result
+ def with_depth_and_path
+ with_depth && with_path
+ end
+
# Get the arel version of the table set on the query
def query_table
raise StandardError, 'The query is not defined yet' if query.nil?
return query.arel_table if relation_query?(query)
@query_table
@@ -39,38 +73,57 @@
query_table[name.to_s]
end
alias column col
- # There are two ways of setting the query:
+ # There are three ways of setting the query:
# - A simple relation based on a Model
# - A Arel-based select manager
- # - A string or a proc that requires the table name as first argument
+ # - A string or a proc
def query(value = nil, command = nil)
return @query if value.nil?
- return @query = value if relation_query?(value)
- if value.is_a?(::Arel::SelectManager)
- @query = value
- @query_table = value.source.left.name
- return
- end
+ @query = sanitize_query(value, command)
+ end
- valid_type = command.respond_to?(:call) || command.is_a?(String)
+ # Same as query, but for the second part of the union for recursive cte
+ def sub_query(value = nil, command = nil)
+ return unless recursive?
+ return @sub_query if value.nil?
- raise ArgumentError, <<-MSG.squish if command.nil?
- To use proc or string as query, you need to provide the table name
- as the first argument
- MSG
+ @sub_query = sanitize_query(value, command)
+ end
- raise ArgumentError, <<-MSG.squish unless valid_type
- Only relation, string and proc are valid object types for query,
- #{command.inspect} given.
- MSG
+ # Assume `parent_` as the other part if provided a Symbol or String
+ def connect(value = nil)
+ return @connect if value.nil?
- @query = command
- @query_table = ::Arel::Table.new(value)
+ value = [value.to_sym, :"parent_#{value}"] \
+ if value.is_a?(String) || value.is_a?(Symbol)
+ value = value.to_a.first if value.is_a?(Hash)
+
+ @connect = value
end
+
+ alias connect= connect
+
+ private
+
+ # Get the query and table from the params
+ def sanitize_query(value, command = nil)
+ return value if relation_query?(value)
+ return value if value.is_a?(::Arel::SelectManager)
+
+ command = value if command.nil? # For compatibility purposes
+ valid_type = command.respond_to?(:call) || command.is_a?(String)
+
+ raise ArgumentError, <<-MSG.squish unless valid_type
+ Only relation, string and proc are valid object types for query,
+ #{command.inspect} given.
+ MSG
+
+ command
+ end
end
end
end
end