composite_primary_keys 13.0.8 → 14.0.1
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 +5 -24
- data/README.rdoc +1 -0
- data/lib/composite_primary_keys/associations/association.rb +2 -2
- data/lib/composite_primary_keys/associations/association_scope.rb +1 -1
- data/lib/composite_primary_keys/associations/{join_association.rb → join_dependency.rb} +137 -137
- data/lib/composite_primary_keys/associations/preloader/association.rb +26 -19
- data/lib/composite_primary_keys/associations/through_association.rb +2 -1
- data/lib/composite_primary_keys/base.rb +141 -137
- data/lib/composite_primary_keys/composite_predicates.rb +1 -50
- data/lib/composite_primary_keys/persistence.rb +20 -7
- data/lib/composite_primary_keys/relation/calculations.rb +7 -1
- data/lib/composite_primary_keys/relation.rb +2 -2
- data/lib/composite_primary_keys/version.rb +2 -2
- data/lib/composite_primary_keys.rb +2 -2
- data/test/abstract_unit.rb +5 -1
- data/test/fixtures/comments.yml +0 -6
- data/test/fixtures/membership.rb +8 -8
- data/test/test_associations.rb +1 -1
- data/test/test_create.rb +218 -219
- data/test/test_polymorphic.rb +0 -6
- data/test/test_predicates.rb +60 -130
- metadata +7 -8
- data/test/fixtures/user_with_polymorphic_name.rb +0 -9
@@ -1,137 +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
|
-
# 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
|
-
|
137
|
-
|
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
|
@@ -51,59 +51,9 @@ module CompositePrimaryKeys
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def cpk_in_predicate(table, primary_keys, ids)
|
54
|
-
if primary_keys.length == 2
|
55
|
-
cpk_in_predicate_with_grouped_keys(table, primary_keys, ids)
|
56
|
-
else
|
57
|
-
cpk_in_predicate_with_non_grouped_keys(table, primary_keys, ids)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def cpk_in_predicate_with_non_grouped_keys(table, primary_keys, ids)
|
62
54
|
and_predicates = ids.map do |id|
|
63
55
|
cpk_id_predicate(table, primary_keys, id)
|
64
56
|
end
|
65
|
-
|
66
|
-
cpk_or_predicate(and_predicates)
|
67
|
-
end
|
68
|
-
|
69
|
-
def cpk_in_predicate_with_grouped_keys(table, primary_keys, ids)
|
70
|
-
keys_by_first_column_name = Hash.new { |hash, key| hash[key] = [] }
|
71
|
-
keys_by_second_column_name = Hash.new { |hash, key| hash[key] = [] }
|
72
|
-
|
73
|
-
ids.map.each do |first_key_part, second_key_part|
|
74
|
-
keys_by_first_column_name[first_key_part] << second_key_part
|
75
|
-
keys_by_second_column_name[second_key_part] << first_key_part
|
76
|
-
end
|
77
|
-
|
78
|
-
low_cardinality_column_name, high_cardinality_column_name, groups = \
|
79
|
-
if keys_by_first_column_name.size <= keys_by_second_column_name.size
|
80
|
-
[primary_keys.first, primary_keys.second, keys_by_first_column_name]
|
81
|
-
else
|
82
|
-
[primary_keys.second, primary_keys.first, keys_by_second_column_name]
|
83
|
-
end
|
84
|
-
|
85
|
-
and_predicates = groups.map do |low_cardinality_value, high_cardinality_values|
|
86
|
-
non_nil_high_cardinality_values = high_cardinality_values.compact
|
87
|
-
in_clause = table[high_cardinality_column_name].in(non_nil_high_cardinality_values)
|
88
|
-
inclusion_clauses = if non_nil_high_cardinality_values.size != high_cardinality_values.size
|
89
|
-
Arel::Nodes::Grouping.new(
|
90
|
-
Arel::Nodes::Or.new(
|
91
|
-
in_clause,
|
92
|
-
table[high_cardinality_column_name].eq(nil)
|
93
|
-
)
|
94
|
-
)
|
95
|
-
else
|
96
|
-
in_clause
|
97
|
-
end
|
98
|
-
|
99
|
-
Arel::Nodes::And.new(
|
100
|
-
[
|
101
|
-
table[low_cardinality_column_name].eq(low_cardinality_value),
|
102
|
-
inclusion_clauses
|
103
|
-
]
|
104
|
-
)
|
105
|
-
end
|
106
|
-
|
107
57
|
cpk_or_predicate(and_predicates)
|
108
58
|
end
|
109
59
|
end
|
@@ -112,6 +62,7 @@ end
|
|
112
62
|
ActiveRecord::Associations::AssociationScope.send(:include, CompositePrimaryKeys::Predicates)
|
113
63
|
ActiveRecord::Associations::JoinDependency::JoinAssociation.send(:include, CompositePrimaryKeys::Predicates)
|
114
64
|
ActiveRecord::Associations::Preloader::Association.send(:include, CompositePrimaryKeys::Predicates)
|
65
|
+
ActiveRecord::Associations::Preloader::Association::LoaderQuery.send(:include, CompositePrimaryKeys::Predicates)
|
115
66
|
ActiveRecord::Associations::HasManyAssociation.send(:include, CompositePrimaryKeys::Predicates)
|
116
67
|
ActiveRecord::Associations::HasManyThroughAssociation.send(:include, CompositePrimaryKeys::Predicates)
|
117
68
|
ActiveRecord::Base.send(:extend, CompositePrimaryKeys::Predicates)
|
@@ -30,11 +30,18 @@ module ActiveRecord
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
constraints =
|
33
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
default_constraint = build_default_constraint
|
36
|
+
constraints << default_constraint if default_constraint
|
37
|
+
|
38
|
+
if current_scope = self.global_current_scope
|
39
|
+
constraints << current_scope.where_clause.ast
|
40
|
+
end
|
41
|
+
|
42
|
+
um = Arel::UpdateManager.new(arel_table)
|
43
|
+
um.set(values.transform_keys { |name| arel_table[name] })
|
44
|
+
um.wheres = constraints
|
38
45
|
|
39
46
|
connection.update(um, "#{self} Update")
|
40
47
|
end
|
@@ -48,10 +55,16 @@ module ActiveRecord
|
|
48
55
|
end
|
49
56
|
end
|
50
57
|
|
51
|
-
constraints =
|
58
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
59
|
+
|
60
|
+
default_constraint = build_default_constraint
|
61
|
+
constraints << default_constraint if default_constraint
|
62
|
+
|
63
|
+
if current_scope = self.global_current_scope
|
64
|
+
constraints << current_scope.where_clause.ast
|
65
|
+
end
|
52
66
|
|
53
|
-
dm = Arel::DeleteManager.new
|
54
|
-
dm.from(arel_table)
|
67
|
+
dm = Arel::DeleteManager.new(arel_table)
|
55
68
|
dm.wheres = constraints
|
56
69
|
|
57
70
|
connection.delete(dm, "#{self} Destroy")
|
@@ -42,7 +42,13 @@ module CompositePrimaryKeys
|
|
42
42
|
|
43
43
|
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
|
44
44
|
|
45
|
-
|
45
|
+
if operation != "count"
|
46
|
+
type = column.try(:type_caster) ||
|
47
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || ::ActiveRecord::Type.default_value
|
48
|
+
type = type.subtype if ::ActiveRecord::Enum::EnumType === type
|
49
|
+
end
|
50
|
+
|
51
|
+
type_cast_calculated_value(result.cast_values.first, operation, type) do |value|
|
46
52
|
type = column.try(:type_caster) ||
|
47
53
|
# CPK
|
48
54
|
# lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
@@ -29,7 +29,7 @@ module ActiveRecord
|
|
29
29
|
stmt.key = table[primary_key]
|
30
30
|
|
31
31
|
# CPK
|
32
|
-
if @klass.composite?
|
32
|
+
if @klass.composite? && @klass.connection.visitor.compile(stmt.ast) =~ /['"]#{primary_key.to_s}['"]/
|
33
33
|
stmt = Arel::UpdateManager.new
|
34
34
|
stmt.table(arel_table)
|
35
35
|
cpk_subquery(stmt)
|
@@ -74,7 +74,7 @@ module ActiveRecord
|
|
74
74
|
stmt.key = table[primary_key]
|
75
75
|
|
76
76
|
# CPK
|
77
|
-
if @klass.composite?
|
77
|
+
if @klass.composite? && @klass.connection.visitor.compile(stmt.ast) =~ /['"]#{primary_key.to_s}['"]/
|
78
78
|
stmt = Arel::DeleteManager.new
|
79
79
|
stmt.from(arel_table)
|
80
80
|
cpk_subquery(stmt)
|
@@ -23,7 +23,7 @@
|
|
23
23
|
|
24
24
|
unless defined?(ActiveRecord)
|
25
25
|
require 'rubygems'
|
26
|
-
gem 'activerecord', '~>
|
26
|
+
gem 'activerecord', '~>7.0.0', '>= 7.0.1'
|
27
27
|
require 'active_record'
|
28
28
|
end
|
29
29
|
|
@@ -88,7 +88,7 @@ require_relative 'composite_primary_keys/associations/association_scope'
|
|
88
88
|
require_relative 'composite_primary_keys/associations/foreign_association'
|
89
89
|
require_relative 'composite_primary_keys/associations/has_many_association'
|
90
90
|
require_relative 'composite_primary_keys/associations/has_many_through_association'
|
91
|
-
require_relative 'composite_primary_keys/associations/
|
91
|
+
require_relative 'composite_primary_keys/associations/join_dependency'
|
92
92
|
require_relative 'composite_primary_keys/associations/preloader/association'
|
93
93
|
require_relative 'composite_primary_keys/associations/collection_association'
|
94
94
|
require_relative 'composite_primary_keys/associations/through_association'
|
data/test/abstract_unit.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
spec_name = ENV['ADAPTER'] || '
|
1
|
+
spec_name = ENV['ADAPTER'] || 'postgresql'
|
2
2
|
require 'bundler'
|
3
3
|
require 'minitest/autorun'
|
4
4
|
|
@@ -21,6 +21,10 @@ ActiveRecord::Base.configurations = {test: spec}
|
|
21
21
|
|
22
22
|
# Tell ActiveRecord where to find models
|
23
23
|
ActiveSupport::Dependencies.autoload_paths << File.join(PROJECT_ROOT, 'test', 'fixtures')
|
24
|
+
Dir[File.join(PROJECT_ROOT, 'test', 'fixtures', '*.rb')].each do |file_path|
|
25
|
+
require_file = file_path.sub(PROJECT_ROOT, '..').sub('.rb', '')
|
26
|
+
require_relative require_file
|
27
|
+
end
|
24
28
|
|
25
29
|
I18n.config.enforce_available_locales = true
|
26
30
|
|
data/test/fixtures/comments.yml
CHANGED
data/test/fixtures/membership.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
class Membership < ActiveRecord::Base
|
2
|
-
self.primary_keys = :user_id, :group_id
|
3
|
-
belongs_to :user
|
4
|
-
belongs_to :group
|
5
|
-
has_many :statuses, :class_name => 'MembershipStatus', :foreign_key => [:user_id, :group_id]
|
6
|
-
has_many :active_statuses, -> { where('membership_statuses.status = ?', 'Active') },
|
7
|
-
:class_name => 'MembershipStatus', :foreign_key => [:user_id, :group_id]
|
8
|
-
has_many :readings, :primary_key => :user_id, :foreign_key => :user_id
|
1
|
+
class Membership < ActiveRecord::Base
|
2
|
+
self.primary_keys = :user_id, :group_id
|
3
|
+
belongs_to :user
|
4
|
+
belongs_to :group
|
5
|
+
has_many :statuses, :class_name => 'MembershipStatus', :foreign_key => [:user_id, :group_id]
|
6
|
+
has_many :active_statuses, -> { where('membership_statuses.status = ?', 'Active') },
|
7
|
+
:class_name => 'MembershipStatus', :foreign_key => [:user_id, :group_id]
|
8
|
+
has_many :readings, :primary_key => :user_id, :foreign_key => :user_id
|
9
9
|
end
|
data/test/test_associations.rb
CHANGED
@@ -343,7 +343,7 @@ class TestAssociations < ActiveSupport::TestCase
|
|
343
343
|
assert_equal([3,2], memberships[1].id)
|
344
344
|
end
|
345
345
|
|
346
|
-
def
|
346
|
+
def test_scoped_has_many_with_primary_key_with_associations
|
347
347
|
memberships = Membership.joins(:active_statuses)
|
348
348
|
assert_equal(2, memberships.length)
|
349
349
|
|