composite_primary_keys 7.0.16 → 8.0.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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/History.rdoc +600 -623
  3. data/lib/composite_primary_keys.rb +113 -115
  4. data/lib/composite_primary_keys/associations/association.rb +23 -23
  5. data/lib/composite_primary_keys/associations/association_scope.rb +73 -77
  6. data/lib/composite_primary_keys/associations/collection_association.rb +15 -0
  7. data/lib/composite_primary_keys/associations/has_many_association.rb +69 -56
  8. data/lib/composite_primary_keys/associations/has_many_through_association.rb +30 -28
  9. data/lib/composite_primary_keys/associations/join_dependency.rb +87 -89
  10. data/lib/composite_primary_keys/associations/join_dependency/join_association.rb +22 -22
  11. data/lib/composite_primary_keys/associations/preloader/association.rb +90 -78
  12. data/lib/composite_primary_keys/associations/preloader/belongs_to.rb +19 -19
  13. data/lib/composite_primary_keys/associations/singular_association.rb +15 -0
  14. data/lib/composite_primary_keys/attribute_methods.rb +9 -0
  15. data/lib/composite_primary_keys/attribute_methods/dirty.rb +29 -26
  16. data/lib/composite_primary_keys/attribute_methods/read.rb +19 -34
  17. data/lib/composite_primary_keys/attribute_methods/write.rb +30 -36
  18. data/lib/composite_primary_keys/attribute_set/builder.rb +20 -0
  19. data/lib/composite_primary_keys/base.rb +135 -129
  20. data/lib/composite_primary_keys/composite_arrays.rb +30 -30
  21. data/lib/composite_primary_keys/composite_predicates.rb +50 -50
  22. data/lib/composite_primary_keys/composite_relation.rb +48 -48
  23. data/lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb +2 -4
  24. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +46 -60
  25. data/lib/composite_primary_keys/core.rb +69 -47
  26. data/lib/composite_primary_keys/fixtures.rb +22 -22
  27. data/lib/composite_primary_keys/persistence.rb +56 -60
  28. data/lib/composite_primary_keys/relation.rb +68 -56
  29. data/lib/composite_primary_keys/relation/calculations.rb +79 -75
  30. data/lib/composite_primary_keys/relation/finder_methods.rb +175 -196
  31. data/lib/composite_primary_keys/relation/query_methods.rb +40 -40
  32. data/lib/composite_primary_keys/sanitization.rb +52 -52
  33. data/lib/composite_primary_keys/validations/uniqueness.rb +36 -37
  34. data/lib/composite_primary_keys/version.rb +8 -8
  35. data/tasks/databases/oracle.rake +25 -25
  36. data/tasks/databases/sqlserver.rake +27 -40
  37. data/test/abstract_unit.rb +113 -113
  38. data/test/connections/databases.ci.yml +15 -15
  39. data/test/connections/databases.example.yml +18 -18
  40. data/test/connections/native_oracle/connection.rb +11 -11
  41. data/test/connections/native_oracle_enhanced/connection.rb +16 -16
  42. data/test/connections/native_sqlserver/connection.rb +11 -14
  43. data/test/fixtures/comment.rb +7 -7
  44. data/test/fixtures/db_definitions/db2-create-tables.sql +125 -126
  45. data/test/fixtures/db_definitions/db2-drop-tables.sql +18 -18
  46. data/test/fixtures/db_definitions/mysql.sql +207 -208
  47. data/test/fixtures/db_definitions/oracle.drop.sql +45 -45
  48. data/test/fixtures/db_definitions/oracle.sql +222 -223
  49. data/test/fixtures/db_definitions/postgresql.sql +209 -210
  50. data/test/fixtures/db_definitions/sqlite.sql +196 -197
  51. data/test/fixtures/db_definitions/sqlserver.drop.sql +91 -94
  52. data/test/fixtures/db_definitions/sqlserver.sql +225 -232
  53. data/test/fixtures/dorm.rb +2 -2
  54. data/test/fixtures/employee.rb +5 -5
  55. data/test/fixtures/membership.rb +6 -6
  56. data/test/fixtures/membership_statuses.yml +16 -16
  57. data/test/fixtures/memberships.yml +10 -10
  58. data/test/fixtures/product_tariffs.yml +14 -14
  59. data/test/fixtures/reference_code.rb +7 -7
  60. data/test/fixtures/restaurants_suburb.rb +2 -2
  61. data/test/fixtures/suburb.rb +5 -5
  62. data/test/fixtures/topic.rb +5 -5
  63. data/test/fixtures/topic_source.rb +6 -6
  64. data/test/fixtures/topic_sources.yml +3 -3
  65. data/test/fixtures/topics.yml +8 -8
  66. data/test/fixtures/users.yml +10 -10
  67. data/test/test_associations.rb +295 -275
  68. data/test/test_attribute_methods.rb +63 -63
  69. data/test/test_attributes.rb +60 -60
  70. data/test/test_calculations.rb +37 -42
  71. data/test/test_callbacks.rb +99 -99
  72. data/test/test_create.rb +112 -112
  73. data/test/test_delete.rb +148 -152
  74. data/test/test_delete_all.rb +28 -26
  75. data/test/test_dumpable.rb +15 -15
  76. data/test/test_enum.rb +21 -20
  77. data/test/test_equal.rb +26 -26
  78. data/test/test_find.rb +118 -118
  79. data/test/test_habtm.rb +113 -113
  80. data/test/test_nested_attributes.rb +124 -124
  81. data/test/test_polymorphic.rb +26 -26
  82. data/test/test_predicates.rb +40 -40
  83. data/test/test_santiago.rb +23 -23
  84. data/test/test_suite.rb +33 -34
  85. data/test/test_touch.rb +23 -23
  86. data/test/test_tutorial_example.rb +21 -21
  87. data/test/test_update.rb +71 -71
  88. metadata +9 -13
  89. data/lib/composite_primary_keys/arel/visitors/to_sql.rb +0 -20
  90. data/lib/composite_primary_keys/associations/has_and_belongs_to_many_association.rb +0 -59
  91. data/lib/composite_primary_keys/associations/join_dependency/join_part.rb +0 -39
  92. data/lib/composite_primary_keys/associations/preloader/has_and_belongs_to_many.rb +0 -46
  93. data/lib/composite_primary_keys/connection_adapters/sqlserver_adapter.rb +0 -17
  94. data/lib/composite_primary_keys/locking/optimistic.rb +0 -55
  95. data/test/test_optimistic.rb +0 -18
