composite_primary_keys 12.0.2 → 12.0.10
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.
- checksums.yaml +4 -4
- data/History.rdoc +880 -841
- data/README.rdoc +180 -179
- data/lib/composite_primary_keys/active_model/attribute_assignment.rb +19 -0
- data/lib/composite_primary_keys/arel/sqlserver.rb +1 -3
- data/lib/composite_primary_keys/associations/association_scope.rb +68 -68
- data/lib/composite_primary_keys/associations/join_dependency.rb +103 -103
- data/lib/composite_primary_keys/associations/through_association.rb +2 -1
- data/lib/composite_primary_keys/attribute_methods/primary_key.rb +13 -0
- data/lib/composite_primary_keys/attribute_methods/read.rb +30 -30
- data/lib/composite_primary_keys/attribute_methods/write.rb +35 -35
- data/lib/composite_primary_keys/attribute_methods.rb +9 -9
- data/lib/composite_primary_keys/base.rb +141 -130
- data/lib/composite_primary_keys/composite_arrays.rb +0 -8
- data/lib/composite_primary_keys/connection_adapters/abstract/database_statements.rb +37 -17
- data/lib/composite_primary_keys/connection_adapters/sqlserver/database_statements.rb +44 -23
- data/lib/composite_primary_keys/core.rb +48 -48
- data/lib/composite_primary_keys/persistence.rb +82 -81
- data/lib/composite_primary_keys/reflection.rb +29 -29
- data/lib/composite_primary_keys/relation/batches.rb +1 -1
- data/lib/composite_primary_keys/relation/calculations.rb +81 -81
- data/lib/composite_primary_keys/relation/finder_methods.rb +235 -235
- data/lib/composite_primary_keys/relation/predicate_builder/association_query_value.rb +20 -20
- data/lib/composite_primary_keys/relation/query_methods.rb +42 -42
- data/lib/composite_primary_keys/relation/where_clause.rb +23 -23
- data/lib/composite_primary_keys/relation.rb +193 -118
- data/lib/composite_primary_keys/version.rb +8 -8
- data/lib/composite_primary_keys.rb +117 -118
- data/test/abstract_unit.rb +114 -113
- data/test/connections/databases.ci.yml +22 -19
- data/test/fixtures/article.rb +4 -0
- data/test/fixtures/articles.yml +4 -3
- data/test/fixtures/comment.rb +1 -3
- data/test/fixtures/comments.yml +10 -9
- data/test/fixtures/db_definitions/db2-create-tables.sql +112 -126
- data/test/fixtures/db_definitions/db2-drop-tables.sql +17 -19
- data/test/fixtures/db_definitions/mysql.sql +180 -217
- data/test/fixtures/db_definitions/oracle.drop.sql +42 -48
- data/test/fixtures/db_definitions/oracle.sql +200 -236
- data/test/fixtures/db_definitions/postgresql.sql +183 -220
- data/test/fixtures/db_definitions/sqlite.sql +170 -206
- data/test/fixtures/db_definitions/sqlserver.sql +176 -212
- data/test/fixtures/department.rb +16 -11
- data/test/fixtures/departments.yml +15 -15
- data/test/fixtures/employees.yml +27 -27
- data/test/fixtures/readings.yml +2 -2
- data/test/fixtures/restaurants_suburbs.yml +11 -11
- data/test/fixtures/streets.yml +16 -16
- data/test/fixtures/suburbs.yml +14 -14
- data/test/fixtures/user.rb +11 -10
- data/test/test_associations.rb +358 -351
- data/test/test_attributes.rb +60 -60
- data/test/test_calculations.rb +42 -42
- data/test/test_create.rb +218 -183
- data/test/test_delete.rb +182 -179
- data/test/test_exists.rb +39 -39
- data/test/test_find.rb +164 -145
- data/test/test_habtm.rb +2 -2
- data/test/test_ids.rb +112 -116
- data/test/test_nested_attributes.rb +67 -124
- data/test/test_polymorphic.rb +29 -13
- data/test/test_preload.rb +4 -3
- data/test/test_serialize.rb +2 -2
- data/test/test_update.rb +96 -78
- metadata +4 -19
- data/test/fixtures/hack.rb +0 -5
- data/test/fixtures/hacks.yml +0 -3
- data/test/fixtures/pk_called_id.rb +0 -5
- data/test/fixtures/pk_called_ids.yml +0 -11
- data/test/fixtures/reference_code_using_composite_key_alias.rb +0 -8
- data/test/fixtures/reference_code_using_simple_key_alias.rb +0 -8
- data/test/fixtures/seat.rb +0 -5
- data/test/fixtures/seats.yml +0 -9
- data/test/fixtures/topic.rb +0 -6
- data/test/fixtures/topic_source.rb +0 -7
- data/test/test_aliases.rb +0 -18
- data/test/test_enum.rb +0 -21
- data/test/test_suite.rb +0 -35
@@ -1,103 +1,103 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module Associations
|
3
|
-
class JoinDependency
|
4
|
-
class Aliases # :nodoc:
|
5
|
-
def column_alias(node, column)
|
6
|
-
# CPK
|
7
|
-
#@alias_cache[node][column]
|
8
|
-
if column.kind_of?(Array)
|
9
|
-
column.map do |a_column|
|
10
|
-
@alias_cache[node][a_column]
|
11
|
-
end
|
12
|
-
else
|
13
|
-
@alias_cache[node][column]
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def instantiate(result_set, &block)
|
19
|
-
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
20
|
-
|
21
|
-
seen = Hash.new { |i, object_id|
|
22
|
-
i[object_id] = Hash.new { |j, child_class|
|
23
|
-
j[child_class] = {}
|
24
|
-
}
|
25
|
-
}
|
26
|
-
|
27
|
-
model_cache = Hash.new { |h, klass| h[klass] = {} }
|
28
|
-
parents = model_cache[join_root]
|
29
|
-
column_aliases = aliases.column_aliases join_root
|
30
|
-
|
31
|
-
message_bus = ActiveSupport::Notifications.instrumenter
|
32
|
-
|
33
|
-
payload = {
|
34
|
-
record_count: result_set.length,
|
35
|
-
class_name: join_root.base_klass.name
|
36
|
-
}
|
37
|
-
|
38
|
-
message_bus.instrument("instantiation.active_record", payload) do
|
39
|
-
result_set.each { |row_hash|
|
40
|
-
# CPK
|
41
|
-
# parent_key = primary_key ? row_hash[primary_key] : row_hash
|
42
|
-
# CPK
|
43
|
-
parent_key = if primary_key.kind_of?(Array)
|
44
|
-
primary_key.map {|key| row_hash[key]}
|
45
|
-
else
|
46
|
-
primary_key ? row_hash[primary_key] : row_hash
|
47
|
-
end
|
48
|
-
|
49
|
-
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
|
50
|
-
construct(parent, join_root, row_hash, seen, model_cache)
|
51
|
-
}
|
52
|
-
end
|
53
|
-
|
54
|
-
parents.values
|
55
|
-
end
|
56
|
-
|
57
|
-
def construct(ar_parent, parent, row, seen, model_cache)
|
58
|
-
return if ar_parent.nil?
|
59
|
-
|
60
|
-
parent.children.each do |node|
|
61
|
-
if node.reflection.collection?
|
62
|
-
other = ar_parent.association(node.reflection.name)
|
63
|
-
other.loaded!
|
64
|
-
elsif ar_parent.association_cached?(node.reflection.name)
|
65
|
-
model = ar_parent.association(node.reflection.name).target
|
66
|
-
construct(model, node, row, seen, model_cache)
|
67
|
-
next
|
68
|
-
end
|
69
|
-
|
70
|
-
key = aliases.column_alias(node, node.primary_key)
|
71
|
-
|
72
|
-
# CPK
|
73
|
-
if key.is_a?(Array)
|
74
|
-
id = Array(key).map do |column_alias|
|
75
|
-
row[column_alias]
|
76
|
-
end
|
77
|
-
# At least the first value in the key has to be set. Should we require all values to be set?
|
78
|
-
id = nil if id.first.nil?
|
79
|
-
else # original
|
80
|
-
id = row[key]
|
81
|
-
end
|
82
|
-
|
83
|
-
if id.nil? # duplicating this so it is clear what remained unchanged from the original
|
84
|
-
nil_association = ar_parent.association(node.reflection.name)
|
85
|
-
nil_association.loaded!
|
86
|
-
next
|
87
|
-
end
|
88
|
-
|
89
|
-
model = seen[ar_parent.object_id][node][id]
|
90
|
-
|
91
|
-
if model
|
92
|
-
construct(model, node, row, seen, model_cache)
|
93
|
-
else
|
94
|
-
model = construct_model(ar_parent, node, row, model_cache, id)
|
95
|
-
|
96
|
-
seen[ar_parent.object_id][node][id] = model
|
97
|
-
construct(model, node, row, seen, model_cache)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class JoinDependency
|
4
|
+
class Aliases # :nodoc:
|
5
|
+
def column_alias(node, column)
|
6
|
+
# CPK
|
7
|
+
#@alias_cache[node][column]
|
8
|
+
if column.kind_of?(Array)
|
9
|
+
column.map do |a_column|
|
10
|
+
@alias_cache[node][a_column]
|
11
|
+
end
|
12
|
+
else
|
13
|
+
@alias_cache[node][column]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def instantiate(result_set, &block)
|
19
|
+
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
20
|
+
|
21
|
+
seen = Hash.new { |i, object_id|
|
22
|
+
i[object_id] = Hash.new { |j, child_class|
|
23
|
+
j[child_class] = {}
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
model_cache = Hash.new { |h, klass| h[klass] = {} }
|
28
|
+
parents = model_cache[join_root]
|
29
|
+
column_aliases = aliases.column_aliases join_root
|
30
|
+
|
31
|
+
message_bus = ActiveSupport::Notifications.instrumenter
|
32
|
+
|
33
|
+
payload = {
|
34
|
+
record_count: result_set.length,
|
35
|
+
class_name: join_root.base_klass.name
|
36
|
+
}
|
37
|
+
|
38
|
+
message_bus.instrument("instantiation.active_record", payload) do
|
39
|
+
result_set.each { |row_hash|
|
40
|
+
# CPK
|
41
|
+
# parent_key = primary_key ? row_hash[primary_key] : row_hash
|
42
|
+
# CPK
|
43
|
+
parent_key = if primary_key.kind_of?(Array)
|
44
|
+
primary_key.map {|key| row_hash[key]}
|
45
|
+
else
|
46
|
+
primary_key ? row_hash[primary_key] : row_hash
|
47
|
+
end
|
48
|
+
|
49
|
+
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
|
50
|
+
construct(parent, join_root, row_hash, seen, model_cache)
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
parents.values
|
55
|
+
end
|
56
|
+
|
57
|
+
def construct(ar_parent, parent, row, seen, model_cache)
|
58
|
+
return if ar_parent.nil?
|
59
|
+
|
60
|
+
parent.children.each do |node|
|
61
|
+
if node.reflection.collection?
|
62
|
+
other = ar_parent.association(node.reflection.name)
|
63
|
+
other.loaded!
|
64
|
+
elsif ar_parent.association_cached?(node.reflection.name)
|
65
|
+
model = ar_parent.association(node.reflection.name).target
|
66
|
+
construct(model, node, row, seen, model_cache)
|
67
|
+
next
|
68
|
+
end
|
69
|
+
|
70
|
+
key = aliases.column_alias(node, node.primary_key)
|
71
|
+
|
72
|
+
# CPK
|
73
|
+
if key.is_a?(Array)
|
74
|
+
id = Array(key).map do |column_alias|
|
75
|
+
row[column_alias]
|
76
|
+
end
|
77
|
+
# At least the first value in the key has to be set. Should we require all values to be set?
|
78
|
+
id = nil if id.first.nil?
|
79
|
+
else # original
|
80
|
+
id = row[key]
|
81
|
+
end
|
82
|
+
|
83
|
+
if id.nil? # duplicating this so it is clear what remained unchanged from the original
|
84
|
+
nil_association = ar_parent.association(node.reflection.name)
|
85
|
+
nil_association.loaded!
|
86
|
+
next
|
87
|
+
end
|
88
|
+
|
89
|
+
model = seen[ar_parent.object_id][node][id]
|
90
|
+
|
91
|
+
if model
|
92
|
+
construct(model, node, row, seen, model_cache)
|
93
|
+
else
|
94
|
+
model = construct_model(ar_parent, node, row, model_cache, id)
|
95
|
+
|
96
|
+
seen[ar_parent.object_id][node][id] = model
|
97
|
+
construct(model, node, row, seen, model_cache)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -5,7 +5,8 @@ module ActiveRecord
|
|
5
5
|
|
6
6
|
def construct_join_attributes(*records)
|
7
7
|
# CPK
|
8
|
-
|
8
|
+
is_composite = self.source_reflection.polymorphic? ? source_reflection.active_record.composite? : source_reflection.klass.composite?
|
9
|
+
if is_composite
|
9
10
|
ensure_mutable
|
10
11
|
|
11
12
|
ids = records.map do |record|
|
@@ -1,6 +1,19 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module AttributeMethods
|
3
3
|
module PrimaryKey
|
4
|
+
module ClassMethods
|
5
|
+
def suppress_composite_primary_key(pk)
|
6
|
+
pk
|
7
|
+
# return pk unless pk.is_a?(Array)
|
8
|
+
#
|
9
|
+
# warn <<~WARNING
|
10
|
+
# WARNING: Active Record does not support composite primary key.
|
11
|
+
#
|
12
|
+
# #{table_name} has composite primary key. Composite primary key is ignored.
|
13
|
+
# WARNING
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
4
17
|
# Returns the primary key previous value.
|
5
18
|
def id_was
|
6
19
|
sync_with_transaction_state
|
@@ -1,30 +1,30 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module AttributeMethods
|
3
|
-
module Read
|
4
|
-
def read_attribute(attr_name, &block)
|
5
|
-
# CPK
|
6
|
-
# name = attr_name.to_s
|
7
|
-
name = attr_name
|
8
|
-
if self.class.attribute_alias?(name)
|
9
|
-
name = self.class.attribute_alias(name)
|
10
|
-
end
|
11
|
-
|
12
|
-
primary_key = self.class.primary_key
|
13
|
-
# CPK
|
14
|
-
# name = primary_key if name == "id" && primary_key
|
15
|
-
name = primary_key if name == "id" && primary_key && !composite?
|
16
|
-
sync_with_transaction_state if name == primary_key
|
17
|
-
_read_attribute(name, &block)
|
18
|
-
end
|
19
|
-
|
20
|
-
def _read_attribute(attr_name, &block) # :nodoc
|
21
|
-
# CPK
|
22
|
-
if attr_name.kind_of?(Array)
|
23
|
-
attr_name.map {|name| @attributes.fetch_value(name.to_s, &block)}
|
24
|
-
else
|
25
|
-
@attributes.fetch_value(attr_name.to_s, &block)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module Read
|
4
|
+
def read_attribute(attr_name, &block)
|
5
|
+
# CPK
|
6
|
+
# name = attr_name.to_s
|
7
|
+
name = attr_name
|
8
|
+
if self.class.attribute_alias?(name)
|
9
|
+
name = self.class.attribute_alias(name)
|
10
|
+
end
|
11
|
+
|
12
|
+
primary_key = self.class.primary_key
|
13
|
+
# CPK
|
14
|
+
# name = primary_key if name == "id" && primary_key
|
15
|
+
name = primary_key if name == "id" && primary_key && !composite?
|
16
|
+
sync_with_transaction_state if name == primary_key
|
17
|
+
_read_attribute(name, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def _read_attribute(attr_name, &block) # :nodoc
|
21
|
+
# CPK
|
22
|
+
if attr_name.kind_of?(Array)
|
23
|
+
attr_name.map {|name| @attributes.fetch_value(name.to_s, &block)}
|
24
|
+
else
|
25
|
+
@attributes.fetch_value(attr_name.to_s, &block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,35 +1,35 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module AttributeMethods
|
3
|
-
module Write
|
4
|
-
def write_attribute(attr_name, value)
|
5
|
-
# CPK
|
6
|
-
#name = attr_name.to_s
|
7
|
-
name = attr_name
|
8
|
-
if self.class.attribute_alias?(name)
|
9
|
-
name = self.class.attribute_alias(name)
|
10
|
-
end
|
11
|
-
|
12
|
-
primary_key = self.class.primary_key
|
13
|
-
# CPK
|
14
|
-
# name = primary_key if name == "id" && primary_key
|
15
|
-
name = primary_key if name == "id" && primary_key && !composite?
|
16
|
-
sync_with_transaction_state if name == primary_key
|
17
|
-
_write_attribute(name, value)
|
18
|
-
end
|
19
|
-
|
20
|
-
def _write_attribute(attr_name, value) # :nodoc:
|
21
|
-
# CPK
|
22
|
-
if attr_name.kind_of?(Array)
|
23
|
-
attr_name.each_with_index do |attr_child_name, i|
|
24
|
-
child_value = value ? value[i] : value
|
25
|
-
@attributes.write_from_user(attr_child_name.to_s, child_value)
|
26
|
-
end
|
27
|
-
else
|
28
|
-
@attributes.write_from_user(attr_name.to_s, value)
|
29
|
-
end
|
30
|
-
|
31
|
-
value
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module Write
|
4
|
+
def write_attribute(attr_name, value)
|
5
|
+
# CPK
|
6
|
+
#name = attr_name.to_s
|
7
|
+
name = attr_name
|
8
|
+
if self.class.attribute_alias?(name)
|
9
|
+
name = self.class.attribute_alias(name)
|
10
|
+
end
|
11
|
+
|
12
|
+
primary_key = self.class.primary_key
|
13
|
+
# CPK
|
14
|
+
# name = primary_key if name == "id" && primary_key
|
15
|
+
name = primary_key if name == "id" && primary_key && !composite?
|
16
|
+
sync_with_transaction_state if name == primary_key
|
17
|
+
_write_attribute(name, value)
|
18
|
+
end
|
19
|
+
|
20
|
+
def _write_attribute(attr_name, value) # :nodoc:
|
21
|
+
# CPK
|
22
|
+
if attr_name.kind_of?(Array)
|
23
|
+
attr_name.each_with_index do |attr_child_name, i|
|
24
|
+
child_value = value ? value[i] : value
|
25
|
+
@attributes.write_from_user(attr_child_name.to_s, child_value)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
@attributes.write_from_user(attr_name.to_s, value)
|
29
|
+
end
|
30
|
+
|
31
|
+
value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module AttributeMethods
|
3
|
-
def has_attribute?(attr_name)
|
4
|
-
# CPK
|
5
|
-
# @attributes.key?(attr_name.to_s)
|
6
|
-
Array(attr_name).all? {|single_attr| attributes.key?(single_attr.to_s) }
|
7
|
-
end
|
8
|
-
end
|
9
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
def has_attribute?(attr_name)
|
4
|
+
# CPK
|
5
|
+
# @attributes.key?(attr_name.to_s)
|
6
|
+
Array(attr_name).all? {|single_attr| @attributes.key?(single_attr.to_s) }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -1,130 +1,141 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
class CompositeKeyError < StandardError #:nodoc:
|
3
|
-
end
|
4
|
-
|
5
|
-
class Base
|
6
|
-
INVALID_FOR_COMPOSITE_KEYS = 'Not appropriate for composite primary keys'
|
7
|
-
NOT_IMPLEMENTED_YET = 'Not implemented for composite primary keys yet'
|
8
|
-
|
9
|
-
class << self
|
10
|
-
alias_method :primary_key_without_composite_key_support=, :primary_key=
|
11
|
-
def primary_key=(keys)
|
12
|
-
unless keys.kind_of?(Array)
|
13
|
-
self.primary_key_without_composite_key_support = keys
|
14
|
-
return
|
15
|
-
end
|
16
|
-
|
17
|
-
@primary_keys = keys.map { |k| k.to_s }.to_composite_keys
|
18
|
-
|
19
|
-
class_eval <<-EOV
|
20
|
-
extend CompositeClassMethods
|
21
|
-
include CompositeInstanceMethods
|
22
|
-
EOV
|
23
|
-
end
|
24
|
-
alias_method :primary_keys=, :primary_key=
|
25
|
-
|
26
|
-
def set_primary_keys(*keys)
|
27
|
-
ActiveSupport::Deprecation.warn(
|
28
|
-
"Calling set_primary_keys is deprecated. Please use `self.primary_keys = keys` instead."
|
29
|
-
)
|
30
|
-
|
31
|
-
keys = keys.first if keys.first.is_a?(Array)
|
32
|
-
if keys.length == 1
|
33
|
-
self.primary_key = keys.first
|
34
|
-
else
|
35
|
-
self.primary_keys = keys
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def composite?
|
40
|
-
false
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def composite?
|
45
|
-
self.class.composite?
|
46
|
-
end
|
47
|
-
|
48
|
-
module CompositeClassMethods
|
49
|
-
def primary_keys
|
50
|
-
@primary_keys = reset_primary_keys unless defined? @primary_keys
|
51
|
-
@primary_keys
|
52
|
-
end
|
53
|
-
|
54
|
-
# Don't like this method name, but its modeled after how AR does it
|
55
|
-
def reset_primary_keys #:nodoc:
|
56
|
-
if self == base_class
|
57
|
-
# CPK
|
58
|
-
self.primary_keys = get_primary_key(base_class.name)
|
59
|
-
else
|
60
|
-
self.primary_keys = base_class.primary_keys
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def primary_key
|
65
|
-
primary_keys
|
66
|
-
end
|
67
|
-
|
68
|
-
def primary_key=(keys)
|
69
|
-
self.primary_keys = keys
|
70
|
-
end
|
71
|
-
|
72
|
-
def composite?
|
73
|
-
true
|
74
|
-
end
|
75
|
-
|
76
|
-
#ids_to_s([[1,2],[7,3]]) -> "(1,2),(7,3)"
|
77
|
-
#ids_to_s([[1,2],[7,3]], ',', ';') -> "1,2;7,3"
|
78
|
-
def ids_to_s(many_ids, id_sep = CompositePrimaryKeys::ID_SEP, list_sep = ',', left_bracket = '(', right_bracket = ')')
|
79
|
-
many_ids.map {|ids| "#{left_bracket}#{CompositePrimaryKeys::CompositeKeys.new(ids)}#{right_bracket}"}.join(list_sep)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
module CompositeInstanceMethods
|
84
|
-
# A model instance's primary keys is always available as model.ids
|
85
|
-
# whether you name it the default 'id' or set it to something else.
|
86
|
-
def id
|
87
|
-
attr_names = self.class.primary_keys
|
88
|
-
::CompositePrimaryKeys::CompositeKeys.new(attr_names.map { |attr_name| read_attribute(attr_name) })
|
89
|
-
end
|
90
|
-
alias_method :ids, :id
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
1
|
+
module ActiveRecord
|
2
|
+
class CompositeKeyError < StandardError #:nodoc:
|
3
|
+
end
|
4
|
+
|
5
|
+
class Base
|
6
|
+
INVALID_FOR_COMPOSITE_KEYS = 'Not appropriate for composite primary keys'
|
7
|
+
NOT_IMPLEMENTED_YET = 'Not implemented for composite primary keys yet'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
alias_method :primary_key_without_composite_key_support=, :primary_key=
|
11
|
+
def primary_key=(keys)
|
12
|
+
unless keys.kind_of?(Array)
|
13
|
+
self.primary_key_without_composite_key_support = keys
|
14
|
+
return
|
15
|
+
end
|
16
|
+
|
17
|
+
@primary_keys = keys.map { |k| k.to_s }.to_composite_keys
|
18
|
+
|
19
|
+
class_eval <<-EOV
|
20
|
+
extend CompositeClassMethods
|
21
|
+
include CompositeInstanceMethods
|
22
|
+
EOV
|
23
|
+
end
|
24
|
+
alias_method :primary_keys=, :primary_key=
|
25
|
+
|
26
|
+
def set_primary_keys(*keys)
|
27
|
+
ActiveSupport::Deprecation.warn(
|
28
|
+
"Calling set_primary_keys is deprecated. Please use `self.primary_keys = keys` instead."
|
29
|
+
)
|
30
|
+
|
31
|
+
keys = keys.first if keys.first.is_a?(Array)
|
32
|
+
if keys.length == 1
|
33
|
+
self.primary_key = keys.first
|
34
|
+
else
|
35
|
+
self.primary_keys = keys
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def composite?
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def composite?
|
45
|
+
self.class.composite?
|
46
|
+
end
|
47
|
+
|
48
|
+
module CompositeClassMethods
|
49
|
+
def primary_keys
|
50
|
+
@primary_keys = reset_primary_keys unless defined? @primary_keys
|
51
|
+
@primary_keys
|
52
|
+
end
|
53
|
+
|
54
|
+
# Don't like this method name, but its modeled after how AR does it
|
55
|
+
def reset_primary_keys #:nodoc:
|
56
|
+
if self == base_class
|
57
|
+
# CPK
|
58
|
+
self.primary_keys = get_primary_key(base_class.name)
|
59
|
+
else
|
60
|
+
self.primary_keys = base_class.primary_keys
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def primary_key
|
65
|
+
primary_keys
|
66
|
+
end
|
67
|
+
|
68
|
+
def primary_key=(keys)
|
69
|
+
self.primary_keys = keys
|
70
|
+
end
|
71
|
+
|
72
|
+
def composite?
|
73
|
+
true
|
74
|
+
end
|
75
|
+
|
76
|
+
#ids_to_s([[1,2],[7,3]]) -> "(1,2),(7,3)"
|
77
|
+
#ids_to_s([[1,2],[7,3]], ',', ';') -> "1,2;7,3"
|
78
|
+
def ids_to_s(many_ids, id_sep = CompositePrimaryKeys::ID_SEP, list_sep = ',', left_bracket = '(', right_bracket = ')')
|
79
|
+
many_ids.map {|ids| "#{left_bracket}#{CompositePrimaryKeys::CompositeKeys.new(ids)}#{right_bracket}"}.join(list_sep)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
module CompositeInstanceMethods
|
84
|
+
# A model instance's primary keys is always available as model.ids
|
85
|
+
# whether you name it the default 'id' or set it to something else.
|
86
|
+
def id
|
87
|
+
attr_names = self.class.primary_keys
|
88
|
+
::CompositePrimaryKeys::CompositeKeys.new(attr_names.map { |attr_name| read_attribute(attr_name) })
|
89
|
+
end
|
90
|
+
alias_method :ids, :id
|
91
|
+
|
92
|
+
# This is overridden purely for json serialization support. If the model is composite
|
93
|
+
# and one of the keys is id, then we don't want to call the id method, instead we want
|
94
|
+
# to get the id attribute value
|
95
|
+
def read_attribute_for_serialization(attribute)
|
96
|
+
if self.composite? && attribute == 'id'
|
97
|
+
read_attribute(attribute)
|
98
|
+
else
|
99
|
+
send(attribute)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def ids_hash
|
104
|
+
self.class.primary_key.zip(ids).inject(Hash.new) do |hash, (key, value)|
|
105
|
+
hash[key] = value
|
106
|
+
hash
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def id_before_type_cast
|
111
|
+
self.class.primary_keys.map do |key|
|
112
|
+
self.read_attribute_before_type_cast(key)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Sets the primary ID.
|
117
|
+
def id=(ids)
|
118
|
+
ids = CompositePrimaryKeys::CompositeKeys.parse(ids)
|
119
|
+
unless ids.length == self.class.primary_keys.length
|
120
|
+
raise "#{self.class}.id= requires #{self.class.primary_keys.length} ids"
|
121
|
+
end
|
122
|
+
[self.class.primary_keys, ids].transpose.each {|key, an_id| write_attribute(key , an_id)}
|
123
|
+
id
|
124
|
+
end
|
125
|
+
|
126
|
+
def can_change_primary_key_values?
|
127
|
+
false
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns this record's primary keys values in an Array
|
131
|
+
# if any value is available
|
132
|
+
def to_key
|
133
|
+
ids.to_a if !ids.compact.empty? # XXX Maybe use primary_keys with send instead of ids
|
134
|
+
end
|
135
|
+
|
136
|
+
def to_param
|
137
|
+
persisted? ? to_key.to_composite_keys.to_s : nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|