composite_primary_keys 5.0.4 → 5.0.5
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.rdoc +12 -0
- data/README.rdoc +4 -2
- data/lib/composite_primary_keys/attribute_methods/dirty.rb +14 -11
- data/lib/composite_primary_keys/base.rb +4 -0
- data/lib/composite_primary_keys/relation.rb +24 -7
- data/lib/composite_primary_keys/relation/calculations.rb +11 -2
- data/lib/composite_primary_keys/version.rb +1 -1
- data/tasks/databases/mysql.rake +4 -1
- data/test/connections/native_mysql/connection.rb +6 -2
- data/test/test_associations.rb +29 -6
- data/test/test_composite_arrays.rb +1 -8
- data/test/test_ids.rb +6 -0
- metadata +2 -2
data/History.rdoc
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
== 5.0.5 2012-05-05
|
2
|
+
* Count without slower subquery in cases where that is possible (Sammy Larbi)
|
3
|
+
* When you call association.build for a has_many association that uses a
|
4
|
+
composite foreign key, the newly built child record should have the values
|
5
|
+
in its belongs_to foreign_key populated from its owner's values (Tyler Rick).
|
6
|
+
* Removed test_has_many_with_primary_key method that wasn't being used because
|
7
|
+
another method by the same name was defined below it (Tyler Rick).
|
8
|
+
* Fixed a bug that was causing object.changes to raise TypeError (Tyler Rick)
|
9
|
+
* Fix error when running rake mysql:rebuild_databases (Tyler Rick)
|
10
|
+
* to_param should join with comma instead of minus sign (Tsutomu Kuroda)
|
11
|
+
* Fix the "Factories" section of README.rdoc (Tsutomu Kuroda)
|
12
|
+
|
1
13
|
== 5.0.4 2012-03-23
|
2
14
|
* Update ActiveRecord::AttributeMethods#Write for Rails 3.2.2 (Travis Warlick)
|
3
15
|
|
data/README.rdoc
CHANGED
@@ -26,8 +26,10 @@ This RubyGem extends the activerecord gem to provide CPK support.
|
|
26
26
|
|
27
27
|
FactoryGirl.define do
|
28
28
|
factory :model_with_composite_keys do
|
29
|
-
|
30
|
-
|
29
|
+
sequence( :id ) { |n| [n,Time.now] }
|
30
|
+
name "Brett"
|
31
|
+
end
|
32
|
+
end
|
31
33
|
|
32
34
|
|
33
35
|
It even supports composite foreign keys for associations.
|
@@ -3,18 +3,21 @@ module ActiveRecord
|
|
3
3
|
module Dirty
|
4
4
|
def write_attribute(attr, value)
|
5
5
|
# CPK
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
if attribute_changed?(attr)
|
11
|
-
old = @changed_attributes[attr]
|
12
|
-
@changed_attributes.delete(attr) unless field_changed?(attr, old, value)
|
6
|
+
if attr.kind_of?(Array)
|
7
|
+
# A *composite* attribute can't be marked as changed! So do nothing now.
|
8
|
+
# We will come back in here with an *individual* attribute when Write#write_attribute looks through the individual attributes comprising this composite key:
|
9
|
+
# [attr_name, value].transpose.map {|name,val| write_attribute(name, val)}
|
13
10
|
else
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
# The attribute already has an unsaved change.
|
12
|
+
if attribute_changed?(attr)
|
13
|
+
old = @changed_attributes[attr]
|
14
|
+
@changed_attributes.delete(attr) unless field_changed?(attr, old, value)
|
15
|
+
else
|
16
|
+
old = clone_attribute_value(:read_attribute, attr)
|
17
|
+
# Save Time objects as TimeWithZone if time_zone_aware_attributes == true
|
18
|
+
old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
|
19
|
+
@changed_attributes[attr] = old if field_changed?(attr, old, value)
|
20
|
+
end
|
18
21
|
end
|
19
22
|
|
20
23
|
# Carry on.
|
@@ -148,6 +148,10 @@ module ActiveRecord
|
|
148
148
|
def to_key
|
149
149
|
ids.to_a if !ids.compact.empty? # XXX Maybe use primary_keys with send instead of ids
|
150
150
|
end
|
151
|
+
|
152
|
+
def to_param
|
153
|
+
persisted? ? to_key.join(CompositePrimaryKeys::ID_SEP) : nil
|
154
|
+
end
|
151
155
|
end
|
152
156
|
end
|
153
157
|
end
|
@@ -8,7 +8,7 @@ module ActiveRecord
|
|
8
8
|
|
9
9
|
def delete(id_or_array)
|
10
10
|
::ActiveRecord::IdentityMap.remove_by_id(self.symbolized_base_class, id_or_array) if ::ActiveRecord::IdentityMap.enabled?
|
11
|
-
# CPK
|
11
|
+
# Without CPK:
|
12
12
|
# where(primary_key => id_or_array).delete_all
|
13
13
|
|
14
14
|
id_or_array = if id_or_array.kind_of?(CompositePrimaryKeys::CompositeKeys)
|
@@ -23,12 +23,13 @@ module ActiveRecord
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def destroy(id_or_array)
|
26
|
-
# CPK
|
26
|
+
# Without CPK:
|
27
27
|
#if id.is_a?(Array)
|
28
28
|
# id.map { |one_id| destroy(one_id) }
|
29
29
|
#else
|
30
30
|
# find(id).destroy
|
31
31
|
#end
|
32
|
+
|
32
33
|
id_or_array = if id_or_array.kind_of?(CompositePrimaryKeys::CompositeKeys)
|
33
34
|
[id_or_array]
|
34
35
|
else
|
@@ -44,16 +45,32 @@ module ActiveRecord
|
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
47
|
-
|
48
|
+
def add_cpk_where_values_hash
|
49
|
+
class << self
|
50
|
+
def where_values_hash
|
51
|
+
# CPK adds this so that it finds the Equality nodes beneath the And node:
|
52
|
+
nodes_from_and = with_default_scope.where_values.grep(Arel::Nodes::And).map {|and_node| and_node.children.grep(Arel::Nodes::Equality) }.flatten
|
53
|
+
|
54
|
+
equalities = (nodes_from_and + with_default_scope.where_values.grep(Arel::Nodes::Equality)).find_all { |node|
|
55
|
+
node.left.relation.name == table_name
|
56
|
+
}
|
57
|
+
|
58
|
+
Hash[equalities.map { |where| [where.left.name, where.right] }]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
alias :initialize_without_cpk :initialize
|
48
64
|
def initialize(klass, table)
|
49
|
-
|
65
|
+
initialize_without_cpk(klass, table)
|
50
66
|
add_cpk_support if klass.composite?
|
67
|
+
add_cpk_where_values_hash
|
51
68
|
end
|
52
69
|
|
53
|
-
alias :
|
70
|
+
alias :initialize_copy_without_cpk :initialize_copy
|
54
71
|
def initialize_copy(other)
|
55
|
-
|
72
|
+
initialize_copy_without_cpk(other)
|
56
73
|
add_cpk_support if klass.composite?
|
57
74
|
end
|
58
75
|
end
|
59
|
-
end
|
76
|
+
end
|
@@ -55,8 +55,17 @@ module CompositePrimaryKeys
|
|
55
55
|
else
|
56
56
|
[Arel.sql(column_name == :all ? "#{@klass.quoted_table_name}.*" : column_name.to_s)]
|
57
57
|
end
|
58
|
-
|
59
|
-
|
58
|
+
|
59
|
+
# do not use slower subquery if we're only counting on one column
|
60
|
+
if relation.select_values.count == 1
|
61
|
+
# exclude distinct table_name.* in case of joins where subbing * would change results
|
62
|
+
if (!distinct && column_name == :all) || column_name != :all
|
63
|
+
column = relation.select_values[0].gsub("#{@klass.quoted_table_name}.","")
|
64
|
+
relation.select_values = [operation_over_aggregate_column(column, 'count', distinct)]
|
65
|
+
return relation.arel
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
60
69
|
relation.distinct(true)
|
61
70
|
subquery = relation.arel.as(subquery_alias)
|
62
71
|
|
data/tasks/databases/mysql.rake
CHANGED
@@ -19,7 +19,9 @@ namespace :mysql do
|
|
19
19
|
file.read
|
20
20
|
end
|
21
21
|
|
22
|
+
Rake::Task['mysql:load_connection'].reenable
|
22
23
|
Rake::Task['mysql:load_connection'].invoke
|
24
|
+
#puts %(ActiveRecord::Base.connection.instance_variable_get(:@config)=#{(ActiveRecord::Base.connection.instance_variable_get(:@config)).inspect})
|
23
25
|
ActiveRecord::Base.connection.execute(sql)
|
24
26
|
end
|
25
27
|
|
@@ -33,5 +35,6 @@ namespace :mysql do
|
|
33
35
|
|
34
36
|
task :load_connection do
|
35
37
|
require File.join(PROJECT_ROOT, "test", "connections", "native_mysql", "connection")
|
38
|
+
establish_connection
|
36
39
|
end
|
37
|
-
end
|
40
|
+
end
|
@@ -8,6 +8,10 @@ def connection_string
|
|
8
8
|
options.map { |key, value| "-#{key}#{value}" }.join(" ")
|
9
9
|
end
|
10
10
|
|
11
|
-
# Adapter config setup in
|
11
|
+
# Adapter config setup in test/connections/databases.yml
|
12
12
|
SPEC = CompositePrimaryKeys::ConnectionSpec['mysql']
|
13
|
-
|
13
|
+
|
14
|
+
def establish_connection
|
15
|
+
ActiveRecord::Base.establish_connection(SPEC)
|
16
|
+
end
|
17
|
+
establish_connection
|
data/test/test_associations.rb
CHANGED
@@ -162,9 +162,18 @@ class TestAssociations < ActiveSupport::TestCase
|
|
162
162
|
assert_equal(rooms(:branner_room_1), room_assignment.room)
|
163
163
|
end
|
164
164
|
|
165
|
-
def
|
166
|
-
|
167
|
-
|
165
|
+
def test_composite_belongs_to_changes
|
166
|
+
room_assignment = room_assignments(:jacksons_room)
|
167
|
+
room_assignment.room = rooms(:branner_room_2)
|
168
|
+
# This was raising an error before:
|
169
|
+
# TypeError: [:dorm_id, :room_id] is not a symbol
|
170
|
+
assert_equal({:room_id=>[1, 2]}, room_assignment.changes)
|
171
|
+
|
172
|
+
steve = employees(:steve)
|
173
|
+
steve.department = departments(:engineering)
|
174
|
+
# It was returning this before:
|
175
|
+
# {"[:department_id, :location_id]"=>[nil, [2, 1]]}
|
176
|
+
assert_equal({:department_id=>[1, 2]}, steve.changes)
|
168
177
|
end
|
169
178
|
|
170
179
|
def test_has_one_with_composite
|
@@ -173,14 +182,28 @@ class TestAssociations < ActiveSupport::TestCase
|
|
173
182
|
assert_not_nil(department.head)
|
174
183
|
end
|
175
184
|
|
185
|
+
def test_has_many_build__simple_key
|
186
|
+
user = users(:santiago)
|
187
|
+
reading = user.readings.build
|
188
|
+
assert_equal user.id, reading.user_id
|
189
|
+
assert_equal user, reading.user
|
190
|
+
end
|
191
|
+
|
192
|
+
def test_has_many_build__composite_key
|
193
|
+
department = departments(:engineering)
|
194
|
+
employee = department.employees.build
|
195
|
+
assert_equal department.department_id, employee.department_id
|
196
|
+
assert_equal department.location_id, employee.location_id
|
197
|
+
assert_equal department, employee.department
|
198
|
+
end
|
199
|
+
|
176
200
|
def test_has_many_with_primary_key
|
177
201
|
@membership = Membership.find([1, 1])
|
178
|
-
|
179
202
|
assert_equal 2, @membership.readings.size
|
180
203
|
end
|
181
204
|
|
182
205
|
def test_has_many_with_composite_key
|
183
|
-
# In this case a regular model has_many composite models
|
206
|
+
# In this case a regular model (Dorm) has_many composite models (Rooms)
|
184
207
|
dorm = dorms(:branner)
|
185
208
|
assert_equal(2, dorm.rooms.length)
|
186
209
|
assert_equal(1, dorm.rooms[0].room_id)
|
@@ -238,4 +261,4 @@ class TestAssociations < ActiveSupport::TestCase
|
|
238
261
|
assert_equal(1, memberships.length)
|
239
262
|
assert_equal([1,1], memberships[0].id)
|
240
263
|
end
|
241
|
-
end
|
264
|
+
end
|
@@ -21,11 +21,4 @@ class CompositeArraysTest < ActiveSupport::TestCase
|
|
21
21
|
assert_equal CompositePrimaryKeys::CompositeKeys, keys.class
|
22
22
|
assert_equal '1,2,3', keys.to_s
|
23
23
|
end
|
24
|
-
|
25
|
-
def test_composite_keys_equality
|
26
|
-
keys_array_1 = [1, Time.now].to_composite_keys
|
27
|
-
keys_array_2 = [1, Time.now].to_composite_keys
|
28
|
-
assert keys_array_1 == keys_array_2
|
29
|
-
assert keys_array_1.eql? keys_array_2
|
30
|
-
end
|
31
|
-
end
|
24
|
+
end
|
data/test/test_ids.rb
CHANGED
@@ -32,6 +32,12 @@ class TestIds < ActiveSupport::TestCase
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
def test_to_param
|
36
|
+
testing_with do
|
37
|
+
assert_equal '1,1', @first.to_param if composite?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
35
41
|
def test_ids_to_s
|
36
42
|
testing_with do
|
37
43
|
order = @klass.primary_key.is_a?(String) ? @klass.primary_key : @klass.primary_key.join(',')
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: composite_primary_keys
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.
|
4
|
+
version: 5.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-05-05 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|