composite_primary_keys 8.1.8 → 9.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +5 -5
  2. data/History.rdoc +3 -25
  3. data/README.rdoc +1 -0
  4. data/README_DB2.rdoc +33 -33
  5. data/Rakefile +34 -34
  6. data/lib/composite_primary_keys.rb +4 -11
  7. data/lib/composite_primary_keys/associations/association.rb +14 -12
  8. data/lib/composite_primary_keys/associations/association_scope.rb +27 -54
  9. data/lib/composite_primary_keys/associations/collection_association.rb +22 -8
  10. data/lib/composite_primary_keys/associations/has_many_association.rb +16 -54
  11. data/lib/composite_primary_keys/associations/has_many_through_association.rb +58 -58
  12. data/lib/composite_primary_keys/associations/join_dependency.rb +74 -56
  13. data/lib/composite_primary_keys/associations/join_dependency/join_association.rb +13 -11
  14. data/lib/composite_primary_keys/associations/preloader/association.rb +75 -72
  15. data/lib/composite_primary_keys/associations/singular_association.rb +8 -12
  16. data/lib/composite_primary_keys/attribute_methods.rb +6 -4
  17. data/lib/composite_primary_keys/attribute_methods/primary_key.rb +13 -11
  18. data/lib/composite_primary_keys/attribute_methods/read.rb +16 -15
  19. data/lib/composite_primary_keys/attribute_methods/write.rb +21 -19
  20. data/lib/composite_primary_keys/attribute_set/builder.rb +13 -11
  21. data/lib/composite_primary_keys/base.rb +5 -69
  22. data/lib/composite_primary_keys/composite_arrays.rb +8 -51
  23. data/lib/composite_primary_keys/composite_predicates.rb +7 -16
  24. data/lib/composite_primary_keys/connection_adapters/abstract_adapter.rb +10 -10
  25. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +42 -11
  26. data/lib/composite_primary_keys/core.rb +46 -45
  27. data/lib/composite_primary_keys/dirty.rb +19 -19
  28. data/lib/composite_primary_keys/fixtures.rb +19 -17
  29. data/lib/composite_primary_keys/locking/optimistic.rb +48 -44
  30. data/lib/composite_primary_keys/nested_attributes.rb +64 -53
  31. data/lib/composite_primary_keys/persistence.rb +49 -41
  32. data/lib/composite_primary_keys/relation.rb +22 -47
  33. data/lib/composite_primary_keys/relation/batches.rb +33 -30
  34. data/lib/composite_primary_keys/relation/calculations.rb +3 -7
  35. data/lib/composite_primary_keys/relation/finder_methods.rb +123 -56
  36. data/lib/composite_primary_keys/relation/predicate_builder.rb +18 -29
  37. data/lib/composite_primary_keys/relation/where_clause.rb +33 -0
  38. data/lib/composite_primary_keys/sanitization.rb +45 -38
  39. data/lib/composite_primary_keys/validations/uniqueness.rb +37 -37
  40. data/lib/composite_primary_keys/version.rb +4 -4
  41. data/scripts/console.rb +48 -48
  42. data/scripts/txt2html +76 -76
  43. data/scripts/txt2js +65 -65
  44. data/tasks/databases/mysql.rake +42 -42
  45. data/tasks/databases/postgresql.rake +47 -47
  46. data/tasks/databases/sqlite3.rake +27 -27
  47. data/tasks/website.rake +18 -18
  48. data/test/README_tests.rdoc +56 -56
  49. data/test/abstract_unit.rb +10 -9
  50. data/test/connections/connection_spec.rb +18 -18
  51. data/test/connections/databases.yml +9 -39
  52. data/test/connections/native_ibm_db/connection.rb +18 -18
  53. data/test/connections/native_mysql/connection.rb +17 -17
  54. data/test/connections/native_postgresql/connection.rb +12 -12
  55. data/test/connections/native_sqlite3/connection.rb +9 -9
  56. data/test/db_test.rb +52 -52
  57. data/test/fixtures/article.rb +5 -5
  58. data/test/fixtures/articles.yml +6 -6
  59. data/test/fixtures/capitol.rb +3 -3
  60. data/test/fixtures/capitols.yml +16 -16
  61. data/test/fixtures/comments.yml +15 -15
  62. data/test/fixtures/db_definitions/mysql.sql +2 -12
  63. data/test/fixtures/db_definitions/oracle.sql +1 -2
  64. data/test/fixtures/db_definitions/postgresql.sql +0 -10
  65. data/test/fixtures/db_definitions/sqlite.sql +0 -9
  66. data/test/fixtures/db_definitions/sqlserver.sql +1 -2
  67. data/test/fixtures/department.rb +5 -5
  68. data/test/fixtures/departments.yml +15 -15
  69. data/test/fixtures/dorms.yml +4 -4
  70. data/test/fixtures/employee.rb +1 -2
  71. data/test/fixtures/employees.yml +19 -23
  72. data/test/fixtures/group.rb +2 -2
  73. data/test/fixtures/groups.yml +6 -6
  74. data/test/fixtures/hack.rb +4 -4
  75. data/test/fixtures/hacks.yml +2 -2
  76. data/test/fixtures/membership_status.rb +2 -2
  77. data/test/fixtures/product.rb +9 -9
  78. data/test/fixtures/product_tariff.rb +5 -5
  79. data/test/fixtures/products.yml +11 -11
  80. data/test/fixtures/reading.rb +4 -4
  81. data/test/fixtures/readings.yml +10 -10
  82. data/test/fixtures/reference_code_using_composite_key_alias.rb +8 -8
  83. data/test/fixtures/reference_code_using_simple_key_alias.rb +8 -8
  84. data/test/fixtures/reference_codes.yml +28 -28
  85. data/test/fixtures/reference_type.rb +1 -1
  86. data/test/fixtures/reference_types.yml +9 -9
  87. data/test/fixtures/restaurant.rb +9 -9
  88. data/test/fixtures/restaurants.yml +14 -14
  89. data/test/fixtures/restaurants_suburbs.yml +10 -10
  90. data/test/fixtures/room.rb +11 -11
  91. data/test/fixtures/room_assignment.rb +13 -13
  92. data/test/fixtures/room_assignments.yml +24 -24
  93. data/test/fixtures/room_attribute.rb +2 -2
  94. data/test/fixtures/room_attribute_assignment.rb +4 -4
  95. data/test/fixtures/room_attribute_assignments.yml +4 -4
  96. data/test/fixtures/room_attributes.yml +2 -2
  97. data/test/fixtures/rooms.yml +12 -12
  98. data/test/fixtures/seat.rb +5 -5
  99. data/test/fixtures/seats.yml +8 -8
  100. data/test/fixtures/street.rb +2 -2
  101. data/test/fixtures/streets.yml +16 -16
  102. data/test/fixtures/student.rb +3 -3
  103. data/test/fixtures/students.yml +15 -15
  104. data/test/fixtures/suburbs.yml +14 -14
  105. data/test/fixtures/tariff.rb +5 -5
  106. data/test/fixtures/tariffs.yml +14 -14
  107. data/test/fixtures/user.rb +0 -1
  108. data/test/plugins/pagination.rb +405 -405
  109. data/test/plugins/pagination_helper.rb +135 -135
  110. data/test/setup.rb +50 -50
  111. data/test/test_aliases.rb +18 -18
  112. data/test/test_associations.rb +7 -18
  113. data/test/test_composite_arrays.rb +24 -38
  114. data/test/test_counter_cache.rb +30 -30
  115. data/test/test_create.rb +5 -5
  116. data/test/test_delete_all.rb +7 -13
  117. data/test/test_dup.rb +37 -37
  118. data/test/test_exists.rb +39 -39
  119. data/test/test_find.rb +16 -12
  120. data/test/test_habtm.rb +26 -2
  121. data/test/test_ids.rb +109 -116
  122. data/test/test_miscellaneous.rb +32 -32
  123. data/test/test_pagination.rb +35 -35
  124. data/test/test_polymorphic.rb +0 -7
  125. data/test/test_predicates.rb +9 -28
  126. data/test/test_update.rb +3 -5
  127. data/test/test_validations.rb +13 -13
  128. metadata +24 -32
  129. data/lib/composite_primary_keys/arel/visitors/to_sql.rb +0 -36
  130. data/lib/composite_primary_keys/attribute_methods/dirty.rb +0 -29
  131. data/lib/composite_primary_keys/autosave_association.rb +0 -67
  132. data/lib/composite_primary_keys/connection_adapters/abstract_mysql_adapter.rb +0 -23
  133. data/test/fixtures/pk_called_id.rb +0 -5
  134. data/test/fixtures/pk_called_ids.yml +0 -11
  135. data/test/test_find_in_batches.rb +0 -30
  136. data/test/test_update_all.rb +0 -17
