composite_primary_keys 3.0.9 → 3.1.0

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.
Files changed (62) hide show
  1. data/History.txt +6 -0
  2. data/Rakefile +10 -1
  3. data/lib/composite_primary_keys.rb +75 -75
  4. data/lib/composite_primary_keys/base.rb +190 -194
  5. data/lib/composite_primary_keys/composite_arrays.rb +23 -23
  6. data/lib/composite_primary_keys/finder_methods.rb +0 -11
  7. data/lib/composite_primary_keys/reflection.rb +38 -38
  8. data/lib/composite_primary_keys/version.rb +2 -2
  9. data/test/abstract_unit.rb +3 -2
  10. data/test/connections/connection_spec.rb +1 -2
  11. data/test/connections/databases.example.yml +14 -12
  12. data/test/connections/databases.yml +14 -14
  13. data/test/connections/native_ibm_db/connection.rb +0 -3
  14. data/test/connections/native_mysql/connection.rb +3 -9
  15. data/test/connections/native_oracle/connection.rb +4 -10
  16. data/test/connections/native_oracle_enhanced/connection.rb +4 -11
  17. data/test/connections/native_postgresql/connection.rb +1 -3
  18. data/test/connections/native_sqlite/connection.rb +2 -4
  19. data/test/debug.log +589 -589
  20. data/test/fixtures/article.rb +5 -5
  21. data/test/fixtures/articles.yml +5 -5
  22. data/test/fixtures/capitol.rb +3 -0
  23. data/test/fixtures/capitols.yml +16 -0
  24. data/test/fixtures/db_definitions/mysql.sql +5 -0
  25. data/test/fixtures/db_definitions/postgresql.sql +5 -0
  26. data/test/fixtures/product.rb +7 -7
  27. data/test/fixtures/product_tariff.rb +5 -5
  28. data/test/fixtures/product_tariffs.yml +12 -12
  29. data/test/fixtures/products.yml +5 -5
  30. data/test/fixtures/reading.rb +4 -4
  31. data/test/fixtures/readings.yml +9 -9
  32. data/test/fixtures/reference_code.rb +7 -7
  33. data/test/fixtures/reference_codes.yml +29 -29
  34. data/test/fixtures/reference_type.rb +7 -7
  35. data/test/fixtures/reference_types.yml +9 -9
  36. data/test/fixtures/suburb.rb +5 -5
  37. data/test/fixtures/suburbs.yml +8 -8
  38. data/test/fixtures/tariff.rb +6 -6
  39. data/test/fixtures/tariffs.yml +12 -12
  40. data/test/fixtures/user.rb +10 -10
  41. data/test/fixtures/users.yml +5 -5
  42. data/test/hash_tricks.rb +34 -34
  43. data/test/test_associations.rb +180 -180
  44. data/test/test_attribute_methods.rb +0 -2
  45. data/test/test_attributes.rb +0 -5
  46. data/test/test_clone.rb +31 -33
  47. data/test/test_composite_arrays.rb +0 -2
  48. data/test/test_create.rb +0 -4
  49. data/test/test_delete.rb +92 -96
  50. data/test/test_equal.rb +21 -0
  51. data/test/test_exists.rb +0 -2
  52. data/test/test_find.rb +79 -76
  53. data/test/test_ids.rb +81 -83
  54. data/test/test_miscellaneous.rb +36 -38
  55. data/test/test_pagination.rb +35 -37
  56. data/test/test_polymorphic.rb +0 -6
  57. data/test/test_santiago.rb +23 -27
  58. data/test/test_suite.rb +1 -0
  59. data/test/test_tutorial_example.rb +0 -4
  60. data/test/test_update.rb +37 -39
  61. data/test/test_validations.rb +0 -1
  62. metadata +8 -4
@@ -1,3 +1,9 @@
1
+ == 3.1.0 2010-12-17
2
+ * Add back in rake test tasks (Toby Cabot)
3
+ * Add support for multiple string composite keys (wouter)
4
+ * Remove support for find('3,3'). Instead use find([3,3])
5
+
6
+
1
7
  == 3.0.9 2010-12-04
2
8
  * Fix up support for composite key fixtures.
3
9
 
data/Rakefile CHANGED
@@ -19,4 +19,13 @@ Rake::GemPackageTask.new(spec).define
19
19
  # Now load in other task files
20
20
  Dir.glob('tasks/**/*.rake').each do |rake_file|
21
21
  load File.join(File.dirname(__FILE__), rake_file)
22
- end
22
+ end
23
+
24
+ # Set up test tasks
25
+ for adapter in %w( mysql sqlite oracle oracle_enhanced postgresql ibm_db )
26
+ Rake::TestTask.new("test_#{adapter}") do |t|
27
+ t.libs << "test" << "test/connections/native_#{adapter}"
28
+ t.pattern = "test/test_*.rb"
29
+ t.verbose = true
30
+ end
31
+ end
@@ -1,75 +1,75 @@
1
- #--
2
- # Copyright (c) 2006 Nic Williams
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining
5
- # a copy of this software and associated documentation files (the
6
- # "Software"), to deal in the Software without restriction, including
7
- # without limitation the rights to use, copy, modify, merge, publish,
8
- # distribute, sublicense, and/or sell copies of the Software, and to
9
- # permit persons to whom the Software is furnished to do so, subject to
10
- # the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be
13
- # included in all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
- # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
- # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
- #++
23
-
24
- $:.unshift(File.dirname(__FILE__)) unless
25
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
26
-
27
- unless defined?(ActiveRecord)
28
- begin
29
- require 'active_record'
30
- rescue LoadError
31
- require 'rubygems'
32
- gem 'active_record'
33
- end
34
- end
35
-
36
- require 'active_record/associations.rb'
37
- require 'active_record/associations/association_proxy.rb'
38
- require 'active_record/associations/association_collection'
39
- require 'active_record/associations/association_proxy'
40
- require 'active_record/associations/belongs_to_association'
41
- require 'active_record/associations/belongs_to_polymorphic_association'
42
- require 'active_record/associations/has_and_belongs_to_many_association'
43
- require 'active_record/associations/has_many_association'
44
- require 'active_record/associations/has_one_association'
45
- require 'active_record/associations/has_one_through_association'
46
- require 'active_record/associations/through_association_scope'
47
- require 'active_record/relation/query_methods'
48
- require 'active_record/attribute_methods/primary_key'
49
- require 'active_record/fixtures'
50
-
51
- require 'composite_primary_keys/composite_arrays'
52
- require 'composite_primary_keys/associations'
53
- require 'composite_primary_keys/associations/association_proxy'
54
- require 'composite_primary_keys/associations/has_one_association'
55
- require 'composite_primary_keys/associations/has_many_association'
56
- require 'composite_primary_keys/associations/has_and_belongs_to_many_association'
57
- require 'composite_primary_keys/associations/through_association_scope'
58
- require 'composite_primary_keys/association_preload'
59
- require 'composite_primary_keys/reflection'
60
- require 'composite_primary_keys/relation'
61
- require 'composite_primary_keys/read'
62
- require 'composite_primary_keys/finder_methods'
63
- require 'composite_primary_keys/base'
64
- require 'composite_primary_keys/calculations'
65
- require 'composite_primary_keys/validations/uniqueness'
66
- require 'composite_primary_keys/query_methods'
67
- require 'composite_primary_keys/primary_key'
68
- require 'composite_primary_keys/fixtures'
69
-
70
- Dir[File.dirname(__FILE__) + '/composite_primary_keys/connection_adapters/*.rb'].each do |adapter|
71
- begin
72
- require adapter.gsub('.rb','')
73
- rescue MissingSourceFile
74
- end
75
- end
1
+ #--
2
+ # Copyright (c) 2006 Nic Williams
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ $:.unshift(File.dirname(__FILE__)) unless
25
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
26
+
27
+ unless defined?(ActiveRecord)
28
+ begin
29
+ require 'active_record'
30
+ rescue LoadError
31
+ require 'rubygems'
32
+ gem 'active_record'
33
+ end
34
+ end
35
+
36
+ require 'active_record/associations.rb'
37
+ require 'active_record/associations/association_proxy.rb'
38
+ require 'active_record/associations/association_collection'
39
+ require 'active_record/associations/association_proxy'
40
+ require 'active_record/associations/belongs_to_association'
41
+ require 'active_record/associations/belongs_to_polymorphic_association'
42
+ require 'active_record/associations/has_and_belongs_to_many_association'
43
+ require 'active_record/associations/has_many_association'
44
+ require 'active_record/associations/has_one_association'
45
+ require 'active_record/associations/has_one_through_association'
46
+ require 'active_record/associations/through_association_scope'
47
+ require 'active_record/relation/query_methods'
48
+ require 'active_record/attribute_methods/primary_key'
49
+ require 'active_record/fixtures'
50
+
51
+ require 'composite_primary_keys/composite_arrays'
52
+ require 'composite_primary_keys/associations'
53
+ require 'composite_primary_keys/associations/association_proxy'
54
+ require 'composite_primary_keys/associations/has_one_association'
55
+ require 'composite_primary_keys/associations/has_many_association'
56
+ require 'composite_primary_keys/associations/has_and_belongs_to_many_association'
57
+ require 'composite_primary_keys/associations/through_association_scope'
58
+ require 'composite_primary_keys/association_preload'
59
+ require 'composite_primary_keys/reflection'
60
+ require 'composite_primary_keys/relation'
61
+ require 'composite_primary_keys/read'
62
+ require 'composite_primary_keys/finder_methods'
63
+ require 'composite_primary_keys/base'
64
+ require 'composite_primary_keys/calculations'
65
+ require 'composite_primary_keys/validations/uniqueness'
66
+ require 'composite_primary_keys/query_methods'
67
+ require 'composite_primary_keys/primary_key'
68
+ require 'composite_primary_keys/fixtures'
69
+
70
+ Dir[File.dirname(__FILE__) + '/composite_primary_keys/connection_adapters/*.rb'].each do |adapter|
71
+ begin
72
+ require adapter.gsub('.rb','')
73
+ rescue MissingSourceFile
74
+ end
75
+ end
@@ -1,195 +1,191 @@
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
- def set_primary_keys(*keys)
11
- keys = keys.first if keys.first.is_a?(Array)
12
-
13
- if keys.length == 1
14
- set_primary_key(keys.first)
15
- return
16
- end
17
-
18
- cattr_accessor :primary_keys
19
- self.primary_keys = keys.map { |k| k.to_sym }
20
-
21
- class_eval <<-EOV
22
- extend CompositeClassMethods
23
- include CompositeInstanceMethods
24
- include CompositePrimaryKeys::ActiveRecord::AssociationPreload
25
- EOV
26
- end
27
-
28
- def composite?
29
- false
30
- end
31
- end
32
-
33
- def composite?
34
- self.class.composite?
35
- end
36
-
37
- def [](attr_name)
38
- # CPK
39
- if attr_name.is_a?(String) and attr_name != attr_name.split(CompositePrimaryKeys::ID_SEP).first
40
- attr_name = attr_name.split(CompositePrimaryKeys::ID_SEP)
41
- end
42
-
43
- # CPK
44
- if attr_name.is_a?(Array)
45
- values = attr_name.map {|name| read_attribute(name)}
46
- CompositePrimaryKeys::CompositeKeys.new(values)
47
- else
48
- read_attribute(attr_name)
49
- end
50
- end
51
-
52
- def []=(attr_name, value)
53
- # CPK
54
- if attr_name.is_a?(String) and attr_name != attr_name.split(CompositePrimaryKeys::ID_SEP).first
55
- attr_name = attr_name.split(CompositePrimaryKeys::ID_SEP)
56
- end
57
-
58
- if attr_name.is_a? Array
59
- unless value.length == attr_name.length
60
- raise "Number of attr_names and values do not match"
61
- end
62
- [attr_name, value].transpose.map {|name,val| write_attribute(name, val)}
63
- value
64
- else
65
- write_attribute(attr_name, value)
66
- end
67
- end
68
-
69
- module CompositeClassMethods
70
- def primary_key
71
- primary_keys
72
- end
73
-
74
- def primary_key=(keys)
75
- primary_keys = keys
76
- end
77
-
78
- def composite?
79
- true
80
- end
81
-
82
- #ids_to_s([[1,2],[7,3]]) -> "(1,2),(7,3)"
83
- #ids_to_s([[1,2],[7,3]], ',', ';') -> "1,2;7,3"
84
- def ids_to_s(many_ids, id_sep = CompositePrimaryKeys::ID_SEP, list_sep = ',', left_bracket = '(', right_bracket = ')')
85
- many_ids.map {|ids| "#{left_bracket}#{CompositePrimaryKeys::CompositeKeys.new(ids)}#{right_bracket}"}.join(list_sep)
86
- end
87
-
88
- def relation #:nodoc:
89
- @relation ||= begin
90
- result = Relation.new(self, arel_table)
91
- # CPK
92
- class << result
93
- include CompositePrimaryKeys::ActiveRecord::FinderMethods::InstanceMethods
94
- include CompositePrimaryKeys::ActiveRecord::Relation::InstanceMethods
95
- end
96
- result
97
- end
98
-
99
- finder_needs_type_condition? ? @relation.where(type_condition) : @relation
100
- end
101
- end
102
-
103
- module CompositeInstanceMethods
104
- # A model instance's primary keys is always available as model.ids
105
- # whether you name it the default 'id' or set it to something else.
106
- def id
107
- attr_names = self.class.primary_keys
108
- ::CompositePrimaryKeys::CompositeKeys.new(attr_names.map { |attr_name| read_attribute(attr_name) })
109
- end
110
- alias_method :ids, :id
111
-
112
- def ids_hash
113
- self.class.primary_key.zip(ids).inject(Hash.new) do |hash, (key, value)|
114
- hash[key] = value
115
- hash
116
- end
117
- end
118
-
119
- def to_param
120
- id.join(CompositePrimaryKeys::ID_SEP)
121
- end
122
-
123
- def id_before_type_cast #:nodoc:
124
- raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::NOT_IMPLEMENTED_YET
125
- end
126
-
127
- def quoted_id #:nodoc:
128
- [self.class.primary_keys, ids].
129
- transpose.
130
- map {|attr_name,id| quote_value(id, column_for_attribute(attr_name))}.
131
- to_composite_ids
132
- end
133
-
134
- # Sets the primary ID.
135
- def id=(ids)
136
- ids = ids.split(CompositePrimaryKeys::ID_SEP) if ids.is_a?(String)
137
- ids.flatten!
138
- unless ids.is_a?(Array) and ids.length == self.class.primary_keys.length
139
- raise "#{self.class}.id= requires #{self.class.primary_keys.length} ids"
140
- end
141
- [primary_keys, ids].transpose.each {|key, an_id| write_attribute(key , an_id)}
142
- id
143
- end
144
-
145
- # Cloned objects have no id assigned and are treated as new records. Note that this is a "shallow" clone
146
- # as it copies the object's attributes only, not its associations. The extent of a "deep" clone is
147
- # application specific and is therefore left to the application to implement according to its need.
148
- def initialize_copy(other)
149
- # Think the assertion which fails if the after_initialize callback goes at the end of the method is wrong. The
150
- # deleted clone method called new which therefore called the after_initialize callback. It then went on to copy
151
- # over the attributes. But if it's copying the attributes afterwards then it hasn't finished initializing right?
152
- # For example in the test suite the topic model's after_initialize method sets the author_email_address to
153
- # test@test.com. I would have thought this would mean that all cloned models would have an author email address
154
- # of test@test.com. However the test_clone test method seems to test that this is not the case. As a result the
155
- # after_initialize callback has to be run *before* the copying of the atrributes rather than afterwards in order
156
- # for all tests to pass. This makes no sense to me.
157
- callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
158
- cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
159
- # CPK
160
- #cloned_attributes.delete(self.class.primary_key)
161
- self.class.primary_key.each {|key| cloned_attributes.delete(key.to_s)}
162
-
163
- @attributes = cloned_attributes
164
- clear_aggregation_cache
165
- @attributes_cache = {}
166
- @new_record = true
167
- ensure_proper_type
168
-
169
- if scope = self.class.send(:current_scoped_methods)
170
- create_with = scope.scope_for_create
171
- create_with.each { |att,value| self.send("#{att}=", value) } if create_with
172
- end
173
- end
174
-
175
- def destroy
176
- if persisted?
177
- # CPK
178
- # self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).delete_all
179
- self.class.unscoped.where(ids_hash).delete_all
180
- end
181
-
182
- @destroyed = true
183
- freeze
184
- end
185
-
186
- def update(attribute_names = @attributes.keys)
187
- attributes_with_values = arel_attributes_values(false, false, attribute_names)
188
- return 0 if attributes_with_values.empty?
189
- # CPK
190
- # self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.update(attributes_with_values)
191
- self.class.unscoped.where(ids_hash).arel.update(attributes_with_values)
192
- end
193
- end
194
- end
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
+ def set_primary_keys(*keys)
11
+ keys = keys.first if keys.first.is_a?(Array)
12
+
13
+ if keys.length == 1
14
+ set_primary_key(keys.first)
15
+ return
16
+ end
17
+
18
+ cattr_accessor :primary_keys
19
+ self.primary_keys = keys.map { |k| k.to_sym }
20
+
21
+ class_eval <<-EOV
22
+ extend CompositeClassMethods
23
+ include CompositeInstanceMethods
24
+ include CompositePrimaryKeys::ActiveRecord::AssociationPreload
25
+ EOV
26
+ end
27
+
28
+ def composite?
29
+ false
30
+ end
31
+ end
32
+
33
+ def composite?
34
+ self.class.composite?
35
+ end
36
+
37
+ def [](attr_name)
38
+ # CPK
39
+ if attr_name.is_a?(String) and attr_name != attr_name.split(CompositePrimaryKeys::ID_SEP).first
40
+ attr_name = attr_name.split(CompositePrimaryKeys::ID_SEP)
41
+ end
42
+
43
+ # CPK
44
+ if attr_name.is_a?(Array)
45
+ values = attr_name.map {|name| read_attribute(name)}
46
+ CompositePrimaryKeys::CompositeKeys.new(values)
47
+ else
48
+ read_attribute(attr_name)
49
+ end
50
+ end
51
+
52
+ def []=(attr_name, value)
53
+ # CPK
54
+ if attr_name.is_a?(String) and attr_name != attr_name.split(CompositePrimaryKeys::ID_SEP).first
55
+ attr_name = attr_name.split(CompositePrimaryKeys::ID_SEP)
56
+ end
57
+
58
+ if attr_name.is_a? Array
59
+ unless value.length == attr_name.length
60
+ raise "Number of attr_names and values do not match"
61
+ end
62
+ [attr_name, value].transpose.map {|name,val| write_attribute(name, val)}
63
+ value
64
+ else
65
+ write_attribute(attr_name, value)
66
+ end
67
+ end
68
+
69
+ module CompositeClassMethods
70
+ def primary_key
71
+ primary_keys
72
+ end
73
+
74
+ def primary_key=(keys)
75
+ primary_keys = keys
76
+ end
77
+
78
+ def composite?
79
+ true
80
+ end
81
+
82
+ #ids_to_s([[1,2],[7,3]]) -> "(1,2),(7,3)"
83
+ #ids_to_s([[1,2],[7,3]], ',', ';') -> "1,2;7,3"
84
+ def ids_to_s(many_ids, id_sep = CompositePrimaryKeys::ID_SEP, list_sep = ',', left_bracket = '(', right_bracket = ')')
85
+ many_ids.map {|ids| "#{left_bracket}#{CompositePrimaryKeys::CompositeKeys.new(ids)}#{right_bracket}"}.join(list_sep)
86
+ end
87
+
88
+ def relation #:nodoc:
89
+ @relation ||= begin
90
+ result = Relation.new(self, arel_table)
91
+ # CPK
92
+ class << result
93
+ include CompositePrimaryKeys::ActiveRecord::FinderMethods::InstanceMethods
94
+ include CompositePrimaryKeys::ActiveRecord::Relation::InstanceMethods
95
+ end
96
+ result
97
+ end
98
+
99
+ finder_needs_type_condition? ? @relation.where(type_condition) : @relation
100
+ end
101
+ end
102
+
103
+ module CompositeInstanceMethods
104
+ # A model instance's primary keys is always available as model.ids
105
+ # whether you name it the default 'id' or set it to something else.
106
+ def id
107
+ attr_names = self.class.primary_keys
108
+ ::CompositePrimaryKeys::CompositeKeys.new(attr_names.map { |attr_name| read_attribute(attr_name) })
109
+ end
110
+ alias_method :ids, :id
111
+
112
+ def ids_hash
113
+ self.class.primary_key.zip(ids).inject(Hash.new) do |hash, (key, value)|
114
+ hash[key] = value
115
+ hash
116
+ end
117
+ end
118
+
119
+ def id_before_type_cast #:nodoc:
120
+ raise CompositeKeyError, CompositePrimaryKeys::ActiveRecord::Base::NOT_IMPLEMENTED_YET
121
+ end
122
+
123
+ def quoted_id #:nodoc:
124
+ [self.class.primary_keys, ids].
125
+ transpose.
126
+ map {|attr_name,id| quote_value(id, column_for_attribute(attr_name))}.
127
+ to_composite_ids
128
+ end
129
+
130
+ # Sets the primary ID.
131
+ def id=(ids)
132
+ ids = ids.split(CompositePrimaryKeys::ID_SEP) if ids.is_a?(String)
133
+ ids.flatten!
134
+ unless ids.is_a?(Array) and ids.length == self.class.primary_keys.length
135
+ raise "#{self.class}.id= requires #{self.class.primary_keys.length} ids"
136
+ end
137
+ [primary_keys, ids].transpose.each {|key, an_id| write_attribute(key , an_id)}
138
+ id
139
+ end
140
+
141
+ # Cloned objects have no id assigned and are treated as new records. Note that this is a "shallow" clone
142
+ # as it copies the object's attributes only, not its associations. The extent of a "deep" clone is
143
+ # application specific and is therefore left to the application to implement according to its need.
144
+ def initialize_copy(other)
145
+ # Think the assertion which fails if the after_initialize callback goes at the end of the method is wrong. The
146
+ # deleted clone method called new which therefore called the after_initialize callback. It then went on to copy
147
+ # over the attributes. But if it's copying the attributes afterwards then it hasn't finished initializing right?
148
+ # For example in the test suite the topic model's after_initialize method sets the author_email_address to
149
+ # test@test.com. I would have thought this would mean that all cloned models would have an author email address
150
+ # of test@test.com. However the test_clone test method seems to test that this is not the case. As a result the
151
+ # after_initialize callback has to be run *before* the copying of the atrributes rather than afterwards in order
152
+ # for all tests to pass. This makes no sense to me.
153
+ callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
154
+ cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
155
+ # CPK
156
+ #cloned_attributes.delete(self.class.primary_key)
157
+ self.class.primary_key.each {|key| cloned_attributes.delete(key.to_s)}
158
+
159
+ @attributes = cloned_attributes
160
+ clear_aggregation_cache
161
+ @attributes_cache = {}
162
+ @new_record = true
163
+ ensure_proper_type
164
+
165
+ if scope = self.class.send(:current_scoped_methods)
166
+ create_with = scope.scope_for_create
167
+ create_with.each { |att,value| self.send("#{att}=", value) } if create_with
168
+ end
169
+ end
170
+
171
+ def destroy
172
+ if persisted?
173
+ # CPK
174
+ # self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).delete_all
175
+ self.class.unscoped.where(ids_hash).delete_all
176
+ end
177
+
178
+ @destroyed = true
179
+ freeze
180
+ end
181
+
182
+ def update(attribute_names = @attributes.keys)
183
+ attributes_with_values = arel_attributes_values(false, false, attribute_names)
184
+ return 0 if attributes_with_values.empty?
185
+ # CPK
186
+ # self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.update(attributes_with_values)
187
+ self.class.unscoped.where(ids_hash).arel.update(attributes_with_values)
188
+ end
189
+ end
190
+ end
195
191
  end