class ActiveRecord::Associations::HasAndBelongsToManyAssociation
:nodoc:
def columns
def columns @reflection.columns(@reflection.options[:join_table], "#{@reflection.options[:join_table]} Columns") end
def construct_find_options!(options)
def construct_find_options!(options) options[:joins] = @join_sql options[:readonly] = finding_with_ambiguous_select?(options[:select] || @reflection.options[:select]) options[:select] ||= (@reflection.options[:select] || '*') end
def construct_scope
def construct_scope { :find => { :conditions => @finder_sql, :joins => @join_sql, :readonly => false, :order => @reflection.options[:order], :include => @reflection.options[:include], :limit => @reflection.options[:limit] } } end
def construct_sql
def construct_sql if @reflection.options[:finder_sql] @finder_sql = interpolate_sql(@reflection.options[:finder_sql]) else @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{owner_quoted_id} " @finder_sql << " AND (#{conditions})" if conditions end @join_sql = "INNER JOIN #{@owner.connection.quote_table_name @reflection.options[:join_table]} ON #{@reflection.quoted_table_name}.#{@reflection.klass.primary_key} = #{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.association_foreign_key}" if @reflection.options[:counter_sql] @counter_sql = interpolate_sql(@reflection.options[:counter_sql]) elsif @reflection.options[:finder_sql] # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */ @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" } @counter_sql = interpolate_sql(@reflection.options[:counter_sql]) else @counter_sql = @finder_sql end end
def count_records
def count_records load_target.size end
def create(attributes = {})
def create(attributes = {}) create_record(attributes) { |record| insert_record(record) } end
def create!(attributes = {})
def create!(attributes = {}) create_record(attributes) { |record| insert_record(record, true) } end
def create_record(attributes, &block)
def create_record(attributes, &block) # Can't use Base.create because the foreign key may be a protected attribute. ensure_owner_is_not_new if attributes.is_a?(Array) attributes.collect { |attr| create(attr) } else build_record(attributes, &block) end end
def delete_records(records)
def delete_records(records) if sql = @reflection.options[:delete_sql] records.each { |record| @owner.connection.delete(interpolate_sql(sql, record)) } else ids = quoted_record_ids(records) sql = "DELETE FROM #{@owner.connection.quote_table_name @reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{owner_quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})" @owner.connection.delete(sql) end end
def finding_with_ambiguous_select?(select_clause)
clause has been explicitly defined. Otherwise you can get broken records back, if, for example, the join column also has
Join tables with additional columns on top of the two foreign keys must be considered ambiguous unless a select
def finding_with_ambiguous_select?(select_clause) !select_clause && columns.size != 2 end
def has_primary_key?
def has_primary_key? return @has_primary_key unless @has_primary_key.nil? @has_primary_key = (@owner.connection.supports_primary_key? && @owner.connection.primary_key(@reflection.options[:join_table])) end
def initialize(owner, reflection)
def initialize(owner, reflection) super @primary_key_list = {} end
def insert_record(record, force = true, validate = true)
def insert_record(record, force = true, validate = true) if has_primary_key? raise ActiveRecord::ConfigurationError, "Primary key is not allowed in a has_and_belongs_to_many join table (#{@reflection.options[:join_table]})." end if record.new_record? if force record.save! else return false unless record.save(validate) end end if @reflection.options[:insert_sql] @owner.connection.insert(interpolate_sql(@reflection.options[:insert_sql], record)) else attributes = columns.inject({}) do |attrs, column| case column.name.to_s when @reflection.primary_key_name.to_s attrs[column.name] = owner_quoted_id when @reflection.association_foreign_key.to_s attrs[column.name] = record.quoted_id else if record.has_attribute?(column.name) value = @owner.send(:quote_value, record[column.name], column) attrs[column.name] = value unless value.nil? end end attrs end sql = "INSERT INTO #{@owner.connection.quote_table_name @reflection.options[:join_table]} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " + "VALUES (#{attributes.values.join(', ')})" @owner.connection.insert(sql) end return true end
def reset_column_information
def reset_column_information @reflection.reset_column_information end