@@ -1,17 +1,48 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters
3
- module PostgreSQL
4
- module Quoting
5
- def quote_column_name(name)
3
+ class PostgreSQLAdapter
4
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
5
+ unless pk
6
+ # Extract the table from the insert sql. Yuck.
7
+ table_ref = extract_table_ref_from_insert_sql(sql)
8
+ pk = primary_key(table_ref) if table_ref
9
+ end
10
+
11
+ if pk && use_insert_returning?
6
12
  # CPK
7
- # PGconn.quote_ident(name.to_s)
8
- if name.is_a?(Array)
9
- name.map do |column|
10
- PGconn.quote_ident(column.to_s)
11
- end.join(', ')
12
- else
13
- PGconn.quote_ident(name.to_s)
14
- end
13
+ # select_value("#{sql} RETURNING #{quote_column_names(pk)}")
14
+ select_value("#{sql} RETURNING #{quote_column_names(pk)}")
15
+ elsif pk
16
+ super
17
+ last_insert_id_value(sequence_name || default_sequence_name(table_ref, pk))
18
+ else
19
+ super
20
+ end
21
+ end
22
+ alias :create :insert
23
+
24
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
25
+ unless pk
26
+ # Extract the table from the insert sql. Yuck.
27
+ table_ref = extract_table_ref_from_insert_sql(sql)
28
+ pk = primary_key(table_ref) if table_ref
29
+ end
30
+
31
+ if pk && use_insert_returning?
32
+ # CPK
33
+ # sql = "#{sql} RETURNING #{quote_column_names(pk)}"
34
+ sql = "#{sql} RETURNING #{quote_column_names(pk)}"
35
+ end
36
+
37
+ [sql, binds]
38
+ end
39
+
40
+ # Returns a single value if query returns a single element
41
+ # otherwise returns an array corresponding to the composite keys
42
+ def last_inserted_id(result)
43
+ row = result && result.rows.first
44
+ if Array === row
45
+ row.size == 1 ? row[0] : row
15
46
  end
16
47
  end
17
48
  end
@@ -1,60 +1,61 @@
1
1
  module ActiveRecord
2
2
  module Core
3
- def initialize_dup(other) # :nodoc:
4
- @attributes = @attributes.dup
5
- # CPK
6
- # @attributes.reset(self.class.primary_key)
7
- Array(self.class.primary_key).each {|key| @attributes.reset(key)}
3
+ silence_warnings do
4
+ def initialize_dup(other) # :nodoc:
5
+ @attributes = @attributes.dup
6
+ # CPK
7
+ # @attributes.reset(self.class.primary_key)
8
+ Array(self.class.primary_key).each {|key| @attributes.reset(key)}
8
9
 
9
- run_callbacks(:initialize) unless _initialize_callbacks.empty?
10
+ run_callbacks(:initialize) unless _initialize_callbacks.empty?
10
11
 
11
- @aggregation_cache = {}
12
- @association_cache = {}
12
+ @aggregation_cache = {}
13
+ @association_cache = {}
13
14
 
14
- @new_record = true
15
- @destroyed = false
15
+ @new_record = true
16
+ @destroyed = false
16
17
 
17
- super
18
+ super
19
+ end
18
20
  end
19
21
 
20
22
  module ClassMethods
21
- def find(*ids) # :nodoc:
22
- # We don't have cache keys for this stuff yet
23
- return super unless ids.length == 1
24
- # Allow symbols to super to maintain compatibility for deprecated finders until Rails 5
25
- return super if ids.first.kind_of?(Symbol)
26
- return super if block_given? ||
27
- primary_key.nil? ||
28
- default_scopes.any? ||
29
- current_scope ||
30
- columns_hash.include?(inheritance_column) ||
31
- ids.first.kind_of?(Array)
32
-
33
- # CPK
34
- return super if self.composite?
35
-
36
- id = ids.first
37
- if ActiveRecord::Base === id
38
- id = id.id
39
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
40
- You are passing an instance of ActiveRecord::Base to `find`.
41
- Please pass the id of the object by calling `.id`
42
- MSG
43
- end
44
- key = primary_key
45
-
46
- s = find_by_statement_cache[key] || find_by_statement_cache.synchronize {
47
- find_by_statement_cache[key] ||= StatementCache.create(connection) { |params|
23
+ silence_warnings do
24
+ def find(*ids) # :nodoc:
25
+ # We don't have cache keys for this stuff yet
26
+ return super unless ids.length == 1
27
+ return super if block_given? ||
28
+ primary_key.nil? ||
29
+ scope_attributes? ||
30
+ columns_hash.include?(inheritance_column) ||
31
+ ids.first.kind_of?(Array)
32
+ # CPK
33
+ return super if self.composite?
34
+
35
+ id = ids.first
36
+ if ActiveRecord::Base === id
37
+ id = id.id
38
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
39
+ You are passing an instance of ActiveRecord::Base to `find`.
40
+ Please pass the id of the object by calling `.id`
41
+ MSG
42
+ end
43
+
44
+ key = primary_key
45
+
46
+ statement = cached_find_by_statement(key) { |params|
48
47
  where(key => params.bind).limit(1)
49
48
  }
50
- }
51
- record = s.execute([id], self, connection).first
52
- unless record
53
- raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"
49
+ record = statement.execute([id], self, connection).first
50
+ unless record
51
+ raise RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}",
52
+ name, primary_key, id)
53
+ end
54
+ record
55
+ rescue RangeError
56
+ raise RecordNotFound.new("Couldn't find #{name} with an out of range value for '#{primary_key}'",
57
+ name, primary_key)
54
58
  end
55
- record
56
- rescue RangeError
57
- raise RecordNotFound, "Couldn't find #{name} with an out of range value for '#{primary_key}'"
58
59
  end
59
60
  end
60
61
  end
@@ -1,19 +1,19 @@
1
- module ActiveModel
2
- module Dirty
3
- def can_change_primary_key?
4
- true
5
- end
6
-
7
- def primary_key_changed?
8
- !!changed.detect { |key| ids_hash.keys.include?(key.to_s) }
9
- end
10
-
11
- def primary_key_was
12
- ids_hash.keys.inject(Hash.new) do |result, attribute_name|
13
- result[attribute_name] = attribute_was(attribute_name.to_s)
14
- result
15
- end
16
- end
17
- alias_method :ids_hash_was, :primary_key_was
18
- end
19
- end
1
+ module ActiveModel
2
+ module Dirty
3
+ def can_change_primary_key?
4
+ true
5
+ end
6
+
7
+ def primary_key_changed?
8
+ !!changed.detect { |key| ids_hash.keys.include?(key.to_s) }
9
+ end
10
+
11
+ def primary_key_was
12
+ ids_hash.keys.inject(Hash.new) do |result, attribute_name|
13
+ result[attribute_name] = attribute_was(attribute_name.to_s)
14
+ result
15
+ end
16
+ end
17
+ alias_method :ids_hash_was, :primary_key_was
18
+ end
19
+ end
@@ -1,25 +1,27 @@
1
1
  module ActiveRecord
2
2
  class Fixture
3
- def find
4
- if model_class
5
- # CPK
6
- # model_class.unscoped do
7
- # model_class.find(fixture[model_class.primary_key])
8
- # end
9
- model_class.unscoped do
10
- ids = self.ids(model_class.primary_key)
11
- model_class.find(ids)
3
+ silence_warnings do
4
+ def find
5
+ if model_class
6
+ # CPK
7
+ # model_class.unscoped do
8
+ # model_class.find(fixture[model_class.primary_key])
9
+ # end
10
+ model_class.unscoped do
11
+ ids = self.ids(model_class.primary_key)
12
+ model_class.find(ids)
13
+ end
14
+ else
15
+ raise FixtureClassNotFound, "No class attached to find."
12
16
  end
13
- else
14
- raise FixtureClassNotFound, "No class attached to find."
15
17
  end
16
- end
17
18
 
18
- def ids(key)
19
- if key.is_a? Array
20
- key.map {|a_key| fixture[a_key.to_s] }
21
- else
22
- fixture[key]
19
+ def ids(key)
20
+ if key.is_a? Array
21
+ key.map {|a_key| fixture[a_key.to_s] }
22
+ else
23
+ fixture[key]
24
+ end
23
25
  end
24
26
  end
25
27
  end
@@ -1,54 +1,58 @@
1
- module ActiveRecord
2
- module Locking
3
- module Optimistic
4
- private
5
- def _update_record(attribute_names = @attributes.keys) #:nodoc:
6
- return super unless locking_enabled?
7
- return 0 if attribute_names.empty?
8
-
9
- lock_col = self.class.locking_column
10
- previous_lock_value = send(lock_col).to_i
11
- increment_lock
12
-
13
- attribute_names += [lock_col]
14
- attribute_names.uniq!
15
-
16
- begin
17
- relation = self.class.unscoped
18
-
19
- if self.composite?
20
- affected_rows = relation.where(
21
- relation.cpk_id_predicate(relation.table, self.class.primary_key, id_was)
22
- ).where(
23
- lock_col => previous_lock_value
24
- ).update_all(
25
- Hash[attributes_for_update(attribute_names).map do |name|
26
- [name, _read_attribute(name)]
27
- end]
28
- )
29
- else
30
- affected_rows = relation.where(
31
- self.class.primary_key => id,
32
- lock_col => previous_lock_value,
33
- ).update_all(
34
- Hash[attributes_for_update(attribute_names).map do |name|
1
+ module ActiveRecord
2
+ module Locking
3
+ module Optimistic
4
+
5
+ private
6
+
7
+ silence_warnings do
8
+ def _update_record(attribute_names = @attributes.keys) #:nodoc:
9
+ return super unless locking_enabled?
10
+ return 0 if attribute_names.empty?
11
+
12
+ lock_col = self.class.locking_column
13
+ previous_lock_value = send(lock_col).to_i
14
+ increment_lock
15
+
16
+ attribute_names += [lock_col]
17
+ attribute_names.uniq!
18
+
19
+ begin
20
+ relation = self.class.unscoped
21
+
22
+ if self.composite?
23
+ affected_rows = relation.where(
24
+ relation.cpk_id_predicate(relation.table, self.class.primary_key, id_was)
25
+ ).where(
26
+ lock_col => previous_lock_value
27
+ ).update_all(
28
+ Hash[attributes_for_update(attribute_names).map do |name|
35
29
  [name, _read_attribute(name)]
36
30
  end]
37
- )
38
- end
31
+ )
32
+ else
33
+ affected_rows = relation.where(
34
+ self.class.primary_key => id,
35
+ lock_col => previous_lock_value,
36
+ ).update_all(
37
+ Hash[attributes_for_update(attribute_names).map do |name|
38
+ [name, _read_attribute(name)]
39
+ end]
40
+ )
41
+ end
39
42
 
40
- unless affected_rows == 1
41
- raise ActiveRecord::StaleObjectError.new(self, "update")
42
- end
43
+ unless affected_rows == 1
44
+ raise ActiveRecord::StaleObjectError.new(self, "update")
45
+ end
43
46
 
44
- affected_rows
47
+ affected_rows
45
48
 
46
- # If something went wrong, revert the version.
47
- rescue Exception
48
- send(lock_col + '=', previous_lock_value)
49
- raise
49
+ # If something went wrong, revert the version.
50
+ rescue Exception
51
+ send(lock_col + '=', previous_lock_value)
52
+ raise
50
53
  end
51
54
  end
55
+ end
52
56
  end
53
57
  end
54
58
  end
@@ -8,70 +8,81 @@ module ActiveRecord
8
8
  end
9
9
  end
10
10
 
11
- def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
12
- options = self.nested_attributes_options[association_name]
11
+ silence_warnings do
12
+ def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
13
+ options = self.nested_attributes_options[association_name]
13
14
 
14
- unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
15
- raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
16
- end
15
+ if attributes_collection.respond_to?(:permitted?)
16
+ attributes_collection = attributes_collection.to_h
17
+ end
17
18
 
18
- check_record_limit!(options[:limit], attributes_collection)
19
+ unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
20
+ raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
21
+ end
19
22
 
20
- if attributes_collection.is_a? Hash
21
- keys = attributes_collection.keys
22
- attributes_collection = if keys.include?('id') || keys.include?(:id)
23
- [attributes_collection]
24
- else
25
- attributes_collection.values
26
- end
27
- end
23
+ check_record_limit!(options[:limit], attributes_collection)
28
24
 
29
- association = association(association_name)
25
+ if attributes_collection.is_a? Hash
26
+ keys = attributes_collection.keys
27
+ attributes_collection = if keys.include?('id') || keys.include?(:id)
28
+ [attributes_collection]
29
+ else
30
+ attributes_collection.values
31
+ end
32
+ end
30
33
 
31
- existing_records = if association.loaded?
32
- association.target
33
- # CPK
34
- elsif association.reflection.klass.composite?
35
- attributes_collection.map do |attribute_collection|
36
- attribute_ids = attribute_collection['id'] || attribute_collection[:id]
37
- if attribute_ids
38
- ids = CompositePrimaryKeys::CompositeKeys.parse(attribute_ids)
39
- eq_predicates = association.klass.primary_key.zip(ids).map do |primary_key, value|
40
- association.klass.arel_table[primary_key].eq(value)
34
+ association = association(association_name)
35
+
36
+ existing_records = if association.loaded?
37
+ association.target
38
+ # CPK
39
+ elsif association.reflection.klass.composite?
40
+ attributes_collection.map do |attribute_collection|
41
+ attribute_ids = attribute_collection['id'] || attribute_collection[:id]
42
+ if attribute_ids
43
+ ids = CompositePrimaryKeys::CompositeKeys.parse(attribute_ids)
44
+ eq_predicates = Class.new.extend(CompositePrimaryKeys::Predicates).cpk_id_predicate(association.klass.arel_table, association.klass.primary_key, ids)
45
+ association.scope.where(eq_predicates).to_a
46
+ else
47
+ []
41
48
  end
42
- association.scope.where(*eq_predicates).to_a
43
- else
44
- []
45
- end
46
- end.flatten.compact
47
- else
48
- attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
49
- attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
50
- end
49
+ end.flatten.compact
50
+ else
51
+ attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
52
+ attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
53
+ end
51
54
 
52
- attributes_collection.each do |attributes|
53
- attributes = attributes.with_indifferent_access
54
- if attributes['id'].blank?
55
- unless reject_new_record?(association_name, attributes)
56
- association.build(attributes.except(*UNASSIGNABLE_KEYS))
55
+ attributes_collection.each do |attributes|
56
+ if attributes.respond_to?(:permitted?)
57
+ attributes = attributes.to_h
57
58
  end
58
- elsif existing_record = cpk_detect_record(attributes['id'], existing_records)
59
- unless call_reject_if(association_name, attributes)
60
- # Make sure we are operating on the actual object which is in the association's
61
- # proxy_target array (either by finding it, or adding it if not found)
62
- # Take into account that the proxy_target may have changed due to callbacks
63
- target_record = cpk_detect_record(attributes['id'], association.target)
64
-
65
- if target_record
66
- existing_record = target_record
67
- else
68
- association.add_to_target(existing_record, :skip_callbacks)
59
+ attributes = attributes.with_indifferent_access
60
+ if attributes['id'].blank?
61
+ unless reject_new_record?(association_name, attributes)
62
+ new_record = association.build(attributes.except(*UNASSIGNABLE_KEYS))
63
+ public_send("#{self.attributes.keys.first}_will_change!") # TODO CPK: Note: Is this a bug in Rails? We need to somehow set the parent model as changed even when changes are only adding new records to an association. This is not the way to do it, but I am not sure how it should be done.
64
+ new_record
69
65
  end
66
+ elsif existing_record = cpk_detect_record(attributes['id'], existing_records)
67
+ unless call_reject_if(association_name, attributes)
68
+ # Make sure we are operating on the actual object which is in the association's
69
+ # proxy_target array (either by finding it, or adding it if not found)
70
+ # Take into account that the proxy_target may have changed due to callbacks
71
+ target_record = cpk_detect_record(attributes['id'], association.target)
70
72
 
71
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
73
+ if target_record
74
+ existing_record = target_record
75
+ else
76
+ association.add_to_target(existing_record, :skip_callbacks)
77
+ end
78
+
79
+ result_from_assign = assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
80
+ public_send("#{self.attributes.keys.first}_will_change!") # TODO CPK: Note: Is this a bug in Rails? We need to somehow set the parent model as changed even when changes are only adding new records to an association. This is not the way to do it, but I am not sure how it should be done.
81
+ result_from_assign
82
+ end
83
+ else
84
+ raise_nested_attributes_record_not_found!(association_name, attributes['id'])
72
85
  end
73
- else
74
- raise_nested_attributes_record_not_found!(association_name, attributes['id'])
75
86
  end
76
87
  end
77
88
  end