@@ -1,3 +1,3 @@
1
- class Dorm < ActiveRecord::Base
2
- has_many :rooms, -> {includes(:room_attributes)}, :primary_key => [:id]
1
+ class Dorm < ActiveRecord::Base
2
+ has_many :rooms, -> {includes(:room_attributes)}, :primary_key => [:id]
3
3
  end
@@ -1,5 +1,5 @@
1
- class Employee < ActiveRecord::Base
2
- belongs_to :department, :foreign_key => [:department_id, :location_id]
3
- has_many :comments, :as => :person
4
- has_and_belongs_to_many :groups
5
- end
1
+ class Employee < ActiveRecord::Base
2
+ belongs_to :department, :foreign_key => [:department_id, :location_id]
3
+ has_many :comments, :as => :person
4
+ has_and_belongs_to_many :groups
5
+ end
@@ -1,7 +1,7 @@
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 :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 :readings, :primary_key => :user_id, :foreign_key => :user_id
7
7
  end
@@ -1,17 +1,17 @@
1
- santiago-cpk:
2
- id: 1
3
- user_id: 1
4
- group_id: 1
5
- status: Active
6
-
7
- drnic-cpk:
8
- id: 2
9
- user_id: 2
10
- group_id: 1
11
- status: Owner
12
-
13
- cfis-cpk:
14
- id: 3
15
- user_id: 3
16
- group_id: 2
1
+ santiago-cpk:
2
+ id: 1
3
+ user_id: 1
4
+ group_id: 1
5
+ status: Active
6
+
7
+ drnic-cpk:
8
+ id: 2
9
+ user_id: 2
10
+ group_id: 1
11
+ status: Owner
12
+
13
+ cfis-cpk:
14
+ id: 3
15
+ user_id: 3
16
+ group_id: 2
17
17
  status: Active
@@ -1,11 +1,11 @@
1
- santiago-cpk:
2
- user_id: 1
3
- group_id: 1
4
-
5
- drnic-cpk:
6
- user_id: 2
7
- group_id: 1
8
-
9
- cfis-cpk:
10
- user_id: 3
1
+ santiago-cpk:
2
+ user_id: 1
3
+ group_id: 1
4
+
5
+ drnic-cpk:
6
+ user_id: 2
7
+ group_id: 1
8
+
9
+ cfis-cpk:
10
+ user_id: 3
11
11
  group_id: 2
@@ -1,14 +1,14 @@
1
- first_flat:
2
- product_id: 1
3
- tariff_id: 1
4
- tariff_start_date: <%= Date.today.to_s(:db) %>
5
-
6
- first_free:
7
- product_id: 1
8
- tariff_id: 2
9
- tariff_start_date: <%= Date.today.to_s(:db) %>
10
-
11
- second_free:
12
- product_id: 2
13
- tariff_id: 2
14
- tariff_start_date: <%= Date.today.to_s(:db) %>
1
+ first_flat:
2
+ product_id: 1
3
+ tariff_id: 1
4
+ tariff_start_date: <%= Date.today.to_s(:db) %>
5
+
6
+ first_free:
7
+ product_id: 1
8
+ tariff_id: 2
9
+ tariff_start_date: <%= Date.today.to_s(:db) %>
10
+
11
+ second_free:
12
+ product_id: 2
13
+ tariff_id: 2
14
+ tariff_start_date: <%= Date.today.to_s(:db) %>
@@ -1,7 +1,7 @@
1
- class ReferenceCode < ActiveRecord::Base
2
- self.primary_keys = :reference_type_id, :reference_code
3
-
4
- belongs_to :reference_type, :foreign_key => "reference_type_id"
5
-
6
- validates_presence_of :reference_code, :code_label, :abbreviation
7
- end
1
+ class ReferenceCode < ActiveRecord::Base
2
+ self.primary_keys = :reference_type_id, :reference_code
3
+
4
+ belongs_to :reference_type, :foreign_key => "reference_type_id"
5
+
6
+ validates_presence_of :reference_code, :code_label, :abbreviation
7
+ end
@@ -1,3 +1,3 @@
1
- class RestaurantsSuburb < ActiveRecord::Base
2
-
1
+ class RestaurantsSuburb < ActiveRecord::Base
2
+
3
3
  end
@@ -1,6 +1,6 @@
1
- class Suburb < ActiveRecord::Base
2
- self.primary_keys = :city_id, :suburb_id
3
- has_many :streets, :foreign_key => [:city_id, :suburb_id]
4
- has_many :first_streets, -> {where("streets.name = 'First Street'")},
5
- :foreign_key => [:city_id, :suburb_id], :class_name => 'Street'
1
+ class Suburb < ActiveRecord::Base
2
+ self.primary_keys = :city_id, :suburb_id
3
+ has_many :streets, :foreign_key => [:city_id, :suburb_id]
4
+ has_many :first_streets, -> {where("streets.name = 'First Street'")},
5
+ :foreign_key => [:city_id, :suburb_id], :class_name => 'Street'
6
6
  end
@@ -1,6 +1,6 @@
1
- class Topic < ActiveRecord::Base
2
- has_many :topic_sources, dependent: :destroy
3
- accepts_nested_attributes_for :topic_sources
4
-
5
- validates :name, :feed_size, presence: true
1
+ class Topic < ActiveRecord::Base
2
+ has_many :topic_sources, dependent: :destroy
3
+ accepts_nested_attributes_for :topic_sources
4
+
5
+ validates :name, :feed_size, presence: true
6
6
  end
@@ -1,7 +1,7 @@
1
- class TopicSource < ActiveRecord::Base
2
- self.primary_keys = :topic_id, :platform
3
-
4
- belongs_to :topic, inverse_of: :topic_sources
5
-
6
- validates :platform, presence: true
1
+ class TopicSource < ActiveRecord::Base
2
+ self.primary_keys = :topic_id, :platform
3
+
4
+ belongs_to :topic, inverse_of: :topic_sources
5
+
6
+ validates :platform, presence: true
7
7
  end
@@ -1,4 +1,4 @@
1
- music_source:
2
- topic_id: 1
3
- platform: 'twitter'
1
+ music_source:
2
+ topic_id: 1
3
+ platform: 'twitter'
4
4
  keywords: 'classical'
@@ -1,9 +1,9 @@
1
- music:
2
- id: 1
3
- name: Guitar
4
- feed_size: 500
5
-
6
- iphone:
7
- id: 2
8
- name: iPhone
1
+ music:
2
+ id: 1
3
+ name: Guitar
4
+ feed_size: 500
5
+
6
+ iphone:
7
+ id: 2
8
+ name: iPhone
9
9
  feed_size: 500
@@ -1,11 +1,11 @@
1
- santiago:
2
- id: 1
3
- name: Santiago
4
-
5
- drnic:
6
- id: 2
7
- name: Dr Nic
8
-
9
- cfis:
10
- id: 3
1
+ santiago:
2
+ id: 1
3
+ name: Santiago
4
+
5
+ drnic:
6
+ id: 2
7
+ name: Dr Nic
8
+
9
+ cfis:
10
+ id: 3
11
11
  name: cfis
@@ -1,275 +1,295 @@
1
- require File.expand_path('../abstract_unit', __FILE__)
2
-
3
- class TestAssociations < ActiveSupport::TestCase
4
- fixtures :articles, :products, :tariffs, :product_tariffs, :suburbs, :streets, :restaurants,
5
- :dorms, :rooms, :room_attributes, :room_attribute_assignments, :students, :room_assignments, :users, :readings,
6
- :departments, :employees, :memberships, :membership_statuses
7
-
8
- def test_products
9
- assert_not_nil products(:first_product).product_tariffs
10
- assert_equal 2, products(:first_product).product_tariffs.length
11
- assert_not_nil products(:first_product).tariffs
12
- assert_equal 2, products(:first_product).tariffs.length
13
- end
14
-
15
- def test_product_tariffs
16
- assert_not_nil product_tariffs(:first_flat).product
17
- assert_not_nil product_tariffs(:first_flat).tariff
18
- assert_equal Product, product_tariffs(:first_flat).product.class
19
- assert_equal Tariff, product_tariffs(:first_flat).tariff.class
20
- end
21
-
22
- def test_tariffs
23
- assert_not_nil tariffs(:flat).product_tariffs
24
- assert_equal 1, tariffs(:flat).product_tariffs.length
25
- assert_not_nil tariffs(:flat).products
26
- assert_equal 1, tariffs(:flat).products.length
27
- end
28
-
29
- # Its not generating the instances of associated classes from the rows
30
- def test_find_includes
31
- # Old style
32
- products = Product.includes(:product_tariffs).all
33
- assert_equal(3, products.length)
34
- assert_equal(3, products.inject(0) {|sum, product| sum + product.product_tariffs.length})
35
-
36
- # New style
37
- products = Product.includes(:product_tariffs)
38
- assert_equal(3, products.length)
39
- assert_equal(3, products.inject(0) {|sum, product| sum + product.product_tariffs.length})
40
- end
41
-
42
- def test_find_includes_eager_loading
43
- product = products(:second_product)
44
- product_tarrif = product_tariffs(:second_free)
45
-
46
- # First get a legitimate product tarrif
47
- products = Product.includes(:product_tariffs).where('product_tariffs.product_id = ?', product.id).references(:product_tariffs)
48
- assert_equal(1, products.length)
49
- assert_equal(product, products.first)
50
- assert_equal([product_tarrif], products.first.product_tariffs)
51
- end
52
-
53
- def test_find_eager_loading_none
54
- product = products(:third_product)
55
-
56
- products = Product.includes(:product_tariffs).where(:id => product.id).references(:product_tariffs)
57
- assert_equal(1, products.length)
58
- assert_equal(product, products.first)
59
- assert_empty(products.first.product_tariffs)
60
- end
61
-
62
- def test_find_includes_tariffs
63
- # Old style
64
- tariffs = Tariff.includes(:product_tariffs)
65
- assert_equal(3, tariffs.length)
66
- assert_equal(3, tariffs.inject(0) {|sum, tariff| sum + tariff.product_tariffs.length})
67
-
68
- # New style
69
- tariffs = Tariff.includes(:product_tariffs)
70
- assert_equal(3, tariffs.length)
71
- assert_equal(3, tariffs.inject(0) {|sum, tariff| sum + tariff.product_tariffs.length})
72
- end
73
-
74
- def test_find_includes_product_tariffs_product
75
- # Old style
76
- product_tariffs = ProductTariff.includes(:product)
77
- assert_not_nil(product_tariffs)
78
- assert_equal(3, product_tariffs.length)
79
-
80
- # New style
81
- product_tariffs = ProductTariff.includes(:product)
82
- assert_not_nil(product_tariffs)
83
- assert_equal(3, product_tariffs.length)
84
- end
85
-
86
- def test_find_includes_product_tariffs_tariff
87
- # Old style
88
- product_tariffs = ProductTariff.includes(:tariff)
89
- assert_equal(3, product_tariffs.length)
90
-
91
- # New style
92
- product_tariffs = ProductTariff.includes(:tariff)
93
- assert_equal(3, product_tariffs.length)
94
- end
95
-
96
- def test_has_many_through
97
- products = Product.includes(:tariffs)
98
- assert_equal(3, products.length)
99
-
100
- tarrifs_length = products.inject(0) {|sum, product| sum + product.tariffs.length}
101
- assert_equal(3, tarrifs_length)
102
- end
103
-
104
- def test_new_style_includes_with_conditions
105
- product_tariff = ProductTariff.includes(:tariff).where('tariffs.amount < 5').references(:tariffs).first
106
- assert_equal(0, product_tariff.tariff.amount)
107
- end
108
-
109
- def test_find_product_includes
110
- products = Product.includes(:product_tariffs => :tariff)
111
- assert_equal(3, products.length)
112
-
113
- product_tariffs_length = products.inject(0) {|sum, product| sum + product.product_tariffs.length}
114
- assert_equal(3, product_tariffs_length)
115
- end
116
-
117
- def test_find_tariffs_includes
118
- tariffs = Tariff.includes(:product_tariffs => :product)
119
- assert_equal(3, tariffs.length)
120
-
121
- product_tariffs_length = tariffs.inject(0) {|sum, tariff| sum + tariff.product_tariffs.length}
122
- assert_equal(3, product_tariffs_length)
123
- end
124
-
125
- def test_has_many_through_when_not_pre_loaded
126
- student = Student.first
127
- rooms = student.rooms
128
- assert_equal(1, rooms.size)
129
- assert_equal(1, rooms.first.dorm_id)
130
- assert_equal(1, rooms.first.room_id)
131
- end
132
-
133
- def test_has_many_through_when_through_association_is_composite
134
- dorm = Dorm.first
135
- assert_equal(3, dorm.rooms.length)
136
- assert_equal(1, dorm.rooms.first.room_attributes.length)
137
- assert_equal('type', dorm.rooms.first.room_attributes.first.name)
138
- end
139
-
140
- def test_associations_with_conditions
141
- suburb = Suburb.find([2, 1])
142
- assert_equal 2, suburb.streets.size
143
-
144
- suburb = Suburb.find([2, 1])
145
- assert_equal 1, suburb.first_streets.size
146
-
147
- suburb = Suburb.includes(:streets).find([2, 1])
148
- assert_equal 2, suburb.streets.size
149
-
150
- suburb = Suburb.includes(:first_streets).find([2, 1])
151
- assert_equal 1, suburb.first_streets.size
152
- end
153
-
154
- def test_composite_has_many_composites
155
- room = rooms(:branner_room_1)
156
- assert_equal(2, room.room_assignments.length)
157
- assert_equal(room_assignments(:jacksons_room), room.room_assignments[0])
158
- assert_equal(room_assignments(:bobs_room), room.room_assignments[1])
159
- end
160
-
161
- def test_composite_belongs_to_composite
162
- room_assignment = room_assignments(:jacksons_room)
163
- assert_equal(rooms(:branner_room_1), room_assignment.room)
164
- end
165
-
166
- def test_composite_belongs_to_changes
167
- room_assignment = room_assignments(:jacksons_room)
168
- room_assignment.room = rooms(:branner_room_2)
169
- # This was raising an error before:
170
- # TypeError: [:dorm_id, :room_id] is not a symbol
171
- # changes returns HashWithIndifferentAccess
172
- assert_equal({"room_id"=>[1, 2]}, room_assignment.changes)
173
-
174
- steve = employees(:steve)
175
- steve.department = departments(:engineering)
176
- # It was returning this before:
177
- # {"[:department_id, :location_id]"=>[nil, [2, 1]]}
178
- assert_equal({"department_id"=>[1, 2]}, steve.changes)
179
- end
180
-
181
- def test_composite_belongs_to__setting_to_nil
182
- room_assignment = room_assignments(:jacksons_room)
183
- # This was raising an error before:
184
- # NoMethodError: undefined method `length' for nil:NilClass
185
- assert_nothing_raised { room_assignment.room = nil }
186
- end
187
-
188
- def test_has_one_with_composite
189
- # In this case a regular model has_one composite model
190
- department = departments(:engineering)
191
- assert_not_nil(department.head)
192
- end
193
-
194
- def test_has_many_build__simple_key
195
- user = users(:santiago)
196
- reading = user.readings.build
197
- assert_equal user.id, reading.user_id
198
- assert_equal user, reading.user
199
- end
200
-
201
- def test_has_many_build__composite_key
202
- department = departments(:engineering)
203
- employee = department.employees.build
204
- assert_equal department.department_id, employee.department_id
205
- assert_equal department.location_id, employee.location_id
206
- assert_equal department, employee.department
207
- end
208
-
209
- def test_has_many_with_primary_key
210
- @membership = Membership.find([1, 1])
211
- assert_equal 2, @membership.readings.size
212
- end
213
-
214
- def test_has_many_with_composite_key
215
- # In this case a regular model (Dorm) has_many composite models (Rooms)
216
- dorm = dorms(:branner)
217
- assert_equal(3, dorm.rooms.length)
218
- assert_equal(1, dorm.rooms[0].room_id)
219
- assert_equal(2, dorm.rooms[1].room_id)
220
- assert_equal(3, dorm.rooms[2].room_id)
221
- end
222
-
223
- def test_joins_has_many_with_primary_key
224
- #@membership = Membership.find(:first, :joins => :readings, :conditions => { :readings => { :id => 1 } })
225
- @membership = Membership.joins(:readings).where(readings: { id: 1 }).first
226
-
227
- assert_equal [1, 1], @membership.id
228
- end
229
-
230
- def test_joins_has_one_with_primary_key
231
- @membership = Membership.joins(:readings).where(readings: { id: 2 }).first
232
-
233
- assert_equal [1, 1], @membership.id
234
- end
235
-
236
- def test_has_many_through_with_conditions_when_through_association_is_not_composite
237
- user = User.first
238
- assert_equal 1, user.articles.where("articles.name = ?", "Article One").size
239
- end
240
-
241
- def test_has_many_through_with_conditions_when_through_association_is_composite
242
- room = Room.first
243
- assert_equal 0, room.room_attributes.where("room_attributes.name != ?", "type").size
244
- end
245
-
246
- def test_has_many_through_on_custom_finder_when_through_association_is_composite_finder_when_through_association_is_not_composite
247
- user = User.first
248
- assert_equal(1, user.find_custom_articles.size)
249
- end
250
-
251
- def test_has_many_through_on_custom_finder_when_through_association_is_composite
252
- room = Room.first
253
- assert_equal(0, room.find_custom_room_attributes.size)
254
- end
255
-
256
- def test_has_many_with_primary_key_with_associations
257
- memberships = Membership.includes(:statuses).where("membership_statuses.status = ?", 'Active').references(:membership_statuses)
258
- assert_equal(2, memberships.length)
259
- assert_equal([1,1], memberships[0].id)
260
- assert_equal([3,2], memberships[1].id)
261
- end
262
-
263
- def test_limitable_reflections
264
- memberships = Membership.includes(:statuses).where("membership_statuses.status = ?", 'Active').references(:membership_statuses).limit(1)
265
- assert_equal(1, memberships.length)
266
- assert_equal([1,1], memberships[0].id)
267
- end
268
-
269
- def test_foreign_key_present_with_null_association_ids
270
- group = Group.new
271
- group.memberships.build
272
- associations = group.association_cache[:memberships]
273
- assert_equal(false, associations.send('foreign_key_present?'))
274
- end
275
- end
1
+ require File.expand_path('../abstract_unit', __FILE__)
2
+
3
+ class TestAssociations < ActiveSupport::TestCase
4
+ fixtures :articles, :products, :tariffs, :product_tariffs, :suburbs, :streets, :restaurants,
5
+ :dorms, :rooms, :room_attributes, :room_attribute_assignments, :students, :room_assignments, :users, :readings,
6
+ :departments, :employees, :memberships, :membership_statuses
7
+
8
+ def test_products
9
+ assert_not_nil products(:first_product).product_tariffs
10
+ assert_equal 2, products(:first_product).product_tariffs.length
11
+ assert_not_nil products(:first_product).tariffs
12
+ assert_equal 2, products(:first_product).tariffs.length
13
+ end
14
+
15
+ def test_product_tariffs
16
+ assert_not_nil product_tariffs(:first_flat).product
17
+ assert_not_nil product_tariffs(:first_flat).tariff
18
+ assert_equal Product, product_tariffs(:first_flat).product.class
19
+ assert_equal Tariff, product_tariffs(:first_flat).tariff.class
20
+ end
21
+
22
+ def test_tariffs
23
+ assert_not_nil tariffs(:flat).product_tariffs
24
+ assert_equal 1, tariffs(:flat).product_tariffs.length
25
+ assert_not_nil tariffs(:flat).products
26
+ assert_equal 1, tariffs(:flat).products.length
27
+ end
28
+
29
+ # Its not generating the instances of associated classes from the rows
30
+ def test_find_includes
31
+ # Old style
32
+ products = Product.includes(:product_tariffs).all
33
+ assert_equal(3, products.length)
34
+ assert_equal(3, products.inject(0) {|sum, product| sum + product.product_tariffs.length})
35
+
36
+ # New style
37
+ products = Product.includes(:product_tariffs)
38
+ assert_equal(3, products.length)
39
+ assert_equal(3, products.inject(0) {|sum, product| sum + product.product_tariffs.length})
40
+ end
41
+
42
+ def test_find_includes_eager_loading
43
+ product = products(:second_product)
44
+ product_tarrif = product_tariffs(:second_free)
45
+
46
+ # First get a legitimate product tarrif
47
+ products = Product.includes(:product_tariffs).where('product_tariffs.product_id = ?', product.id).references(:product_tariffs)
48
+ assert_equal(1, products.length)
49
+ assert_equal(product, products.first)
50
+ assert_equal([product_tarrif], products.first.product_tariffs)
51
+ end
52
+
53
+ def test_find_eager_loading_none
54
+ product = products(:third_product)
55
+
56
+ products = Product.includes(:product_tariffs).where(:id => product.id).references(:product_tariffs)
57
+ assert_equal(1, products.length)
58
+ assert_equal(product, products.first)
59
+ assert_empty(products.first.product_tariffs)
60
+ end
61
+
62
+ def test_find_includes_tariffs
63
+ # Old style
64
+ tariffs = Tariff.includes(:product_tariffs)
65
+ assert_equal(3, tariffs.length)
66
+ assert_equal(3, tariffs.inject(0) {|sum, tariff| sum + tariff.product_tariffs.length})
67
+
68
+ # New style
69
+ tariffs = Tariff.includes(:product_tariffs)
70
+ assert_equal(3, tariffs.length)
71
+ assert_equal(3, tariffs.inject(0) {|sum, tariff| sum + tariff.product_tariffs.length})
72
+ end
73
+
74
+ def test_has_one_association_is_not_cached_to_where_it_returns_the_wrong_one
75
+ engineering = departments(:engineering)
76
+ engineering_head = engineering.head
77
+
78
+ accounting = departments(:accounting)
79
+ accounting_head = accounting.head
80
+
81
+ refute_equal accounting_head, engineering_head
82
+ end
83
+
84
+ def test_has_many_association_is_not_cached_to_where_it_returns_the_wrong_ones
85
+ engineering = departments(:engineering)
86
+ engineering_employees = engineering.employees
87
+
88
+ accounting = departments(:accounting)
89
+ accounting_employees = accounting.employees
90
+
91
+ refute_equal accounting_employees, engineering_employees
92
+ end
93
+
94
+ def test_find_includes_product_tariffs_product
95
+ # Old style
96
+ product_tariffs = ProductTariff.includes(:product)
97
+ assert_not_nil(product_tariffs)
98
+ assert_equal(3, product_tariffs.length)
99
+
100
+ # New style
101
+ product_tariffs = ProductTariff.includes(:product)
102
+ assert_not_nil(product_tariffs)
103
+ assert_equal(3, product_tariffs.length)
104
+ end
105
+
106
+ def test_find_includes_product_tariffs_tariff
107
+ # Old style
108
+ product_tariffs = ProductTariff.includes(:tariff)
109
+ assert_equal(3, product_tariffs.length)
110
+
111
+ # New style
112
+ product_tariffs = ProductTariff.includes(:tariff)
113
+ assert_equal(3, product_tariffs.length)
114
+ end
115
+
116
+ def test_has_many_through
117
+ products = Product.includes(:tariffs)
118
+ assert_equal(3, products.length)
119
+
120
+ tarrifs_length = products.inject(0) {|sum, product| sum + product.tariffs.length}
121
+ assert_equal(3, tarrifs_length)
122
+ end
123
+
124
+ def test_new_style_includes_with_conditions
125
+ product_tariff = ProductTariff.includes(:tariff).where('tariffs.amount < 5').references(:tariffs).first
126
+ assert_equal(0, product_tariff.tariff.amount)
127
+ end
128
+
129
+ def test_find_product_includes
130
+ products = Product.includes(:product_tariffs => :tariff)
131
+ assert_equal(3, products.length)
132
+
133
+ product_tariffs_length = products.inject(0) {|sum, product| sum + product.product_tariffs.length}
134
+ assert_equal(3, product_tariffs_length)
135
+ end
136
+
137
+ def test_find_tariffs_includes
138
+ tariffs = Tariff.includes(:product_tariffs => :product)
139
+ assert_equal(3, tariffs.length)
140
+
141
+ product_tariffs_length = tariffs.inject(0) {|sum, tariff| sum + tariff.product_tariffs.length}
142
+ assert_equal(3, product_tariffs_length)
143
+ end
144
+
145
+ def test_has_many_through_when_not_pre_loaded
146
+ student = Student.first
147
+ rooms = student.rooms
148
+ assert_equal(1, rooms.size)
149
+ assert_equal(1, rooms.first.dorm_id)
150
+ assert_equal(1, rooms.first.room_id)
151
+ end
152
+
153
+ def test_has_many_through_when_through_association_is_composite
154
+ dorm = Dorm.first
155
+ assert_equal(3, dorm.rooms.length)
156
+ assert_equal(1, dorm.rooms.first.room_attributes.length)
157
+ assert_equal('type', dorm.rooms.first.room_attributes.first.name)
158
+ end
159
+
160
+ def test_associations_with_conditions
161
+ suburb = Suburb.find([2, 1])
162
+ assert_equal 2, suburb.streets.size
163
+
164
+ suburb = Suburb.find([2, 1])
165
+ assert_equal 1, suburb.first_streets.size
166
+
167
+ suburb = Suburb.includes(:streets).find([2, 1])
168
+ assert_equal 2, suburb.streets.size
169
+
170
+ suburb = Suburb.includes(:first_streets).find([2, 1])
171
+ assert_equal 1, suburb.first_streets.size
172
+ end
173
+
174
+ def test_composite_has_many_composites
175
+ room = rooms(:branner_room_1)
176
+ assert_equal(2, room.room_assignments.length)
177
+ assert_equal(room_assignments(:jacksons_room), room.room_assignments[0])
178
+ assert_equal(room_assignments(:bobs_room), room.room_assignments[1])
179
+ end
180
+
181
+ def test_composite_belongs_to_composite
182
+ room_assignment = room_assignments(:jacksons_room)
183
+ assert_equal(rooms(:branner_room_1), room_assignment.room)
184
+ end
185
+
186
+ def test_composite_belongs_to_changes
187
+ room_assignment = room_assignments(:jacksons_room)
188
+ room_assignment.room = rooms(:branner_room_2)
189
+ # This was raising an error before:
190
+ # TypeError: [:dorm_id, :room_id] is not a symbol
191
+ # changes returns HashWithIndifferentAccess
192
+ assert_equal({"room_id"=>[1, 2]}, room_assignment.changes)
193
+
194
+ steve = employees(:steve)
195
+ steve.department = departments(:engineering)
196
+ # It was returning this before:
197
+ # {"[:department_id, :location_id]"=>[nil, [2, 1]]}
198
+ assert_equal({"department_id"=>[1, 2]}, steve.changes)
199
+ end
200
+
201
+ def test_composite_belongs_to__setting_to_nil
202
+ room_assignment = room_assignments(:jacksons_room)
203
+ # This was raising an error before:
204
+ # NoMethodError: undefined method `length' for nil:NilClass
205
+ assert_nothing_raised { room_assignment.room = nil }
206
+ end
207
+
208
+ def test_has_one_with_composite
209
+ # In this case a regular model has_one composite model
210
+ department = departments(:engineering)
211
+ assert_not_nil(department.head)
212
+ end
213
+
214
+ def test_has_many_build__simple_key
215
+ user = users(:santiago)
216
+ reading = user.readings.build
217
+ assert_equal user.id, reading.user_id
218
+ assert_equal user, reading.user
219
+ end
220
+
221
+ def test_has_many_build__composite_key
222
+ department = departments(:engineering)
223
+ employee = department.employees.build
224
+ assert_equal department.department_id, employee.department_id
225
+ assert_equal department.location_id, employee.location_id
226
+ assert_equal department, employee.department
227
+ end
228
+
229
+ def test_has_many_with_primary_key
230
+ @membership = Membership.find([1, 1])
231
+ assert_equal 2, @membership.readings.size
232
+ end
233
+
234
+ def test_has_many_with_composite_key
235
+ # In this case a regular model (Dorm) has_many composite models (Rooms)
236
+ dorm = dorms(:branner)
237
+ assert_equal(3, dorm.rooms.length)
238
+ assert_equal(1, dorm.rooms[0].room_id)
239
+ assert_equal(2, dorm.rooms[1].room_id)
240
+ assert_equal(3, dorm.rooms[2].room_id)
241
+ end
242
+
243
+ def test_joins_has_many_with_primary_key
244
+ #@membership = Membership.find(:first, :joins => :readings, :conditions => { :readings => { :id => 1 } })
245
+ @membership = Membership.joins(:readings).where(readings: { id: 1 }).first
246
+
247
+ assert_equal [1, 1], @membership.id
248
+ end
249
+
250
+ def test_joins_has_one_with_primary_key
251
+ @membership = Membership.joins(:readings).where(readings: { id: 2 }).first
252
+
253
+ assert_equal [1, 1], @membership.id
254
+ end
255
+
256
+ def test_has_many_through_with_conditions_when_through_association_is_not_composite
257
+ user = User.first
258
+ assert_equal 1, user.articles.where("articles.name = ?", "Article One").size
259
+ end
260
+
261
+ def test_has_many_through_with_conditions_when_through_association_is_composite
262
+ room = Room.first
263
+ assert_equal 0, room.room_attributes.where("room_attributes.name != ?", "type").size
264
+ end
265
+
266
+ def test_has_many_through_on_custom_finder_when_through_association_is_composite_finder_when_through_association_is_not_composite
267
+ user = User.first
268
+ assert_equal(1, user.find_custom_articles.size)
269
+ end
270
+
271
+ def test_has_many_through_on_custom_finder_when_through_association_is_composite
272
+ room = Room.first
273
+ assert_equal(0, room.find_custom_room_attributes.size)
274
+ end
275
+
276
+ def test_has_many_with_primary_key_with_associations
277
+ memberships = Membership.includes(:statuses).where("membership_statuses.status = ?", 'Active').references(:membership_statuses)
278
+ assert_equal(2, memberships.length)
279
+ assert_equal([1,1], memberships[0].id)
280
+ assert_equal([3,2], memberships[1].id)
281
+ end
282
+
283
+ def test_limitable_reflections
284
+ memberships = Membership.includes(:statuses).where("membership_statuses.status = ?", 'Active').references(:membership_statuses).limit(1)
285
+ assert_equal(1, memberships.length)
286
+ assert_equal([1,1], memberships[0].id)
287
+ end
288
+
289
+ def test_foreign_key_present_with_null_association_ids
290
+ group = Group.new
291
+ group.memberships.build
292
+ associations = group.association_cache[:memberships]
293
+ assert_equal(false, associations.send('foreign_key_present?'))
294
+ end
295
+ end