Add version 2.2.2 of composite_primary_keys.
This commit is contained in:
parent
9156448ad6
commit
a69f380fa5
147 changed files with 6140 additions and 0 deletions
BIN
vendor/gems/composite_primary_keys-2.2.2/lib/._composite_primary_keys.rb
vendored
Normal file
BIN
vendor/gems/composite_primary_keys-2.2.2/lib/._composite_primary_keys.rb
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/composite_primary_keys-2.2.2/lib/adapter_helper/._base.rb
vendored
Normal file
BIN
vendor/gems/composite_primary_keys-2.2.2/lib/adapter_helper/._base.rb
vendored
Normal file
Binary file not shown.
63
vendor/gems/composite_primary_keys-2.2.2/lib/adapter_helper/base.rb
vendored
Normal file
63
vendor/gems/composite_primary_keys-2.2.2/lib/adapter_helper/base.rb
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
module AdapterHelper
|
||||
class Base
|
||||
class << self
|
||||
attr_accessor :adapter
|
||||
|
||||
def load_connection_from_env(adapter)
|
||||
self.adapter = adapter
|
||||
unless ENV['cpk_adapters']
|
||||
puts error_msg_setup_helper
|
||||
exit
|
||||
end
|
||||
|
||||
ActiveRecord::Base.configurations = YAML.load(ENV['cpk_adapters'])
|
||||
unless spec = ActiveRecord::Base.configurations[adapter]
|
||||
puts error_msg_adapter_helper
|
||||
exit
|
||||
end
|
||||
spec[:adapter] = adapter
|
||||
spec
|
||||
end
|
||||
|
||||
def error_msg_setup_helper
|
||||
<<-EOS
|
||||
Setup Helper:
|
||||
CPK now has a place for your individual testing configuration.
|
||||
That is, instead of hardcoding it in the Rakefile and test/connections files,
|
||||
there is now a local/database_connections.rb file that is NOT in the
|
||||
repository. Your personal DB information (username, password etc) can
|
||||
be stored here without making it difficult to submit patches etc.
|
||||
|
||||
Installation:
|
||||
i) cp locals/database_connections.rb.sample locals/database_connections.rb
|
||||
ii) For #{adapter} connection details see "Adapter Setup Helper" below.
|
||||
iii) Rerun this task
|
||||
|
||||
#{error_msg_adapter_helper}
|
||||
|
||||
Current ENV:
|
||||
#{ENV.inspect}
|
||||
EOS
|
||||
end
|
||||
|
||||
def error_msg_adapter_helper
|
||||
<<-EOS
|
||||
Adapter Setup Helper:
|
||||
To run #{adapter} tests, you need to setup your #{adapter} connections.
|
||||
In your local/database_connections.rb file, within the ENV['cpk_adapter'] hash, add:
|
||||
"#{adapter}" => { adapter settings }
|
||||
|
||||
That is, it will look like:
|
||||
ENV['cpk_adapters'] = {
|
||||
"#{adapter}" => {
|
||||
:adapter => "#{adapter}",
|
||||
:username => "root",
|
||||
:password => "root",
|
||||
# ...
|
||||
}
|
||||
}.to_yaml
|
||||
EOS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
13
vendor/gems/composite_primary_keys-2.2.2/lib/adapter_helper/mysql.rb
vendored
Normal file
13
vendor/gems/composite_primary_keys-2.2.2/lib/adapter_helper/mysql.rb
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
require File.join(File.dirname(__FILE__), 'base')
|
||||
|
||||
module AdapterHelper
|
||||
class MySQL < Base
|
||||
class << self
|
||||
def load_connection_from_env
|
||||
spec = super('mysql')
|
||||
spec[:database] ||= 'composite_primary_keys_unittest'
|
||||
spec
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
12
vendor/gems/composite_primary_keys-2.2.2/lib/adapter_helper/oracle.rb
vendored
Normal file
12
vendor/gems/composite_primary_keys-2.2.2/lib/adapter_helper/oracle.rb
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
require File.join(File.dirname(__FILE__), 'base')
|
||||
|
||||
module AdapterHelper
|
||||
class Oracle < Base
|
||||
class << self
|
||||
def load_connection_from_env
|
||||
spec = super('oracle')
|
||||
spec
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
13
vendor/gems/composite_primary_keys-2.2.2/lib/adapter_helper/postgresql.rb
vendored
Normal file
13
vendor/gems/composite_primary_keys-2.2.2/lib/adapter_helper/postgresql.rb
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
require File.join(File.dirname(__FILE__), 'base')
|
||||
|
||||
module AdapterHelper
|
||||
class Postgresql < Base
|
||||
class << self
|
||||
def load_connection_from_env
|
||||
spec = super('postgresql')
|
||||
spec[:database] ||= 'composite_primary_keys_unittest'
|
||||
spec
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
13
vendor/gems/composite_primary_keys-2.2.2/lib/adapter_helper/sqlite3.rb
vendored
Normal file
13
vendor/gems/composite_primary_keys-2.2.2/lib/adapter_helper/sqlite3.rb
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
require File.join(File.dirname(__FILE__), 'base')
|
||||
|
||||
module AdapterHelper
|
||||
class Sqlite3 < Base
|
||||
class << self
|
||||
def load_connection_from_env
|
||||
spec = super('sqlite3')
|
||||
spec[:dbfile] ||= "tmp/test.db"
|
||||
spec
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
55
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys.rb
vendored
Normal file
55
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys.rb
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
#--
|
||||
# Copyright (c) 2006 Nic Williams
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#++
|
||||
|
||||
$:.unshift(File.dirname(__FILE__)) unless
|
||||
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
||||
|
||||
unless defined?(ActiveRecord)
|
||||
begin
|
||||
require 'active_record'
|
||||
rescue LoadError
|
||||
require 'rubygems'
|
||||
require_gem 'activerecord'
|
||||
end
|
||||
end
|
||||
|
||||
require 'composite_primary_keys/fixtures'
|
||||
require 'composite_primary_keys/composite_arrays'
|
||||
require 'composite_primary_keys/associations'
|
||||
require 'composite_primary_keys/association_preload'
|
||||
require 'composite_primary_keys/reflection'
|
||||
require 'composite_primary_keys/base'
|
||||
require 'composite_primary_keys/calculations'
|
||||
require 'composite_primary_keys/migration'
|
||||
require 'composite_primary_keys/attribute_methods'
|
||||
|
||||
ActiveRecord::Base.class_eval do
|
||||
include CompositePrimaryKeys::ActiveRecord::Base
|
||||
end
|
||||
|
||||
Dir[File.dirname(__FILE__) + '/composite_primary_keys/connection_adapters/*.rb'].each do |adapter|
|
||||
begin
|
||||
require adapter.gsub('.rb','')
|
||||
rescue MissingSourceFile
|
||||
end
|
||||
end
|
BIN
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/._associations.rb
vendored
Normal file
BIN
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/._associations.rb
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/._base.rb
vendored
Normal file
BIN
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/._base.rb
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/._version.rb
vendored
Normal file
BIN
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/._version.rb
vendored
Normal file
Binary file not shown.
253
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/association_preload.rb
vendored
Normal file
253
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/association_preload.rb
vendored
Normal file
|
@ -0,0 +1,253 @@
|
|||
module CompositePrimaryKeys
|
||||
module ActiveRecord
|
||||
module AssociationPreload
|
||||
def self.append_features(base)
|
||||
super
|
||||
base.send(:extend, ClassMethods)
|
||||
end
|
||||
|
||||
# Composite key versions of Association functions
|
||||
module ClassMethods
|
||||
def preload_has_and_belongs_to_many_association(records, reflection, preload_options={})
|
||||
table_name = reflection.klass.quoted_table_name
|
||||
id_to_record_map, ids = construct_id_map_for_composite(records)
|
||||
records.each {|record| record.send(reflection.name).loaded}
|
||||
options = reflection.options
|
||||
|
||||
if composite?
|
||||
primary_key = reflection.primary_key_name.to_s.split(CompositePrimaryKeys::ID_SEP)
|
||||
where = (primary_key * ids.size).in_groups_of(primary_key.size).map do |keys|
|
||||
"(" + keys.map{|key| "t0.#{connection.quote_column_name(key)} = ?"}.join(" AND ") + ")"
|
||||
end.join(" OR ")
|
||||
|
||||
conditions = [where, ids].flatten
|
||||
joins = "INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{full_composite_join_clause(reflection, reflection.klass.table_name, reflection.klass.primary_key, 't0', reflection.association_foreign_key)}"
|
||||
parent_primary_keys = reflection.primary_key_name.to_s.split(CompositePrimaryKeys::ID_SEP).map{|k| "t0.#{connection.quote_column_name(k)}"}
|
||||
parent_record_id = connection.concat(*parent_primary_keys.zip(["','"] * (parent_primary_keys.size - 1)).flatten.compact)
|
||||
else
|
||||
conditions = ["t0.#{connection.quote_column_name(reflection.primary_key_name)} IN (?)", ids]
|
||||
joins = "INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{reflection.klass.quoted_table_name}.#{connection.quote_column_name(reflection.klass.primary_key)} = t0.#{connection.quote_column_name(reflection.association_foreign_key)})"
|
||||
parent_record_id = reflection.primary_key_name
|
||||
end
|
||||
|
||||
conditions.first << append_conditions(reflection, preload_options)
|
||||
|
||||
associated_records = reflection.klass.find(:all,
|
||||
:conditions => conditions,
|
||||
:include => options[:include],
|
||||
:joins => joins,
|
||||
:select => "#{options[:select] || table_name+'.*'}, #{parent_record_id} as parent_record_id_",
|
||||
:order => options[:order])
|
||||
|
||||
set_association_collection_records(id_to_record_map, reflection.name, associated_records, 'parent_record_id_')
|
||||
end
|
||||
|
||||
def preload_has_many_association(records, reflection, preload_options={})
|
||||
id_to_record_map, ids = construct_id_map_for_composite(records)
|
||||
records.each {|record| record.send(reflection.name).loaded}
|
||||
options = reflection.options
|
||||
|
||||
if options[:through]
|
||||
through_records = preload_through_records(records, reflection, options[:through])
|
||||
through_reflection = reflections[options[:through]]
|
||||
through_primary_key = through_reflection.primary_key_name
|
||||
|
||||
unless through_records.empty?
|
||||
source = reflection.source_reflection.name
|
||||
#add conditions from reflection!
|
||||
through_records.first.class.preload_associations(through_records, source, reflection.options)
|
||||
through_records.each do |through_record|
|
||||
key = through_primary_key.to_s.split(CompositePrimaryKeys::ID_SEP).map{|k| through_record.send(k)}.join(CompositePrimaryKeys::ID_SEP)
|
||||
add_preloaded_records_to_collection(id_to_record_map[key], reflection.name, through_record.send(source))
|
||||
end
|
||||
end
|
||||
else
|
||||
associated_records = find_associated_records(ids, reflection, preload_options)
|
||||
set_association_collection_records(id_to_record_map, reflection.name, associated_records, reflection.primary_key_name.to_s.split(CompositePrimaryKeys::ID_SEP))
|
||||
end
|
||||
end
|
||||
|
||||
def preload_through_records(records, reflection, through_association)
|
||||
through_reflection = reflections[through_association]
|
||||
through_primary_key = through_reflection.primary_key_name
|
||||
|
||||
if reflection.options[:source_type]
|
||||
interface = reflection.source_reflection.options[:foreign_type]
|
||||
preload_options = {:conditions => ["#{connection.quote_column_name interface} = ?", reflection.options[:source_type]]}
|
||||
|
||||
records.compact!
|
||||
records.first.class.preload_associations(records, through_association, preload_options)
|
||||
|
||||
# Dont cache the association - we would only be caching a subset
|
||||
through_records = []
|
||||
records.each do |record|
|
||||
proxy = record.send(through_association)
|
||||
|
||||
if proxy.respond_to?(:target)
|
||||
through_records << proxy.target
|
||||
proxy.reset
|
||||
else # this is a has_one :through reflection
|
||||
through_records << proxy if proxy
|
||||
end
|
||||
end
|
||||
through_records.flatten!
|
||||
else
|
||||
records.first.class.preload_associations(records, through_association)
|
||||
through_records = records.map {|record| record.send(through_association)}.flatten
|
||||
end
|
||||
|
||||
through_records.compact!
|
||||
through_records
|
||||
end
|
||||
|
||||
def preload_belongs_to_association(records, reflection, preload_options={})
|
||||
options = reflection.options
|
||||
primary_key_name = reflection.primary_key_name.to_s.split(CompositePrimaryKeys::ID_SEP)
|
||||
|
||||
if options[:polymorphic]
|
||||
raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
|
||||
else
|
||||
# I need to keep the original ids for each record (as opposed to the stringified) so
|
||||
# that they get properly converted for each db so the id_map ends up looking like:
|
||||
#
|
||||
# { '1,2' => {:id => [1,2], :records => [...records...]}}
|
||||
id_map = {}
|
||||
|
||||
records.each do |record|
|
||||
key = primary_key_name.map{|k| record.send(k)}
|
||||
key_as_string = key.join(CompositePrimaryKeys::ID_SEP)
|
||||
|
||||
if key_as_string
|
||||
mapped_records = (id_map[key_as_string] ||= {:id => key, :records => []})
|
||||
mapped_records[:records] << record
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
klasses_and_ids = [[reflection.klass.name, id_map]]
|
||||
end
|
||||
|
||||
klasses_and_ids.each do |klass_and_id|
|
||||
klass_name, id_map = *klass_and_id
|
||||
klass = klass_name.constantize
|
||||
table_name = klass.quoted_table_name
|
||||
connection = reflection.active_record.connection
|
||||
|
||||
if composite?
|
||||
primary_key = klass.primary_key.to_s.split(CompositePrimaryKeys::ID_SEP)
|
||||
ids = id_map.keys.uniq.map {|id| id_map[id][:id]}
|
||||
|
||||
where = (primary_key * ids.size).in_groups_of(primary_key.size).map do |keys|
|
||||
"(" + keys.map{|key| "#{table_name}.#{connection.quote_column_name(key)} = ?"}.join(" AND ") + ")"
|
||||
end.join(" OR ")
|
||||
|
||||
conditions = [where, ids].flatten
|
||||
else
|
||||
conditions = ["#{table_name}.#{connection.quote_column_name(primary_key)} IN (?)", id_map.keys.uniq]
|
||||
end
|
||||
|
||||
conditions.first << append_conditions(reflection, preload_options)
|
||||
|
||||
associated_records = klass.find(:all,
|
||||
:conditions => conditions,
|
||||
:include => options[:include],
|
||||
:select => options[:select],
|
||||
:joins => options[:joins],
|
||||
:order => options[:order])
|
||||
|
||||
set_association_single_records(id_map, reflection.name, associated_records, primary_key)
|
||||
end
|
||||
end
|
||||
|
||||
def set_association_collection_records(id_to_record_map, reflection_name, associated_records, key)
|
||||
associated_records.each do |associated_record|
|
||||
associated_record_key = associated_record[key]
|
||||
associated_record_key = associated_record_key.is_a?(Array) ? associated_record_key.join(CompositePrimaryKeys::ID_SEP) : associated_record_key.to_s
|
||||
mapped_records = id_to_record_map[associated_record_key]
|
||||
add_preloaded_records_to_collection(mapped_records, reflection_name, associated_record)
|
||||
end
|
||||
end
|
||||
|
||||
def set_association_single_records(id_to_record_map, reflection_name, associated_records, key)
|
||||
seen_keys = {}
|
||||
associated_records.each do |associated_record|
|
||||
associated_record_key = associated_record[key]
|
||||
associated_record_key = associated_record_key.is_a?(Array) ? associated_record_key.join(CompositePrimaryKeys::ID_SEP) : associated_record_key.to_s
|
||||
|
||||
#this is a has_one or belongs_to: there should only be one record.
|
||||
#Unfortunately we can't (in portable way) ask the database for 'all records where foo_id in (x,y,z), but please
|
||||
# only one row per distinct foo_id' so this where we enforce that
|
||||
next if seen_keys[associated_record_key]
|
||||
seen_keys[associated_record_key] = true
|
||||
mapped_records = id_to_record_map[associated_record_key][:records]
|
||||
mapped_records.each do |mapped_record|
|
||||
mapped_record.send("set_#{reflection_name}_target", associated_record)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def find_associated_records(ids, reflection, preload_options)
|
||||
options = reflection.options
|
||||
table_name = reflection.klass.quoted_table_name
|
||||
|
||||
if interface = reflection.options[:as]
|
||||
raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
|
||||
else
|
||||
connection = reflection.active_record.connection
|
||||
foreign_key = reflection.primary_key_name
|
||||
conditions = ["#{table_name}.#{connection.quote_column_name(foreign_key)} IN (?)", ids]
|
||||
|
||||
if composite?
|
||||
foreign_keys = foreign_key.to_s.split(CompositePrimaryKeys::ID_SEP)
|
||||
|
||||
where = (foreign_keys * ids.size).in_groups_of(foreign_keys.size).map do |keys|
|
||||
"(" + keys.map{|key| "#{table_name}.#{connection.quote_column_name(key)} = ?"}.join(" AND ") + ")"
|
||||
end.join(" OR ")
|
||||
|
||||
conditions = [where, ids].flatten
|
||||
end
|
||||
end
|
||||
|
||||
conditions.first << append_conditions(reflection, preload_options)
|
||||
|
||||
reflection.klass.find(:all,
|
||||
:select => (preload_options[:select] || options[:select] || "#{table_name}.*"),
|
||||
:include => preload_options[:include] || options[:include],
|
||||
:conditions => conditions,
|
||||
:joins => options[:joins],
|
||||
:group => preload_options[:group] || options[:group],
|
||||
:order => preload_options[:order] || options[:order])
|
||||
end
|
||||
|
||||
# Given a collection of ActiveRecord objects, constructs a Hash which maps
|
||||
# the objects' IDs to the relevant objects. Returns a 2-tuple
|
||||
# <tt>(id_to_record_map, ids)</tt> where +id_to_record_map+ is the Hash,
|
||||
# and +ids+ is an Array of record IDs.
|
||||
def construct_id_map_for_composite(records)
|
||||
id_to_record_map = {}
|
||||
ids = []
|
||||
records.each do |record|
|
||||
primary_key ||= record.class.primary_key
|
||||
ids << record.id
|
||||
mapped_records = (id_to_record_map[record.id.to_s] ||= [])
|
||||
mapped_records << record
|
||||
end
|
||||
ids.uniq!
|
||||
return id_to_record_map, ids
|
||||
end
|
||||
|
||||
def full_composite_join_clause(reflection, table1, full_keys1, table2, full_keys2)
|
||||
connection = reflection.active_record.connection
|
||||
full_keys1 = full_keys1.split(CompositePrimaryKeys::ID_SEP) if full_keys1.is_a?(String)
|
||||
full_keys2 = full_keys2.split(CompositePrimaryKeys::ID_SEP) if full_keys2.is_a?(String)
|
||||
where_clause = [full_keys1, full_keys2].transpose.map do |key_pair|
|
||||
quoted1 = connection.quote_table_name(table1)
|
||||
quoted2 = connection.quote_table_name(table2)
|
||||
"#{quoted1}.#{connection.quote_column_name(key_pair.first)}=#{quoted2}.#{connection.quote_column_name(key_pair.last)}"
|
||||
end.join(" AND ")
|
||||
"(#{where_clause})"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
428
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/associations.rb
vendored
Normal file
428
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/associations.rb
vendored
Normal file
|
@ -0,0 +1,428 @@
|
|||
module CompositePrimaryKeys
|
||||
module ActiveRecord
|
||||
module Associations
|
||||
def self.append_features(base)
|
||||
super
|
||||
base.send(:extend, ClassMethods)
|
||||
end
|
||||
|
||||
# Composite key versions of Association functions
|
||||
module ClassMethods
|
||||
|
||||
def construct_counter_sql_with_included_associations(options, join_dependency)
|
||||
scope = scope(:find)
|
||||
sql = "SELECT COUNT(DISTINCT #{quoted_table_columns(primary_key)})"
|
||||
|
||||
# A (slower) workaround if we're using a backend, like sqlite, that doesn't support COUNT DISTINCT.
|
||||
if !self.connection.supports_count_distinct?
|
||||
sql = "SELECT COUNT(*) FROM (SELECT DISTINCT #{quoted_table_columns(primary_key)}"
|
||||
end
|
||||
|
||||
sql << " FROM #{quoted_table_name} "
|
||||
sql << join_dependency.join_associations.collect{|join| join.association_join }.join
|
||||
|
||||
add_joins!(sql, options[:joins], scope)
|
||||
add_conditions!(sql, options[:conditions], scope)
|
||||
add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
|
||||
|
||||
add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections)
|
||||
|
||||
if !self.connection.supports_count_distinct?
|
||||
sql << ")"
|
||||
end
|
||||
|
||||
return sanitize_sql(sql)
|
||||
end
|
||||
|
||||
def construct_finder_sql_with_included_associations(options, join_dependency)
|
||||
scope = scope(:find)
|
||||
sql = "SELECT #{column_aliases(join_dependency)} FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
|
||||
sql << join_dependency.join_associations.collect{|join| join.association_join }.join
|
||||
|
||||
add_joins!(sql, options[:joins], scope)
|
||||
add_conditions!(sql, options[:conditions], scope)
|
||||
add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && options[:limit]
|
||||
|
||||
sql << "ORDER BY #{options[:order]} " if options[:order]
|
||||
|
||||
add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections)
|
||||
|
||||
return sanitize_sql(sql)
|
||||
end
|
||||
|
||||
def table_columns(columns)
|
||||
columns.collect {|column| "#{self.quoted_table_name}.#{connection.quote_column_name(column)}"}
|
||||
end
|
||||
|
||||
def quoted_table_columns(columns)
|
||||
table_columns(columns).join(ID_SEP)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ActiveRecord::Associations::ClassMethods
|
||||
class JoinDependency
|
||||
def construct_association(record, join, row)
|
||||
case join.reflection.macro
|
||||
when :has_many, :has_and_belongs_to_many
|
||||
collection = record.send(join.reflection.name)
|
||||
collection.loaded
|
||||
|
||||
join_aliased_primary_keys = join.active_record.composite? ?
|
||||
join.aliased_primary_key : [join.aliased_primary_key]
|
||||
return nil if
|
||||
record.id.to_s != join.parent.record_id(row).to_s or not
|
||||
join_aliased_primary_keys.select {|key| row[key].nil?}.blank?
|
||||
association = join.instantiate(row)
|
||||
collection.target.push(association) unless collection.target.include?(association)
|
||||
when :has_one, :belongs_to
|
||||
return if record.id.to_s != join.parent.record_id(row).to_s or
|
||||
[*join.aliased_primary_key].any? { |key| row[key].nil? }
|
||||
association = join.instantiate(row)
|
||||
record.send("set_#{join.reflection.name}_target", association)
|
||||
else
|
||||
raise ConfigurationError, "unknown macro: #{join.reflection.macro}"
|
||||
end
|
||||
return association
|
||||
end
|
||||
|
||||
class JoinBase
|
||||
def aliased_primary_key
|
||||
active_record.composite? ?
|
||||
primary_key.inject([]) {|aliased_keys, key| aliased_keys << "#{ aliased_prefix }_r#{aliased_keys.length}"} :
|
||||
"#{ aliased_prefix }_r0"
|
||||
end
|
||||
|
||||
def record_id(row)
|
||||
active_record.composite? ?
|
||||
aliased_primary_key.map {|key| row[key]}.to_composite_ids :
|
||||
row[aliased_primary_key]
|
||||
end
|
||||
|
||||
def column_names_with_alias
|
||||
unless @column_names_with_alias
|
||||
@column_names_with_alias = []
|
||||
keys = active_record.composite? ? primary_key.map(&:to_s) : [primary_key]
|
||||
(keys + (column_names - keys)).each_with_index do |column_name, i|
|
||||
@column_names_with_alias << [column_name, "#{ aliased_prefix }_r#{ i }"]
|
||||
end
|
||||
end
|
||||
return @column_names_with_alias
|
||||
end
|
||||
end
|
||||
|
||||
class JoinAssociation < JoinBase
|
||||
alias single_association_join association_join
|
||||
def association_join
|
||||
reflection.active_record.composite? ? composite_association_join : single_association_join
|
||||
end
|
||||
|
||||
def composite_association_join
|
||||
join = case reflection.macro
|
||||
when :has_and_belongs_to_many
|
||||
" LEFT OUTER JOIN %s ON %s " % [
|
||||
table_alias_for(options[:join_table], aliased_join_table_name),
|
||||
composite_join_clause(
|
||||
full_keys(aliased_join_table_name, options[:foreign_key] || reflection.active_record.to_s.classify.foreign_key),
|
||||
full_keys(reflection.active_record.table_name, reflection.active_record.primary_key)
|
||||
)
|
||||
] +
|
||||
" LEFT OUTER JOIN %s ON %s " % [
|
||||
table_name_and_alias,
|
||||
composite_join_clause(
|
||||
full_keys(aliased_table_name, klass.primary_key),
|
||||
full_keys(aliased_join_table_name, options[:association_foreign_key] || klass.table_name.classify.foreign_key)
|
||||
)
|
||||
]
|
||||
when :has_many, :has_one
|
||||
case
|
||||
when reflection.macro == :has_many && reflection.options[:through]
|
||||
through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : ''
|
||||
if through_reflection.options[:as] # has_many :through against a polymorphic join
|
||||
raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
|
||||
else
|
||||
if source_reflection.macro == :has_many && source_reflection.options[:as]
|
||||
raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
|
||||
else
|
||||
case source_reflection.macro
|
||||
when :belongs_to
|
||||
first_key = primary_key
|
||||
second_key = options[:foreign_key] || klass.to_s.classify.foreign_key
|
||||
when :has_many
|
||||
first_key = through_reflection.klass.to_s.classify.foreign_key
|
||||
second_key = options[:foreign_key] || primary_key
|
||||
end
|
||||
|
||||
" LEFT OUTER JOIN %s ON %s " % [
|
||||
table_alias_for(through_reflection.klass.table_name, aliased_join_table_name),
|
||||
composite_join_clause(
|
||||
full_keys(aliased_join_table_name, through_reflection.primary_key_name),
|
||||
full_keys(parent.aliased_table_name, parent.primary_key)
|
||||
)
|
||||
] +
|
||||
" LEFT OUTER JOIN %s ON %s " % [
|
||||
table_name_and_alias,
|
||||
composite_join_clause(
|
||||
full_keys(aliased_table_name, first_key),
|
||||
full_keys(aliased_join_table_name, second_key)
|
||||
)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
when reflection.macro == :has_many && reflection.options[:as]
|
||||
raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
|
||||
when reflection.macro == :has_one && reflection.options[:as]
|
||||
raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
|
||||
else
|
||||
foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
|
||||
" LEFT OUTER JOIN %s ON %s " % [
|
||||
table_name_and_alias,
|
||||
composite_join_clause(
|
||||
full_keys(aliased_table_name, foreign_key),
|
||||
full_keys(parent.aliased_table_name, parent.primary_key)),
|
||||
]
|
||||
end
|
||||
when :belongs_to
|
||||
" LEFT OUTER JOIN %s ON %s " % [
|
||||
table_name_and_alias,
|
||||
composite_join_clause(
|
||||
full_keys(aliased_table_name, reflection.klass.primary_key),
|
||||
full_keys(parent.aliased_table_name, options[:foreign_key] || klass.to_s.foreign_key)),
|
||||
]
|
||||
else
|
||||
""
|
||||
end || ''
|
||||
join << %(AND %s.%s = %s ) % [
|
||||
aliased_table_name,
|
||||
reflection.active_record.connection.quote_column_name(reflection.active_record.inheritance_column),
|
||||
klass.connection.quote(klass.name)] unless klass.descends_from_active_record?
|
||||
join << "AND #{interpolate_sql(sanitize_sql(reflection.options[:conditions]))} " if reflection.options[:conditions]
|
||||
join
|
||||
end
|
||||
|
||||
def full_keys(table_name, keys)
|
||||
connection = reflection.active_record.connection
|
||||
quoted_table_name = connection.quote_table_name(table_name)
|
||||
if keys.is_a?(Array)
|
||||
keys.collect {|key| "#{quoted_table_name}.#{connection.quote_column_name(key)}"}.join(CompositePrimaryKeys::ID_SEP)
|
||||
else
|
||||
"#{quoted_table_name}.#{connection.quote_column_name(keys)}"
|
||||
end
|
||||
end
|
||||
|
||||
def composite_join_clause(full_keys1, full_keys2)
|
||||
full_keys1 = full_keys1.split(CompositePrimaryKeys::ID_SEP) if full_keys1.is_a?(String)
|
||||
full_keys2 = full_keys2.split(CompositePrimaryKeys::ID_SEP) if full_keys2.is_a?(String)
|
||||
where_clause = [full_keys1, full_keys2].transpose.map do |key1, key2|
|
||||
"#{key1}=#{key2}"
|
||||
end.join(" AND ")
|
||||
"(#{where_clause})"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ActiveRecord::Associations
|
||||
class AssociationProxy #:nodoc:
|
||||
|
||||
def composite_where_clause(full_keys, ids)
|
||||
full_keys = full_keys.split(CompositePrimaryKeys::ID_SEP) if full_keys.is_a?(String)
|
||||
|
||||
if ids.is_a?(String)
|
||||
ids = [[ids]]
|
||||
elsif not ids.first.is_a?(Array) # if single comp key passed, turn into an array of 1
|
||||
ids = [ids.to_composite_ids]
|
||||
end
|
||||
|
||||
where_clause = ids.map do |id_set|
|
||||
transposed = id_set.size == 1 ? [[full_keys, id_set.first]] : [full_keys, id_set].transpose
|
||||
transposed.map do |full_key, id|
|
||||
"#{full_key.to_s}=#{@reflection.klass.sanitize(id)}"
|
||||
end.join(" AND ")
|
||||
end.join(") OR (")
|
||||
|
||||
"(#{where_clause})"
|
||||
end
|
||||
|
||||
def composite_join_clause(full_keys1, full_keys2)
|
||||
full_keys1 = full_keys1.split(CompositePrimaryKeys::ID_SEP) if full_keys1.is_a?(String)
|
||||
full_keys2 = full_keys2.split(CompositePrimaryKeys::ID_SEP) if full_keys2.is_a?(String)
|
||||
|
||||
where_clause = [full_keys1, full_keys2].transpose.map do |key1, key2|
|
||||
"#{key1}=#{key2}"
|
||||
end.join(" AND ")
|
||||
|
||||
"(#{where_clause})"
|
||||
end
|
||||
|
||||
def full_composite_join_clause(table1, full_keys1, table2, full_keys2)
|
||||
connection = @reflection.active_record.connection
|
||||
full_keys1 = full_keys1.split(CompositePrimaryKeys::ID_SEP) if full_keys1.is_a?(String)
|
||||
full_keys2 = full_keys2.split(CompositePrimaryKeys::ID_SEP) if full_keys2.is_a?(String)
|
||||
|
||||
quoted1 = connection.quote_table_name(table1)
|
||||
quoted2 = connection.quote_table_name(table2)
|
||||
|
||||
where_clause = [full_keys1, full_keys2].transpose.map do |key_pair|
|
||||
"#{quoted1}.#{connection.quote_column_name(key_pair.first)}=#{quoted2}.#{connection.quote_column_name(key_pair.last)}"
|
||||
end.join(" AND ")
|
||||
|
||||
"(#{where_clause})"
|
||||
end
|
||||
|
||||
def full_keys(table_name, keys)
|
||||
connection = @reflection.active_record.connection
|
||||
quoted_table_name = connection.quote_table_name(table_name)
|
||||
keys = keys.split(CompositePrimaryKeys::ID_SEP) if keys.is_a?(String)
|
||||
if keys.is_a?(Array)
|
||||
keys.collect {|key| "#{quoted_table_name}.#{connection.quote_column_name(key)}"}.join(CompositePrimaryKeys::ID_SEP)
|
||||
else
|
||||
"#{quoted_table_name}.#{connection.quote_column_name(keys)}"
|
||||
end
|
||||
end
|
||||
|
||||
def full_columns_equals(table_name, keys, quoted_ids)
|
||||
connection = @reflection.active_record.connection
|
||||
quoted_table_name = connection.quote_table_name(table_name)
|
||||
if keys.is_a?(Symbol) or (keys.is_a?(String) and keys == keys.to_s.split(CompositePrimaryKeys::ID_SEP))
|
||||
return "#{quoted_table_name}.#{connection.quote_column_name(keys)} = #{quoted_ids}"
|
||||
end
|
||||
keys = keys.split(CompositePrimaryKeys::ID_SEP) if keys.is_a?(String)
|
||||
quoted_ids = quoted_ids.split(CompositePrimaryKeys::ID_SEP) if quoted_ids.is_a?(String)
|
||||
keys_ids = [keys, quoted_ids].transpose
|
||||
keys_ids.collect {|key, id| "(#{quoted_table_name}.#{connection.quote_column_name(key)} = #{id})"}.join(' AND ')
|
||||
end
|
||||
|
||||
def set_belongs_to_association_for(record)
|
||||
if @reflection.options[:as]
|
||||
record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
|
||||
record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s
|
||||
else
|
||||
key_values = @reflection.primary_key_name.to_s.split(CompositePrimaryKeys::ID_SEP).zip([@owner.id].flatten)
|
||||
key_values.each{|key, value| record[key] = value} unless @owner.new_record?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
|
||||
def construct_sql
|
||||
@reflection.options[:finder_sql] &&= interpolate_sql(@reflection.options[:finder_sql])
|
||||
|
||||
if @reflection.options[:finder_sql]
|
||||
@finder_sql = @reflection.options[:finder_sql]
|
||||
else
|
||||
@finder_sql = full_columns_equals(@reflection.options[:join_table], @reflection.primary_key_name, @owner.quoted_id)
|
||||
@finder_sql << " AND (#{conditions})" if conditions
|
||||
end
|
||||
|
||||
@join_sql = "INNER JOIN #{@reflection.active_record.connection.quote_table_name(@reflection.options[:join_table])} ON " +
|
||||
full_composite_join_clause(@reflection.klass.table_name, @reflection.klass.primary_key, @reflection.options[:join_table], @reflection.association_foreign_key)
|
||||
end
|
||||
end
|
||||
|
||||
class HasManyAssociation < AssociationCollection #:nodoc:
|
||||
def construct_sql
|
||||
case
|
||||
when @reflection.options[:finder_sql]
|
||||
@finder_sql = interpolate_sql(@reflection.options[:finder_sql])
|
||||
|
||||
when @reflection.options[:as]
|
||||
@finder_sql =
|
||||
"#{@reflection.klass.quoted_table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " +
|
||||
"#{@reflection.klass.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
|
||||
@finder_sql << " AND (#{conditions})" if conditions
|
||||
|
||||
else
|
||||
@finder_sql = full_columns_equals(@reflection.klass.table_name, @reflection.primary_key_name, @owner.quoted_id)
|
||||
@finder_sql << " AND (#{conditions})" if conditions
|
||||
end
|
||||
|
||||
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 delete_records(records)
|
||||
if @reflection.options[:dependent]
|
||||
records.each { |r| r.destroy }
|
||||
else
|
||||
connection = @reflection.active_record.connection
|
||||
field_names = @reflection.primary_key_name.split(',')
|
||||
field_names.collect! {|n| connection.quote_column_name(n) + " = NULL"}
|
||||
records.each do |r|
|
||||
where_clause = nil
|
||||
|
||||
if r.quoted_id.to_s.include?(CompositePrimaryKeys::ID_SEP)
|
||||
where_clause_terms = [@reflection.klass.primary_key, r.quoted_id].transpose.map do |pair|
|
||||
"(#{connection.quote_column_name(pair[0])} = #{pair[1]})"
|
||||
end
|
||||
where_clause = where_clause_terms.join(" AND ")
|
||||
else
|
||||
where_clause = connection.quote_column_name(@reflection.klass.primary_key) + ' = ' + r.quoted_id
|
||||
end
|
||||
|
||||
@reflection.klass.update_all( field_names.join(',') , where_clause)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class HasOneAssociation < BelongsToAssociation #:nodoc:
|
||||
def construct_sql
|
||||
case
|
||||
when @reflection.options[:as]
|
||||
@finder_sql =
|
||||
"#{@reflection.klass.quoted_table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " +
|
||||
"#{@reflection.klass.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
|
||||
else
|
||||
@finder_sql = full_columns_equals(@reflection.klass.table_name, @reflection.primary_key_name, @owner.quoted_id)
|
||||
end
|
||||
|
||||
@finder_sql << " AND (#{conditions})" if conditions
|
||||
end
|
||||
end
|
||||
|
||||
class HasManyThroughAssociation < HasManyAssociation #:nodoc:
|
||||
def construct_conditions_with_composite_keys
|
||||
if @reflection.through_reflection.options[:as]
|
||||
construct_conditions_without_composite_keys
|
||||
else
|
||||
conditions = full_columns_equals(@reflection.through_reflection.table_name, @reflection.through_reflection.primary_key_name, @owner.quoted_id)
|
||||
conditions << " AND (#{sql_conditions})" if sql_conditions
|
||||
conditions
|
||||
end
|
||||
end
|
||||
alias_method_chain :construct_conditions, :composite_keys
|
||||
|
||||
def construct_joins_with_composite_keys(custom_joins = nil)
|
||||
if @reflection.through_reflection.options[:as] || @reflection.source_reflection.options[:as]
|
||||
construct_joins_without_composite_keys(custom_joins)
|
||||
else
|
||||
if @reflection.source_reflection.macro == :belongs_to
|
||||
reflection_primary_key = @reflection.klass.primary_key
|
||||
source_primary_key = @reflection.source_reflection.primary_key_name
|
||||
else
|
||||
reflection_primary_key = @reflection.source_reflection.primary_key_name
|
||||
source_primary_key = @reflection.klass.primary_key
|
||||
end
|
||||
|
||||
"INNER JOIN %s ON %s #{@reflection.options[:joins]} #{custom_joins}" % [
|
||||
@reflection.through_reflection.quoted_table_name,
|
||||
composite_join_clause(full_keys(@reflection.table_name, reflection_primary_key), full_keys(@reflection.through_reflection.table_name, source_primary_key))
|
||||
]
|
||||
end
|
||||
end
|
||||
alias_method_chain :construct_joins, :composite_keys
|
||||
end
|
||||
end
|
84
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/attribute_methods.rb
vendored
Normal file
84
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/attribute_methods.rb
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
module CompositePrimaryKeys
|
||||
module ActiveRecord
|
||||
module AttributeMethods #:nodoc:
|
||||
def self.append_features(base)
|
||||
super
|
||||
base.send(:extend, ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Define an attribute reader method. Cope with nil column.
|
||||
def define_read_method(symbol, attr_name, column)
|
||||
cast_code = column.type_cast_code('v') if column
|
||||
cast_code = "::#{cast_code}" if cast_code && cast_code.match('ActiveRecord::.*')
|
||||
access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
|
||||
|
||||
unless self.primary_keys.include?(attr_name.to_sym)
|
||||
access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
|
||||
end
|
||||
|
||||
if cache_attribute?(attr_name)
|
||||
access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
|
||||
end
|
||||
|
||||
evaluate_attribute_method attr_name, "def #{symbol}; #{access_code}; end"
|
||||
end
|
||||
|
||||
# Evaluate the definition for an attribute related method
|
||||
def evaluate_attribute_method(attr_name, method_definition, method_name=attr_name)
|
||||
unless primary_keys.include?(method_name.to_sym)
|
||||
generated_methods << method_name
|
||||
end
|
||||
|
||||
begin
|
||||
class_eval(method_definition, __FILE__, __LINE__)
|
||||
rescue SyntaxError => err
|
||||
generated_methods.delete(attr_name)
|
||||
if logger
|
||||
logger.warn "Exception occurred during reader method compilation."
|
||||
logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
|
||||
logger.warn "#{err.message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Allows access to the object attributes, which are held in the @attributes hash, as though they
|
||||
# were first-class methods. So a Person class with a name attribute can use Person#name and
|
||||
# Person#name= and never directly use the attributes hash -- except for multiple assigns with
|
||||
# ActiveRecord#attributes=. A Milestone class can also ask Milestone#completed? to test that
|
||||
# the completed attribute is not nil or 0.
|
||||
#
|
||||
# It's also possible to instantiate related objects, so a Client class belonging to the clients
|
||||
# table with a master_id foreign key can instantiate master through Client#master.
|
||||
def method_missing(method_id, *args, &block)
|
||||
method_name = method_id.to_s
|
||||
|
||||
# If we haven't generated any methods yet, generate them, then
|
||||
# see if we've created the method we're looking for.
|
||||
if !self.class.generated_methods?
|
||||
self.class.define_attribute_methods
|
||||
|
||||
if self.class.generated_methods.include?(method_name)
|
||||
return self.send(method_id, *args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
if self.class.primary_keys.include?(method_name.to_sym)
|
||||
ids[self.class.primary_keys.index(method_name.to_sym)]
|
||||
elsif md = self.class.match_attribute_method?(method_name)
|
||||
attribute_name, method_type = md.pre_match, md.to_s
|
||||
if @attributes.include?(attribute_name)
|
||||
__send__("attribute#{method_type}", attribute_name, *args, &block)
|
||||
else
|
||||
super
|
||||
end
|
||||
elsif @attributes.include?(method_name)
|
||||
read_attribute(method_name)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
341
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/base.rb
vendored
Normal file
341
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/base.rb
vendored
Normal file
|
@ -0,0 +1,341 @@
|
|||
module CompositePrimaryKeys
|
||||
module ActiveRecord #:nodoc:
|
||||
class CompositeKeyError < StandardError #:nodoc:
|
||||
end
|
||||
|
||||
module Base #:nodoc:
|
||||
|
||||
INVALID_FOR_COMPOSITE_KEYS = 'Not appropriate for composite primary keys'
|
||||
NOT_IMPLEMENTED_YET = 'Not implemented for composite primary keys yet'
|
||||
|
||||
def self.append_features(base)
|
||||
super
|
||||
base.send(:include, InstanceMethods)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def set_primary_keys(*keys)
|
||||
keys = keys.first if keys.first.is_a?(Array)
|
||||
keys = keys.map { |k| k.to_sym }
|
||||
cattr_accessor :primary_keys
|
||||
self.primary_keys = keys.to_composite_keys
|
||||
|
||||
class_eval <<-EOV
|
||||
extend CompositeClassMethods
|
||||
include CompositeInstanceMethods
|
||||
|
||||
include CompositePrimaryKeys::ActiveRecord::Associations
|
||||
include CompositePrimaryKeys::ActiveRecord::AssociationPreload
|
||||
include CompositePrimaryKeys::ActiveRecord::Calculations
|
||||
include CompositePrimaryKeys::ActiveRecord::AttributeMethods
|
||||
EOV
|
||||
end
|
||||
|
||||
def composite?
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def composite?; self.class.composite?; end
|
||||
end
|
||||
|
||||
module CompositeInstanceMethods
|
||||
|
||||
# A model instance's primary keys is always available as model.ids
|
||||
# whether you name it the default 'id' or set it to something else.
|
||||
def id
|
||||
attr_names = self.class.primary_keys
|
||||
CompositeIds.new(attr_names.map { |attr_name| read_attribute(attr_name) })
|
||||
end
|
||||
alias_method :ids, :id
|
||||
|
||||
def to_param
|
||||
id.to_s
|
||||
end
|
||||
|
||||
def id_before_type_cast #:nodoc:
|
||||
raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::NOT_IMPLEMENTED_YET
|
||||
end
|
||||
|
||||
def quoted_id #:nodoc:
|
||||
[self.class.primary_keys, ids].
|
||||
transpose.
|
||||
map {|attr_name,id| quote_value(id, column_for_attribute(attr_name))}.
|
||||
to_composite_ids
|
||||
end
|
||||
|
||||
# Sets the primary ID.
|
||||
def id=(ids)
|
||||
ids = ids.split(ID_SEP) if ids.is_a?(String)
|
||||
ids.flatten!
|
||||
unless ids.is_a?(Array) and ids.length == self.class.primary_keys.length
|
||||
raise "#{self.class}.id= requires #{self.class.primary_keys.length} ids"
|
||||
end
|
||||
[primary_keys, ids].transpose.each {|key, an_id| write_attribute(key , an_id)}
|
||||
id
|
||||
end
|
||||
|
||||
# Returns a clone of the record that hasn't been assigned an id yet and
|
||||
# is treated as a new record. Note that this is a "shallow" clone:
|
||||
# it copies the object's attributes only, not its associations.
|
||||
# The extent of a "deep" clone is application-specific and is therefore
|
||||
# left to the application to implement according to its need.
|
||||
def clone
|
||||
attrs = self.attributes_before_type_cast
|
||||
self.class.primary_keys.each {|key| attrs.delete(key.to_s)}
|
||||
self.class.new do |record|
|
||||
record.send :instance_variable_set, '@attributes', attrs
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
# The xx_without_callbacks methods are overwritten as that is the end of the alias chain
|
||||
|
||||
# Creates a new record with values matching those of the instance attributes.
|
||||
def create_without_callbacks
|
||||
unless self.id
|
||||
raise CompositeKeyError, "Composite keys do not generated ids from sequences, you must provide id values"
|
||||
end
|
||||
attributes_minus_pks = attributes_with_quotes(false)
|
||||
quoted_pk_columns = self.class.primary_key.map { |col| connection.quote_column_name(col) }
|
||||
cols = quoted_column_names(attributes_minus_pks) << quoted_pk_columns
|
||||
vals = attributes_minus_pks.values << quoted_id
|
||||
connection.insert(
|
||||
"INSERT INTO #{self.class.quoted_table_name} " +
|
||||
"(#{cols.join(', ')}) " +
|
||||
"VALUES (#{vals.join(', ')})",
|
||||
"#{self.class.name} Create",
|
||||
self.class.primary_key,
|
||||
self.id
|
||||
)
|
||||
@new_record = false
|
||||
return true
|
||||
end
|
||||
|
||||
# Updates the associated record with values matching those of the instance attributes.
|
||||
def update_without_callbacks
|
||||
where_clause_terms = [self.class.primary_key, quoted_id].transpose.map do |pair|
|
||||
"(#{connection.quote_column_name(pair[0])} = #{pair[1]})"
|
||||
end
|
||||
where_clause = where_clause_terms.join(" AND ")
|
||||
connection.update(
|
||||
"UPDATE #{self.class.quoted_table_name} " +
|
||||
"SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false))} " +
|
||||
"WHERE #{where_clause}",
|
||||
"#{self.class.name} Update"
|
||||
)
|
||||
return true
|
||||
end
|
||||
|
||||
# Deletes the record in the database and freezes this instance to reflect that no changes should
|
||||
# be made (since they can't be persisted).
|
||||
def destroy_without_callbacks
|
||||
where_clause_terms = [self.class.primary_key, quoted_id].transpose.map do |pair|
|
||||
"(#{connection.quote_column_name(pair[0])} = #{pair[1]})"
|
||||
end
|
||||
where_clause = where_clause_terms.join(" AND ")
|
||||
unless new_record?
|
||||
connection.delete(
|
||||
"DELETE FROM #{self.class.quoted_table_name} " +
|
||||
"WHERE #{where_clause}",
|
||||
"#{self.class.name} Destroy"
|
||||
)
|
||||
end
|
||||
freeze
|
||||
end
|
||||
end
|
||||
|
||||
module CompositeClassMethods
|
||||
def primary_key; primary_keys; end
|
||||
def primary_key=(keys); primary_keys = keys; end
|
||||
|
||||
def composite?
|
||||
true
|
||||
end
|
||||
|
||||
#ids_to_s([[1,2],[7,3]]) -> "(1,2),(7,3)"
|
||||
#ids_to_s([[1,2],[7,3]], ',', ';') -> "1,2;7,3"
|
||||
def ids_to_s(many_ids, id_sep = CompositePrimaryKeys::ID_SEP, list_sep = ',', left_bracket = '(', right_bracket = ')')
|
||||
many_ids.map {|ids| "#{left_bracket}#{ids}#{right_bracket}"}.join(list_sep)
|
||||
end
|
||||
|
||||
# Creates WHERE condition from list of composited ids
|
||||
# User.update_all({:role => 'admin'}, :conditions => composite_where_clause([[1, 2], [2, 2]])) #=> UPDATE admins SET admin.role='admin' WHERE (admin.type=1 AND admin.type2=2) OR (admin.type=2 AND admin.type2=2)
|
||||
# User.find(:all, :conditions => composite_where_clause([[1, 2], [2, 2]])) #=> SELECT * FROM admins WHERE (admin.type=1 AND admin.type2=2) OR (admin.type=2 AND admin.type2=2)
|
||||
def composite_where_clause(ids)
|
||||
if ids.is_a?(String)
|
||||
ids = [[ids]]
|
||||
elsif not ids.first.is_a?(Array) # if single comp key passed, turn into an array of 1
|
||||
ids = [ids.to_composite_ids]
|
||||
end
|
||||
|
||||
ids.map do |id_set|
|
||||
[primary_keys, id_set].transpose.map do |key, id|
|
||||
"#{table_name}.#{key.to_s}=#{sanitize(id)}"
|
||||
end.join(" AND ")
|
||||
end.join(") OR (")
|
||||
end
|
||||
|
||||
# Returns true if the given +ids+ represents the primary keys of a record in the database, false otherwise.
|
||||
# Example:
|
||||
# Person.exists?(5,7)
|
||||
def exists?(ids)
|
||||
if ids.is_a?(Array) && ids.first.is_a?(String)
|
||||
count(:conditions => ids) > 0
|
||||
else
|
||||
obj = find(ids) rescue false
|
||||
!obj.nil? and obj.is_a?(self)
|
||||
end
|
||||
end
|
||||
|
||||
# Deletes the record with the given +ids+ without instantiating an object first, e.g. delete(1,2)
|
||||
# If an array of ids is provided (e.g. delete([1,2], [3,4]), all of them
|
||||
# are deleted.
|
||||
def delete(*ids)
|
||||
unless ids.is_a?(Array); raise "*ids must be an Array"; end
|
||||
ids = [ids.to_composite_ids] if not ids.first.is_a?(Array)
|
||||
where_clause = ids.map do |id_set|
|
||||
[primary_keys, id_set].transpose.map do |key, id|
|
||||
"#{quoted_table_name}.#{connection.quote_column_name(key.to_s)}=#{sanitize(id)}"
|
||||
end.join(" AND ")
|
||||
end.join(") OR (")
|
||||
delete_all([ "(#{where_clause})" ])
|
||||
end
|
||||
|
||||
# Destroys the record with the given +ids+ by instantiating the object and calling #destroy (all the callbacks are the triggered).
|
||||
# If an array of ids is provided, all of them are destroyed.
|
||||
def destroy(*ids)
|
||||
unless ids.is_a?(Array); raise "*ids must be an Array"; end
|
||||
if ids.first.is_a?(Array)
|
||||
ids = ids.map{|compids| compids.to_composite_ids}
|
||||
else
|
||||
ids = ids.to_composite_ids
|
||||
end
|
||||
ids.first.is_a?(CompositeIds) ? ids.each { |id_set| find(id_set).destroy } : find(ids).destroy
|
||||
end
|
||||
|
||||
# Returns an array of column objects for the table associated with this class.
|
||||
# Each column that matches to one of the primary keys has its
|
||||
# primary attribute set to true
|
||||
def columns
|
||||
unless @columns
|
||||
@columns = connection.columns(table_name, "#{name} Columns")
|
||||
@columns.each {|column| column.primary = primary_keys.include?(column.name.to_sym)}
|
||||
end
|
||||
@columns
|
||||
end
|
||||
|
||||
## DEACTIVATED METHODS ##
|
||||
public
|
||||
# Lazy-set the sequence name to the connection's default. This method
|
||||
# is only ever called once since set_sequence_name overrides it.
|
||||
def sequence_name #:nodoc:
|
||||
raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
|
||||
end
|
||||
|
||||
def reset_sequence_name #:nodoc:
|
||||
raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
|
||||
end
|
||||
|
||||
def set_primary_key(value = nil, &block)
|
||||
raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
|
||||
end
|
||||
|
||||
private
|
||||
def find_one(id, options)
|
||||
raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
|
||||
end
|
||||
|
||||
def find_some(ids, options)
|
||||
raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
|
||||
end
|
||||
|
||||
def find_from_ids(ids, options)
|
||||
ids = ids.first if ids.last == nil
|
||||
conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
|
||||
# if ids is just a flat list, then its size must = primary_key.length (one id per primary key, in order)
|
||||
# if ids is list of lists, then each inner list must follow rule above
|
||||
if ids.first.is_a? String
|
||||
# find '2,1' -> ids = ['2,1']
|
||||
# find '2,1;7,3' -> ids = ['2,1;7,3']
|
||||
ids = ids.first.split(ID_SET_SEP).map {|id_set| id_set.split(ID_SEP).to_composite_ids}
|
||||
# find '2,1;7,3' -> ids = [['2','1'],['7','3']], inner [] are CompositeIds
|
||||
end
|
||||
ids = [ids.to_composite_ids] if not ids.first.kind_of?(Array)
|
||||
ids.each do |id_set|
|
||||
unless id_set.is_a?(Array)
|
||||
raise "Ids must be in an Array, instead received: #{id_set.inspect}"
|
||||
end
|
||||
unless id_set.length == primary_keys.length
|
||||
raise "#{id_set.inspect}: Incorrect number of primary keys for #{class_name}: #{primary_keys.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
# Let keys = [:a, :b]
|
||||
# If ids = [[10, 50], [11, 51]], then :conditions =>
|
||||
# "(#{quoted_table_name}.a, #{quoted_table_name}.b) IN ((10, 50), (11, 51))"
|
||||
|
||||
conditions = ids.map do |id_set|
|
||||
[primary_keys, id_set].transpose.map do |key, id|
|
||||
col = columns_hash[key.to_s]
|
||||
val = quote_value(id, col)
|
||||
"#{quoted_table_name}.#{connection.quote_column_name(key.to_s)}=#{val}"
|
||||
end.join(" AND ")
|
||||
end.join(") OR (")
|
||||
|
||||
options.update :conditions => "(#{conditions})"
|
||||
|
||||
result = find_every(options)
|
||||
|
||||
if result.size == ids.size
|
||||
ids.size == 1 ? result[0] : result
|
||||
else
|
||||
raise ::ActiveRecord::RecordNotFound, "Couldn't find all #{name.pluralize} with IDs (#{ids.inspect})#{conditions}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
module ActiveRecord
|
||||
ID_SEP = ','
|
||||
ID_SET_SEP = ';'
|
||||
|
||||
class Base
|
||||
# Allows +attr_name+ to be the list of primary_keys, and returns the id
|
||||
# of the object
|
||||
# e.g. @object[@object.class.primary_key] => [1,1]
|
||||
def [](attr_name)
|
||||
if attr_name.is_a?(String) and attr_name != attr_name.split(ID_SEP).first
|
||||
attr_name = attr_name.split(ID_SEP)
|
||||
end
|
||||
attr_name.is_a?(Array) ?
|
||||
attr_name.map {|name| read_attribute(name)} :
|
||||
read_attribute(attr_name)
|
||||
end
|
||||
|
||||
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
|
||||
# (Alias for the protected write_attribute method).
|
||||
def []=(attr_name, value)
|
||||
if attr_name.is_a?(String) and attr_name != attr_name.split(ID_SEP).first
|
||||
attr_name = attr_name.split(ID_SEP)
|
||||
end
|
||||
|
||||
if attr_name.is_a? Array
|
||||
value = value.split(ID_SEP) if value.is_a? String
|
||||
unless value.length == attr_name.length
|
||||
raise "Number of attr_names and values do not match"
|
||||
end
|
||||
#breakpoint
|
||||
[attr_name, value].transpose.map {|name,val| write_attribute(name.to_s, val)}
|
||||
else
|
||||
write_attribute(attr_name, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
69
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/calculations.rb
vendored
Normal file
69
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/calculations.rb
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
module CompositePrimaryKeys
|
||||
module ActiveRecord
|
||||
module Calculations
|
||||
def self.append_features(base)
|
||||
super
|
||||
base.send(:extend, ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def construct_calculation_sql(operation, column_name, options) #:nodoc:
|
||||
operation = operation.to_s.downcase
|
||||
options = options.symbolize_keys
|
||||
|
||||
scope = scope(:find)
|
||||
merged_includes = merge_includes(scope ? scope[:include] : [], options[:include])
|
||||
aggregate_alias = column_alias_for(operation, column_name)
|
||||
use_workaround = !connection.supports_count_distinct? && options[:distinct] && operation.to_s.downcase == 'count'
|
||||
join_dependency = nil
|
||||
|
||||
if merged_includes.any? && operation.to_s.downcase == 'count'
|
||||
options[:distinct] = true
|
||||
use_workaround = !connection.supports_count_distinct?
|
||||
column_name = options[:select] || primary_key.map{ |part| "#{quoted_table_name}.#{connection.quote_column_name(part)}"}.join(',')
|
||||
end
|
||||
|
||||
sql = "SELECT #{operation}(#{'DISTINCT ' if options[:distinct]}#{column_name}) AS #{aggregate_alias}"
|
||||
|
||||
# A (slower) workaround if we're using a backend, like sqlite, that doesn't support COUNT DISTINCT.
|
||||
sql = "SELECT COUNT(*) AS #{aggregate_alias}" if use_workaround
|
||||
|
||||
sql << ", #{connection.quote_column_name(options[:group_field])} AS #{options[:group_alias]}" if options[:group]
|
||||
sql << " FROM (SELECT DISTINCT #{column_name}" if use_workaround
|
||||
sql << " FROM #{quoted_table_name} "
|
||||
if merged_includes.any?
|
||||
join_dependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, options[:joins])
|
||||
sql << join_dependency.join_associations.collect{|join| join.association_join }.join
|
||||
end
|
||||
|
||||
add_joins!(sql, options[:joins], scope)
|
||||
add_conditions!(sql, options[:conditions], scope)
|
||||
add_limited_ids_condition!(sql, options, join_dependency) if \
|
||||
join_dependency &&
|
||||
!using_limitable_reflections?(join_dependency.reflections) &&
|
||||
((scope && scope[:limit]) || options[:limit])
|
||||
|
||||
if options[:group]
|
||||
group_key = connection.adapter_name == 'FrontBase' ? :group_alias : :group_field
|
||||
sql << " GROUP BY #{connection.quote_column_name(options[group_key])} "
|
||||
end
|
||||
|
||||
if options[:group] && options[:having]
|
||||
# FrontBase requires identifiers in the HAVING clause and chokes on function calls
|
||||
if connection.adapter_name == 'FrontBase'
|
||||
options[:having].downcase!
|
||||
options[:having].gsub!(/#{operation}\s*\(\s*#{column_name}\s*\)/, aggregate_alias)
|
||||
end
|
||||
|
||||
sql << " HAVING #{options[:having]} "
|
||||
end
|
||||
|
||||
sql << " ORDER BY #{options[:order]} " if options[:order]
|
||||
add_limit!(sql, options, scope)
|
||||
sql << ') w1' if use_workaround # assign a dummy table name as required for postgresql
|
||||
sql
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
30
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/composite_arrays.rb
vendored
Normal file
30
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/composite_arrays.rb
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
module CompositePrimaryKeys
|
||||
ID_SEP = ','
|
||||
ID_SET_SEP = ';'
|
||||
|
||||
module ArrayExtension
|
||||
def to_composite_keys
|
||||
CompositeKeys.new(self)
|
||||
end
|
||||
|
||||
def to_composite_ids
|
||||
CompositeIds.new(self)
|
||||
end
|
||||
end
|
||||
|
||||
class CompositeArray < Array
|
||||
def to_s
|
||||
join(ID_SEP)
|
||||
end
|
||||
end
|
||||
|
||||
class CompositeKeys < CompositeArray
|
||||
|
||||
end
|
||||
|
||||
class CompositeIds < CompositeArray
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
Array.send(:include, CompositePrimaryKeys::ArrayExtension)
|
|
@ -0,0 +1,21 @@
|
|||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
class IBM_DBAdapter < AbstractAdapter
|
||||
|
||||
# This mightn't be in Core, but count(distinct x,y) doesn't work for me
|
||||
def supports_count_distinct? #:nodoc:
|
||||
false
|
||||
end
|
||||
|
||||
alias_method :quote_original, :quote
|
||||
def quote(value, column = nil)
|
||||
if value.kind_of?(String) && column && [:integer, :float].include?(column.type)
|
||||
value = column.type == :integer ? value.to_i : value.to_f
|
||||
value.to_s
|
||||
else
|
||||
quote_original(value, column)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
class OracleAdapter < AbstractAdapter
|
||||
|
||||
# This mightn't be in Core, but count(distinct x,y) doesn't work for me
|
||||
def supports_count_distinct? #:nodoc:
|
||||
false
|
||||
end
|
||||
|
||||
def concat(*columns)
|
||||
"(#{columns.join('||')})"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,53 @@
|
|||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
class PostgreSQLAdapter < AbstractAdapter
|
||||
|
||||
# This mightn't be in Core, but count(distinct x,y) doesn't work for me
|
||||
def supports_count_distinct? #:nodoc:
|
||||
false
|
||||
end
|
||||
|
||||
def concat(*columns)
|
||||
columns = columns.map { |c| "CAST(#{c} AS varchar)" }
|
||||
"(#{columns.join('||')})"
|
||||
end
|
||||
|
||||
# Executes an INSERT query and returns the new record's ID
|
||||
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
||||
# Extract the table from the insert sql. Yuck.
|
||||
table = sql.split(" ", 4)[2].gsub('"', '')
|
||||
|
||||
# Try an insert with 'returning id' if available (PG >= 8.2)
|
||||
if supports_insert_with_returning?
|
||||
pk, sequence_name = *pk_and_sequence_for(table) unless pk
|
||||
if pk
|
||||
quoted_pk = if pk.is_a?(Array)
|
||||
pk.map { |col| quote_column_name(col) }.join(ID_SEP)
|
||||
else
|
||||
quote_column_name(pk)
|
||||
end
|
||||
id = select_value("#{sql} RETURNING #{quoted_pk}")
|
||||
clear_query_cache
|
||||
return id
|
||||
end
|
||||
end
|
||||
|
||||
# Otherwise, insert then grab last_insert_id.
|
||||
if insert_id = super
|
||||
insert_id
|
||||
else
|
||||
# If neither pk nor sequence name is given, look them up.
|
||||
unless pk || sequence_name
|
||||
pk, sequence_name = *pk_and_sequence_for(table)
|
||||
end
|
||||
|
||||
# If a pk is given, fallback to default sequence name.
|
||||
# Don't fetch last insert id for a table without a pk.
|
||||
if pk && sequence_name ||= default_sequence_name(table, pk)
|
||||
last_insert_id(table, sequence_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
require 'active_record/connection_adapters/sqlite_adapter'
|
||||
|
||||
module ActiveRecord
|
||||
module ConnectionAdapters #:nodoc:
|
||||
class SQLite3Adapter < SQLiteAdapter # :nodoc:
|
||||
def supports_count_distinct? #:nodoc:
|
||||
false
|
||||
end
|
||||
|
||||
def concat(*columns)
|
||||
"(#{columns.join('||')})"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
8
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/fixtures.rb
vendored
Normal file
8
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/fixtures.rb
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
class Fixture #:nodoc:
|
||||
def [](key)
|
||||
if key.is_a? Array
|
||||
return key.map { |a_key| self[a_key.to_s] }.to_composite_ids.to_s
|
||||
end
|
||||
@fixture[key]
|
||||
end
|
||||
end
|
20
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/migration.rb
vendored
Normal file
20
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/migration.rb
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
ActiveRecord::ConnectionAdapters::ColumnDefinition.send(:alias_method, :to_s_without_composite_keys, :to_s)
|
||||
|
||||
ActiveRecord::ConnectionAdapters::ColumnDefinition.class_eval <<-'EOF'
|
||||
def to_s
|
||||
if name.is_a? Array
|
||||
"PRIMARY KEY (#{name.join(',')})"
|
||||
else
|
||||
to_s_without_composite_keys
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
ActiveRecord::ConnectionAdapters::TableDefinition.class_eval <<-'EOF'
|
||||
def [](name)
|
||||
@columns.find { |column|
|
||||
!column.name.is_a?(Array) && column.name.to_s == name.to_s
|
||||
}
|
||||
end
|
||||
EOF
|
||||
|
19
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/reflection.rb
vendored
Normal file
19
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/reflection.rb
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
module ActiveRecord
|
||||
module Reflection
|
||||
class AssociationReflection
|
||||
def primary_key_name
|
||||
return @primary_key_name if @primary_key_name
|
||||
case
|
||||
when macro == :belongs_to
|
||||
@primary_key_name = options[:foreign_key] || class_name.foreign_key
|
||||
when options[:as]
|
||||
@primary_key_name = options[:foreign_key] || "#{options[:as]}_id"
|
||||
else
|
||||
@primary_key_name = options[:foreign_key] || active_record.name.foreign_key
|
||||
end
|
||||
@primary_key_name = @primary_key_name.to_composite_keys.to_s if @primary_key_name.is_a? Array
|
||||
@primary_key_name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
8
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/version.rb
vendored
Normal file
8
vendor/gems/composite_primary_keys-2.2.2/lib/composite_primary_keys/version.rb
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
module CompositePrimaryKeys
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 2
|
||||
TINY = 2
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue