composite_primary_keys 3.0.9 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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