composite_primary_keys 3.1.8 → 3.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +16 -12
- data/Rakefile +2 -4
- data/lib/composite_primary_keys/version.rb +1 -1
- data/test/connections/native_mysql/connection.rb +1 -1
- data/test/connections/native_oracle/connection.rb +1 -1
- data/test/connections/native_oracle_enhanced/connection.rb +1 -1
- data/test/connections/native_postgresql/connection.rb +1 -1
- data/test/connections/native_sqlite/connection.rb +1 -1
- data/test/fixtures/article.rb +1 -1
- data/test/fixtures/comment.rb +0 -1
- data/test/fixtures/db_definitions/db2-create-tables.sql +4 -13
- data/test/fixtures/db_definitions/db2-drop-tables.sql +1 -1
- data/test/fixtures/db_definitions/mysql.sql +5 -13
- data/test/fixtures/db_definitions/oracle.drop.sql +1 -2
- data/test/fixtures/db_definitions/oracle.sql +4 -13
- data/test/fixtures/db_definitions/postgresql.sql +5 -13
- data/test/fixtures/db_definitions/sqlite.sql +4 -13
- data/test/fixtures/department.rb +1 -0
- data/test/fixtures/departments.yml +5 -1
- data/test/fixtures/employees.yml +12 -2
- data/test/fixtures/membership.rb +0 -2
- data/test/fixtures/product.rb +4 -2
- data/test/fixtures/restaurant.rb +4 -1
- data/test/fixtures/restaurants.yml +8 -1
- data/test/fixtures/tariff.rb +0 -1
- data/test/fixtures/tariffs.yml +2 -0
- data/test/test_associations.rb +6 -23
- data/test/test_attribute_methods.rb +5 -6
- data/test/test_delete.rb +35 -2
- data/test/test_habtm.rb +42 -0
- data/test/test_ids.rb +0 -8
- data/test/test_suite.rb +1 -0
- metadata +6 -23
- data/lib/composite_primary_keys/associations/association.rb +0 -23
- data/lib/composite_primary_keys/associations/association_scope.rb +0 -67
- data/lib/composite_primary_keys/associations/join_dependency/join_association.rb +0 -22
- data/lib/composite_primary_keys/associations/join_dependency/join_part.rb +0 -39
- data/lib/composite_primary_keys/associations/preloader/association.rb +0 -61
- data/lib/composite_primary_keys/associations/preloader/belongs_to.rb +0 -13
- data/lib/composite_primary_keys/associations/preloader/has_and_belongs_to_many.rb +0 -46
- data/lib/composite_primary_keys/attribute_methods/primary_key.rb +0 -19
- data/lib/composite_primary_keys/attribute_methods/read.rb +0 -88
- data/lib/composite_primary_keys/attribute_methods/write.rb +0 -40
- data/lib/composite_primary_keys/composite_predicates.rb +0 -44
- data/lib/composite_primary_keys/named_scope.rb +0 -29
- data/lib/composite_primary_keys/relation/finder_methods.rb +0 -119
- data/lib/composite_primary_keys/relation/query_methods.rb +0 -24
- data/test/fixtures/article_group.rb +0 -4
- data/test/fixtures/article_groups.yml +0 -7
- data/test/fixtures/kitchen_sink.rb +0 -3
- data/test/fixtures/kitchen_sinks.yml +0 -5
- data/test/fixtures/way_node.rb +0 -3
@@ -1,46 +0,0 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module Associations
|
3
|
-
class Preloader
|
4
|
-
class HasAndBelongsToMany
|
5
|
-
def records_for(ids)
|
6
|
-
# CPK
|
7
|
-
#scope = super
|
8
|
-
predicate = cpk_in_predicate(join_table, reflection.foreign_key, ids)
|
9
|
-
scope = scoped.where(predicate)
|
10
|
-
|
11
|
-
klass.connection.select_all(scope.arel.to_sql, 'SQL', scope.bind_values)
|
12
|
-
end
|
13
|
-
|
14
|
-
def join
|
15
|
-
# CPK
|
16
|
-
#condition = table[reflection.association_primary_key].eq(
|
17
|
-
# join_table[reflection.association_foreign_key])
|
18
|
-
condition = cpk_join_predicate(table, reflection.association_primary_key,
|
19
|
-
join_table, reflection.association_foreign_key)
|
20
|
-
|
21
|
-
table.create_join(join_table, table.create_on(condition))
|
22
|
-
end
|
23
|
-
|
24
|
-
def association_key_alias(field)
|
25
|
-
"ar_association_key_name_#{field.to_s}"
|
26
|
-
end
|
27
|
-
|
28
|
-
def join_select
|
29
|
-
# CPK
|
30
|
-
# association_key.as(Arel.sql(association_key_name))
|
31
|
-
Array(reflection.foreign_key).map do |key|
|
32
|
-
join_table[key].as(Arel.sql(association_key_alias(key)))
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def association_key_name
|
37
|
-
# CPK
|
38
|
-
# 'ar_association_key_name'
|
39
|
-
Array(reflection.foreign_key).map do |key|
|
40
|
-
association_key_alias(key)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module AttributeMethods #:nodoc:
|
3
|
-
module PrimaryKey
|
4
|
-
def to_key
|
5
|
-
# CPK
|
6
|
-
#key = send(self.class.primary_key)
|
7
|
-
#[key] if key
|
8
|
-
|
9
|
-
primary_key = self.class.primary_key
|
10
|
-
if primary_key.is_a?(Array)
|
11
|
-
primary_key.collect{|k| send(k)}
|
12
|
-
else
|
13
|
-
key = send(primary_key)
|
14
|
-
[key] if key
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,88 +0,0 @@
|
|
1
|
-
module CompositePrimaryKeys
|
2
|
-
module ActiveRecord
|
3
|
-
module AttributeMethods
|
4
|
-
module Read
|
5
|
-
def self.included(base)
|
6
|
-
base.send(:extend, ClassMethods)
|
7
|
-
alias [] read_attribute
|
8
|
-
end
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
def define_read_method(method_name, attr_name, column)
|
12
|
-
cast_code = column.type_cast_code('v')
|
13
|
-
# CPK - this is a really horrid hack, needed to get
|
14
|
-
# right class namespace :(
|
15
|
-
if cast_code.match(/^ActiveRecord/)
|
16
|
-
cast_code = "::#{cast_code}"
|
17
|
-
end
|
18
|
-
|
19
|
-
access_code = "(v=@attributes['#{attr_name}']) && #{cast_code}"
|
20
|
-
|
21
|
-
# CPK
|
22
|
-
# unless attr_name.to_s == self.primary_key.to_s
|
23
|
-
# access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
|
24
|
-
# end
|
25
|
-
unless self.primary_keys.include?(attr_name.to_sym)
|
26
|
-
access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
|
27
|
-
end
|
28
|
-
|
29
|
-
if cache_attribute?(attr_name)
|
30
|
-
access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
|
31
|
-
end
|
32
|
-
|
33
|
-
# Where possible, generate the method by evalling a string, as this will result in
|
34
|
-
# faster accesses because it avoids the block eval and then string eval incurred
|
35
|
-
# by the second branch.
|
36
|
-
#
|
37
|
-
# The second, slower, branch is necessary to support instances where the database
|
38
|
-
# returns columns with extra stuff in (like 'my_column(omg)').
|
39
|
-
if method_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP
|
40
|
-
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__
|
41
|
-
def _#{method_name}
|
42
|
-
#{access_code}
|
43
|
-
end
|
44
|
-
|
45
|
-
alias #{method_name} _#{method_name}
|
46
|
-
STR
|
47
|
-
else
|
48
|
-
generated_attribute_methods.module_eval do
|
49
|
-
define_method("_#{method_name}") { eval(access_code) }
|
50
|
-
alias_method(method_name, "_#{method_name}")
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def read_attribute(attr_name)
|
57
|
-
# CPK
|
58
|
-
if attr_name.kind_of?(Array)
|
59
|
-
attr_name.map {|name| read_attribute(name)}.to_composite_keys
|
60
|
-
elsif respond_to? "_#{attr_name}"
|
61
|
-
send "_#{attr_name}" if @attributes.has_key?(attr_name.to_s)
|
62
|
-
else
|
63
|
-
_read_attribute attr_name
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def _read_attribute(attr_name)
|
68
|
-
attr_name = attr_name.to_s
|
69
|
-
# CPK
|
70
|
-
# attr_name = self.class.primary_key if attr_name == 'id'
|
71
|
-
attr_name = self.class.primary_key if (attr_name == 'id' and !self.composite?)
|
72
|
-
value = @attributes[attr_name]
|
73
|
-
unless value.nil?
|
74
|
-
if column = column_for_attribute(attr_name)
|
75
|
-
if unserializable_attribute?(attr_name, column)
|
76
|
-
unserialize_attribute(attr_name)
|
77
|
-
else
|
78
|
-
column.type_cast(value)
|
79
|
-
end
|
80
|
-
else
|
81
|
-
value
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
module CompositePrimaryKeys
|
2
|
-
module ActiveRecord
|
3
|
-
module AttributeMethods
|
4
|
-
module Write
|
5
|
-
def self.included(base)
|
6
|
-
alias []= write_attribute
|
7
|
-
alias_method :raw_write_attribute, :write_attribute
|
8
|
-
end
|
9
|
-
|
10
|
-
def write_attribute(attr_name, value)
|
11
|
-
if attr_name.kind_of?(Array)
|
12
|
-
unless value.length == attr_name.length
|
13
|
-
raise "Number of attr_names and values do not match"
|
14
|
-
end
|
15
|
-
[attr_name, value].transpose.map {|name,val| _write_attribute(name, val)}
|
16
|
-
value
|
17
|
-
else
|
18
|
-
_write_attribute(attr_name, value)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def _write_attribute(attr_name, value)
|
23
|
-
attr_name = attr_name.to_s
|
24
|
-
# CPK
|
25
|
-
# attr_name = self.class.primary_key if attr_name == 'id'
|
26
|
-
attr_name = self.class.primary_key if (attr_name == 'id' and !self.composite?)
|
27
|
-
@attributes_cache.delete(attr_name)
|
28
|
-
if (column = column_for_attribute(attr_name)) && column.number?
|
29
|
-
@attributes[attr_name] = convert_number_column_value(value)
|
30
|
-
else
|
31
|
-
@attributes[attr_name] = value
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
module CompositePrimaryKeys
|
2
|
-
module Predicates
|
3
|
-
def cpk_or_predicate(predicates)
|
4
|
-
result = predicates.shift
|
5
|
-
predicates.each do |predicate|
|
6
|
-
result = result.or(predicate)
|
7
|
-
end
|
8
|
-
result
|
9
|
-
end
|
10
|
-
|
11
|
-
def cpk_id_predicate(table, keys, values)
|
12
|
-
eq_predicates = keys.zip(values).map do |key, value|
|
13
|
-
table[key].eq(value)
|
14
|
-
end
|
15
|
-
Arel::Nodes::And.new(eq_predicates)
|
16
|
-
end
|
17
|
-
|
18
|
-
def cpk_join_predicate(table1, key1, table2, key2)
|
19
|
-
key1_fields = Array(key1).map {|key| table1[key]}
|
20
|
-
key2_fields = Array(key2).map {|key| table2[key]}
|
21
|
-
|
22
|
-
predicates = key1_fields.zip(key2_fields).map do |key_field1, key_field2|
|
23
|
-
key_field1.eq(key_field2)
|
24
|
-
end
|
25
|
-
Arel::Nodes::And.new(predicates)
|
26
|
-
end
|
27
|
-
|
28
|
-
def cpk_in_predicate(table, primary_keys, ids)
|
29
|
-
and_predicates = ids.map do |id_set|
|
30
|
-
eq_predicates = Array(primary_keys).zip(id_set).map do |primary_key, value|
|
31
|
-
table[primary_key].eq(value)
|
32
|
-
end
|
33
|
-
Arel::Nodes::And.new(eq_predicates)
|
34
|
-
end
|
35
|
-
|
36
|
-
cpk_or_predicate(and_predicates)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
ActiveRecord::Associations::AssociationScope.send(:include, CompositePrimaryKeys::Predicates)
|
42
|
-
ActiveRecord::Associations::JoinDependency::JoinAssociation.send(:include, CompositePrimaryKeys::Predicates)
|
43
|
-
ActiveRecord::Associations::Preloader::Association.send(:include, CompositePrimaryKeys::Predicates)
|
44
|
-
ActiveRecord::Relation.send(:include, CompositePrimaryKeys::Predicates)
|
@@ -1,29 +0,0 @@
|
|
1
|
-
module CompositePrimaryKeys
|
2
|
-
module ActiveRecord
|
3
|
-
module NamedScope
|
4
|
-
module ClassMethods
|
5
|
-
def scoped(options = nil)
|
6
|
-
result = if options
|
7
|
-
scoped.apply_finder_options(options)
|
8
|
-
else
|
9
|
-
if current_scope
|
10
|
-
current_scope.clone
|
11
|
-
else
|
12
|
-
scope = relation.clone
|
13
|
-
scope.default_scoped = true
|
14
|
-
scope
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
# CPK
|
19
|
-
class << result
|
20
|
-
include CompositePrimaryKeys::ActiveRecord::FinderMethods
|
21
|
-
include CompositePrimaryKeys::ActiveRecord::QueryMethods
|
22
|
-
include CompositePrimaryKeys::ActiveRecord::Relation
|
23
|
-
end
|
24
|
-
result
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,119 +0,0 @@
|
|
1
|
-
module CompositePrimaryKeys
|
2
|
-
module ActiveRecord
|
3
|
-
module FinderMethods
|
4
|
-
def construct_limited_ids_condition(relation)
|
5
|
-
orders = relation.order_values
|
6
|
-
# CPK
|
7
|
-
# values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders)
|
8
|
-
keys = @klass.primary_keys.map do |key|
|
9
|
-
"#{@klass.connection.quote_table_name @klass.table_name}.#{key}"
|
10
|
-
end
|
11
|
-
values = @klass.connection.distinct(keys.join(', '), orders)
|
12
|
-
|
13
|
-
relation = relation.dup
|
14
|
-
|
15
|
-
ids_array = relation.select(values).collect {|row| row[primary_key]}
|
16
|
-
# CPK
|
17
|
-
# ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array)
|
18
|
-
|
19
|
-
# OR together each and expression (key=value and key=value) that matches an id set
|
20
|
-
# since we only need to match 0 or more records
|
21
|
-
or_expressions = ids_array.map do |id_set|
|
22
|
-
# AND together "key=value" exprssios to match each id set
|
23
|
-
and_expressions = [self.primary_keys, id_set].transpose.map do |key, id|
|
24
|
-
table[key].eq(id)
|
25
|
-
end
|
26
|
-
Arel::Nodes::And.new(and_expressions)
|
27
|
-
end
|
28
|
-
|
29
|
-
first = or_expressions.shift
|
30
|
-
Arel::Nodes::Grouping.new(or_expressions.inject(first) do |memo, expr|
|
31
|
-
Arel::Nodes::Or.new(memo, expr)
|
32
|
-
end)
|
33
|
-
end
|
34
|
-
|
35
|
-
def exists?(id = nil)
|
36
|
-
# ID can be:
|
37
|
-
# Array - ['department_id = ? and location_id = ?', 1, 1]
|
38
|
-
# Array -> [1,2]
|
39
|
-
# CompositeKeys -> [1,2]
|
40
|
-
|
41
|
-
id = id.id if ::ActiveRecord::Base === id
|
42
|
-
|
43
|
-
join_dependency = construct_join_dependency_for_association_find
|
44
|
-
relation = construct_relation_for_association_find(join_dependency)
|
45
|
-
relation = relation.except(:select).select("1").limit(1)
|
46
|
-
|
47
|
-
# CPK
|
48
|
-
#case id
|
49
|
-
#when Array, Hash
|
50
|
-
# relation = relation.where(id)
|
51
|
-
#else
|
52
|
-
# relation = relation.where(table[primary_key].eq(id)) if id
|
53
|
-
#end
|
54
|
-
|
55
|
-
case id
|
56
|
-
when CompositePrimaryKeys::CompositeKeys
|
57
|
-
relation = relation.where(cpk_id_predicate(table, primary_key, id))
|
58
|
-
when Array
|
59
|
-
if !id.first.kind_of?(String)
|
60
|
-
return self.exists?(id.to_composite_keys)
|
61
|
-
else
|
62
|
-
relation = relation.where(id)
|
63
|
-
end
|
64
|
-
when Hash
|
65
|
-
relation = relation.where(id)
|
66
|
-
else
|
67
|
-
raise(ArgumentError, "Unsupported id sent to exists?")
|
68
|
-
end
|
69
|
-
connection.select_value(relation.to_sql) ? true : false
|
70
|
-
end
|
71
|
-
|
72
|
-
def find_with_ids(*ids, &block)
|
73
|
-
return to_a.find { |*block_args| yield(*block_args) } if block_given?
|
74
|
-
|
75
|
-
# Supports:
|
76
|
-
# find('1,2') -> ['1,2']
|
77
|
-
# find(1,2) -> [1,2]
|
78
|
-
# find([1,2]) -> [['1,2']]
|
79
|
-
# find([1,2], [3,4]) -> [[1,2],[3,4]]
|
80
|
-
#
|
81
|
-
# Does *not* support:
|
82
|
-
# find('1,2', '3,4') -> ['1,2','3,4']
|
83
|
-
|
84
|
-
# Normalize incoming data. Note the last arg can be nil. Happens
|
85
|
-
# when find is called with nil options like the reload method does.
|
86
|
-
ids.compact!
|
87
|
-
ids = [ids] unless ids.first.kind_of?(Array)
|
88
|
-
|
89
|
-
results = ids.map do |cpk_ids|
|
90
|
-
cpk_ids = if cpk_ids.length == 1
|
91
|
-
cpk_ids.first.split(CompositePrimaryKeys::ID_SEP).to_composite_keys
|
92
|
-
else
|
93
|
-
cpk_ids.to_composite_keys
|
94
|
-
end
|
95
|
-
|
96
|
-
unless cpk_ids.length == @klass.primary_keys.length
|
97
|
-
raise "#{cpk_ids.inspect}: Incorrect number of primary keys for #{@klass.name}: #{@klass.primary_keys.inspect}"
|
98
|
-
end
|
99
|
-
|
100
|
-
new_relation = clone
|
101
|
-
[@klass.primary_keys, cpk_ids].transpose.map do |key, id|
|
102
|
-
new_relation = new_relation.where(key => id)
|
103
|
-
end
|
104
|
-
|
105
|
-
records = new_relation.to_a
|
106
|
-
|
107
|
-
if records.empty?
|
108
|
-
conditions = new_relation.arel.where_sql
|
109
|
-
raise(::ActiveRecord::RecordNotFound,
|
110
|
-
"Couldn't find #{@klass.name} with ID=#{cpk_ids} #{conditions}")
|
111
|
-
end
|
112
|
-
records
|
113
|
-
end.flatten
|
114
|
-
|
115
|
-
ids.length == 1 ? results.first : results
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
module CompositePrimaryKeys
|
2
|
-
module ActiveRecord
|
3
|
-
module QueryMethods
|
4
|
-
def reverse_order
|
5
|
-
order_clause = arel.order_clauses
|
6
|
-
|
7
|
-
# CPK
|
8
|
-
# order = order_clause.empty? ?
|
9
|
-
# "#{table_name}.#{primary_key} DESC" :
|
10
|
-
# reverse_sql_order(order_clause).join(', ')
|
11
|
-
|
12
|
-
order = unless order_clause.empty?
|
13
|
-
reverse_sql_order(order_clause).join(', ')
|
14
|
-
else
|
15
|
-
klass.primary_key.map do |key|
|
16
|
-
"#{table_name}.#{key} DESC"
|
17
|
-
end.join(", ")
|
18
|
-
end
|
19
|
-
|
20
|
-
except(:order).order(Arel.sql(order))
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|