module WhereExists
Constants
- VERSION
Public Instance Methods
where_exists(association_name, *where_parameters, &block)
click to toggle source
# File lib/where_exists.rb, line 4 def where_exists(association_name, *where_parameters, &block) where_exists_or_not_exists(true, association_name, where_parameters, &block) end
where_not_exists(association_name, *where_parameters, &block)
click to toggle source
# File lib/where_exists.rb, line 8 def where_not_exists(association_name, *where_parameters, &block) where_exists_or_not_exists(false, association_name, where_parameters, &block) end
Protected Instance Methods
build_exists_string(association_name, *where_parameters) { |query| ... }
click to toggle source
# File lib/where_exists.rb, line 30 def build_exists_string(association_name, *where_parameters, &block) association = self.reflect_on_association(association_name) unless association raise ArgumentError.new("where_exists: association - #{association_name} - #{association_name.inspect} not found on #{self.name}") end case association.macro when :belongs_to queries = where_exists_for_belongs_to_query(association, where_parameters) when :has_many, :has_one queries = where_exists_for_has_many_query(association, where_parameters) when :has_and_belongs_to_many queries = where_exists_for_habtm_query(association, where_parameters) else inspection = nil begin inspection = association.macros.inspect rescue inspection = association.macro end raise ArgumentError.new("where_exists: not supported association – #{inspection}") end queries_sql = queries.map do |query| query = yield query if block_given? "EXISTS (" + query.to_sql + ")" end queries_sql.join(" OR ") end
loop_nested_association(query, next_association = {}, nested = false)
click to toggle source
# File lib/where_exists.rb, line 199 def loop_nested_association(query, next_association = {}, nested = false) str = query.klass.build_exists_string( next_association[:association].name, *[ *next_association[:params] ], ) if next_association[:next_association] && next_association[:next_association][:association] subq = str.match(/\([^\(\)]+\)/mi)[0] str.sub!(subq) do "(#{subq} AND (#{loop_nested_association( next_association[:association], next_association[:next_association], true )}))" end end nested ? str : [query.where(str)] end
quote_table_and_column_name(table_name, column_name)
click to toggle source
# File lib/where_exists.rb, line 221 def quote_table_and_column_name(table_name, column_name) connection.quote_table_name(table_name) + '.' + connection.quote_column_name(column_name) end
where_exists_for_belongs_to_query(association, where_parameters)
click to toggle source
# File lib/where_exists.rb, line 62 def where_exists_for_belongs_to_query(association, where_parameters) polymorphic = association.options[:polymorphic].present? association_scope = association.scope if polymorphic associated_models = self.select("DISTINCT #{connection.quote_column_name(association.foreign_type)}"). where("#{connection.quote_column_name(association.foreign_type)} IS NOT NULL").pluck(association.foreign_type). uniq.map(&:classify).map(&:constantize) else associated_models = [association.klass] end queries = [] self_ids = quote_table_and_column_name(self.table_name, association.foreign_key) self_type = quote_table_and_column_name(self.table_name, association.foreign_type) associated_models.each do |associated_model| primary_key = association.options[:primary_key] || associated_model.primary_key other_ids = quote_table_and_column_name(associated_model.table_name, primary_key) query = associated_model.select("1").where("#{self_ids} = #{other_ids}") if where_parameters != [] query = query.where(*where_parameters) end if association_scope query = query.instance_exec(&association_scope) end if polymorphic other_types = [associated_model.name, associated_model.table_name] other_types << associated_model.polymorphic_name if associated_model.respond_to?(:polymorphic_name) query = query.where("#{self_type} IN (?)", other_types.uniq) end queries.push query end queries end
where_exists_for_habtm_query(association, where_parameters, next_association = {})
click to toggle source
# File lib/where_exists.rb, line 161 def where_exists_for_habtm_query(association, where_parameters, next_association = {}) association_scope = association.scope associated_model = association.klass primary_key = association.options[:primary_key] || self.primary_key self_ids = quote_table_and_column_name(self.table_name, primary_key) join_ids = quote_table_and_column_name(association.join_table, association.foreign_key) associated_join_ids = quote_table_and_column_name(association.join_table, association.association_foreign_key) associated_ids = quote_table_and_column_name(associated_model.table_name, associated_model.primary_key) result = associated_model. select("1"). joins( <<-SQL INNER JOIN #{connection.quote_table_name(association.join_table)} ON #{associated_ids} = #{associated_join_ids} SQL ). where("#{join_ids} = #{self_ids}") if next_association[:association] return loop_nested_association(result, next_association) end if where_parameters != [] result = result.where(*where_parameters) end if association_scope result = result.instance_exec(&association_scope) end [result] end
where_exists_for_has_many_query(association, where_parameters, next_association = {})
click to toggle source
# File lib/where_exists.rb, line 102 def where_exists_for_has_many_query(association, where_parameters, next_association = {}) if association.through_reflection raise ArgumentError.new(association) unless association.source_reflection next_association = { association: association.source_reflection, params: where_parameters, next_association: next_association } association = association.through_reflection case association.macro when :has_many, :has_one return where_exists_for_has_many_query(association, {}, next_association) when :has_and_belongs_to_many return where_exists_for_habtm_query(association, {}, next_association) else inspection = nil begin inspection = association.macros.inspect rescue inspection = association.macro end raise ArgumentError.new("where_exists: not supported association – #{inspection}") end end association_scope = next_association[:scope] || association.scope associated_model = association.klass primary_key = association.options[:primary_key] || self.primary_key self_ids = quote_table_and_column_name(self.table_name, primary_key) associated_ids = quote_table_and_column_name(associated_model.table_name, association.foreign_key) result = associated_model.select("1").where("#{associated_ids} = #{self_ids}") if association.options[:as] other_types = quote_table_and_column_name(associated_model.table_name, association.type) class_values = [self.name, self.table_name] class_values << self.polymorphic_name if associated_model.respond_to?(:polymorphic_name) result = result.where("#{other_types} IN (?)", class_values.uniq) end if association_scope result = result.instance_exec(&association_scope) end if next_association[:association] return loop_nested_association(result, next_association) end if where_parameters != [] result = result.where(*where_parameters) end [result] end
where_exists_or_not_exists(does_exist, association_name, where_parameters, &block)
click to toggle source
# File lib/where_exists.rb, line 14 def where_exists_or_not_exists(does_exist, association_name, where_parameters, &block) queries_sql = build_exists_string(association_name, *where_parameters, &block) if does_exist not_string = "" else not_string = "NOT " end if queries_sql.empty? does_exist ? self.none : self.all else self.where("#{not_string}(#{queries_sql})") end end