activerecord 4.1.15 → 4.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +634 -2176
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +12 -8
  5. data/lib/active_record/associations/association.rb +1 -1
  6. data/lib/active_record/associations/association_scope.rb +53 -21
  7. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  8. data/lib/active_record/associations/builder/association.rb +16 -5
  9. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  10. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
  11. data/lib/active_record/associations/builder/has_one.rb +2 -2
  12. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  13. data/lib/active_record/associations/collection_association.rb +32 -44
  14. data/lib/active_record/associations/collection_proxy.rb +1 -10
  15. data/lib/active_record/associations/has_many_association.rb +60 -14
  16. data/lib/active_record/associations/has_many_through_association.rb +34 -23
  17. data/lib/active_record/associations/has_one_association.rb +0 -1
  18. data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
  19. data/lib/active_record/associations/join_dependency.rb +7 -9
  20. data/lib/active_record/associations/preloader/association.rb +9 -5
  21. data/lib/active_record/associations/preloader/through_association.rb +3 -3
  22. data/lib/active_record/associations/preloader.rb +2 -2
  23. data/lib/active_record/associations/singular_association.rb +16 -1
  24. data/lib/active_record/associations/through_association.rb +6 -22
  25. data/lib/active_record/associations.rb +58 -33
  26. data/lib/active_record/attribute.rb +131 -0
  27. data/lib/active_record/attribute_assignment.rb +19 -11
  28. data/lib/active_record/attribute_decorators.rb +66 -0
  29. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  30. data/lib/active_record/attribute_methods/dirty.rb +85 -42
  31. data/lib/active_record/attribute_methods/primary_key.rb +6 -8
  32. data/lib/active_record/attribute_methods/read.rb +14 -57
  33. data/lib/active_record/attribute_methods/serialization.rb +12 -146
  34. data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
  35. data/lib/active_record/attribute_methods/write.rb +8 -23
  36. data/lib/active_record/attribute_methods.rb +53 -90
  37. data/lib/active_record/attribute_set/builder.rb +32 -0
  38. data/lib/active_record/attribute_set.rb +77 -0
  39. data/lib/active_record/attributes.rb +122 -0
  40. data/lib/active_record/autosave_association.rb +11 -21
  41. data/lib/active_record/base.rb +9 -19
  42. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
  45. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
  46. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
  47. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
  48. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
  49. data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
  50. data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
  51. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
  52. data/lib/active_record/connection_adapters/column.rb +13 -244
  53. data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
  54. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
  55. data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
  56. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  57. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  58. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
  59. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
  60. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  61. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  62. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  63. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  64. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  67. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  85. data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
  86. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  87. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
  88. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
  89. data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
  90. data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
  91. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  92. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
  93. data/lib/active_record/connection_handling.rb +1 -1
  94. data/lib/active_record/core.rb +119 -22
  95. data/lib/active_record/counter_cache.rb +60 -6
  96. data/lib/active_record/enum.rb +9 -10
  97. data/lib/active_record/errors.rb +27 -26
  98. data/lib/active_record/explain.rb +1 -1
  99. data/lib/active_record/fixtures.rb +52 -45
  100. data/lib/active_record/gem_version.rb +3 -3
  101. data/lib/active_record/inheritance.rb +33 -8
  102. data/lib/active_record/integration.rb +4 -4
  103. data/lib/active_record/locking/optimistic.rb +34 -16
  104. data/lib/active_record/migration/command_recorder.rb +19 -2
  105. data/lib/active_record/migration/join_table.rb +1 -1
  106. data/lib/active_record/migration.rb +22 -32
  107. data/lib/active_record/model_schema.rb +39 -48
  108. data/lib/active_record/nested_attributes.rb +8 -18
  109. data/lib/active_record/persistence.rb +39 -22
  110. data/lib/active_record/query_cache.rb +3 -3
  111. data/lib/active_record/querying.rb +1 -8
  112. data/lib/active_record/railtie.rb +17 -10
  113. data/lib/active_record/railties/databases.rake +47 -42
  114. data/lib/active_record/readonly_attributes.rb +0 -1
  115. data/lib/active_record/reflection.rb +225 -92
  116. data/lib/active_record/relation/batches.rb +0 -2
  117. data/lib/active_record/relation/calculations.rb +28 -32
  118. data/lib/active_record/relation/delegation.rb +1 -1
  119. data/lib/active_record/relation/finder_methods.rb +42 -20
  120. data/lib/active_record/relation/merger.rb +0 -1
  121. data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
  122. data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
  123. data/lib/active_record/relation/predicate_builder.rb +1 -22
  124. data/lib/active_record/relation/query_methods.rb +98 -62
  125. data/lib/active_record/relation/spawn_methods.rb +6 -7
  126. data/lib/active_record/relation.rb +35 -11
  127. data/lib/active_record/result.rb +16 -9
  128. data/lib/active_record/sanitization.rb +8 -1
  129. data/lib/active_record/schema.rb +0 -1
  130. data/lib/active_record/schema_dumper.rb +51 -9
  131. data/lib/active_record/schema_migration.rb +4 -0
  132. data/lib/active_record/scoping/default.rb +5 -4
  133. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  134. data/lib/active_record/statement_cache.rb +79 -5
  135. data/lib/active_record/store.rb +5 -5
  136. data/lib/active_record/tasks/database_tasks.rb +37 -5
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  138. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
  139. data/lib/active_record/timestamp.rb +9 -7
  140. data/lib/active_record/transactions.rb +35 -21
  141. data/lib/active_record/type/binary.rb +40 -0
  142. data/lib/active_record/type/boolean.rb +19 -0
  143. data/lib/active_record/type/date.rb +46 -0
  144. data/lib/active_record/type/date_time.rb +43 -0
  145. data/lib/active_record/type/decimal.rb +40 -0
  146. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  147. data/lib/active_record/type/float.rb +19 -0
  148. data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
  149. data/lib/active_record/type/integer.rb +23 -0
  150. data/lib/active_record/type/mutable.rb +16 -0
  151. data/lib/active_record/type/numeric.rb +36 -0
  152. data/lib/active_record/type/serialized.rb +51 -0
  153. data/lib/active_record/type/string.rb +36 -0
  154. data/lib/active_record/type/text.rb +11 -0
  155. data/lib/active_record/type/time.rb +26 -0
  156. data/lib/active_record/type/time_value.rb +38 -0
  157. data/lib/active_record/type/type_map.rb +48 -0
  158. data/lib/active_record/type/value.rb +101 -0
  159. data/lib/active_record/type.rb +20 -0
  160. data/lib/active_record/validations/uniqueness.rb +9 -23
  161. data/lib/active_record/validations.rb +21 -16
  162. data/lib/active_record.rb +2 -1
  163. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  164. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  165. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  166. metadata +71 -14
  167. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -30,17 +30,18 @@ module ActiveRecord
30
30
  class SerializationTypeMismatch < ActiveRecordError
31
31
  end
32
32
 
33
- # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt>
34
- # misses adapter field).
33
+ # Raised when adapter not specified on connection (or configuration file
34
+ # +config/database.yml+ misses adapter field).
35
35
  class AdapterNotSpecified < ActiveRecordError
36
36
  end
37
37
 
38
- # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
38
+ # Raised when Active Record cannot find database adapter specified in
39
+ # +config/database.yml+ or programmatically.
39
40
  class AdapterNotFound < ActiveRecordError
40
41
  end
41
42
 
42
- # Raised when connection to the database could not been established (for example when <tt>connection=</tt>
43
- # is given a nil object).
43
+ # Raised when connection to the database could not been established (for
44
+ # example when +connection=+ is given a nil object).
44
45
  class ConnectionNotEstablished < ActiveRecordError
45
46
  end
46
47
 
@@ -82,35 +83,26 @@ module ActiveRecord
82
83
  class InvalidForeignKey < WrappedDatabaseException
83
84
  end
84
85
 
85
- # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example,
86
- # when using +find+ method)
87
- # does not match number of expected variables.
86
+ # Raised when number of bind variables in statement given to +:condition+ key
87
+ # (for example, when using +find+ method) does not match number of expected
88
+ # values supplied.
88
89
  #
89
- # For example, in
90
+ # For example, when there are two placeholders with only one value supplied:
90
91
  #
91
92
  # Location.where("lat = ? AND lng = ?", 53.7362)
92
- #
93
- # two placeholders are given but only one variable to fill them.
94
93
  class PreparedStatementInvalid < ActiveRecordError
95
94
  end
96
95
 
97
- # Raised when a given database does not exist
98
- class NoDatabaseError < ActiveRecordError
99
- def initialize(message)
100
- super extend_message(message)
101
- end
102
-
103
- # can be over written to add additional error information.
104
- def extend_message(message)
105
- message
106
- end
96
+ # Raised when a given database does not exist.
97
+ class NoDatabaseError < StatementInvalid
107
98
  end
108
99
 
109
100
  # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
110
101
  # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
111
102
  # the page before the other.
112
103
  #
113
- # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
104
+ # Read more about optimistic locking in ActiveRecord::Locking module
105
+ # documentation.
114
106
  class StaleObjectError < ActiveRecordError
115
107
  attr_reader :record, :attempted_action
116
108
 
@@ -122,8 +114,9 @@ module ActiveRecord
122
114
 
123
115
  end
124
116
 
125
- # Raised when association is being configured improperly or
126
- # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
117
+ # Raised when association is being configured improperly or user tries to use
118
+ # offset and limit together with +has_many+ or +has_and_belongs_to_many+
119
+ # associations.
127
120
  class ConfigurationError < ActiveRecordError
128
121
  end
129
122
 
@@ -161,7 +154,8 @@ module ActiveRecord
161
154
  class Rollback < ActiveRecordError
162
155
  end
163
156
 
164
- # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
157
+ # Raised when attribute has a name reserved by Active Record (when attribute
158
+ # has name of one of Active Record instance methods).
165
159
  class DangerousAttributeError < ActiveRecordError
166
160
  end
167
161
 
@@ -179,7 +173,7 @@ module ActiveRecord
179
173
  end
180
174
 
181
175
  # Raised when an error occurred while doing a mass assignment to an attribute through the
182
- # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
176
+ # +attributes=+ method. The exception has an +attribute+ property that is the name of the
183
177
  # offending attribute.
184
178
  class AttributeAssignmentError < ActiveRecordError
185
179
  attr_reader :exception, :attribute
@@ -225,6 +219,13 @@ module ActiveRecord
225
219
  class ImmutableRelation < ActiveRecordError
226
220
  end
227
221
 
222
+ # TransactionIsolationError will be raised under the following conditions:
223
+ #
224
+ # * The adapter does not support setting the isolation level
225
+ # * You are joining an existing open transaction
226
+ # * You are creating a nested (savepoint) transaction
227
+ #
228
+ # The mysql, mysql2 and postgresql adapters support setting the transaction isolation level.
228
229
  class TransactionIsolationError < ActiveRecordError
229
230
  end
230
231
  end
@@ -27,7 +27,7 @@ module ActiveRecord
27
27
  end.join("\n")
28
28
  end.join("\n")
29
29
 
30
- # Overriding inspect to be more human readable, specially in the console.
30
+ # Overriding inspect to be more human readable, especially in the console.
31
31
  def str.inspect
32
32
  self
33
33
  end
@@ -2,6 +2,7 @@ require 'erb'
2
2
  require 'yaml'
3
3
  require 'zlib'
4
4
  require 'active_support/dependencies'
5
+ require 'active_support/core_ext/digest/uuid'
5
6
  require 'active_record/fixture_set/file'
6
7
  require 'active_record/errors'
7
8
 
@@ -14,9 +15,10 @@ module ActiveRecord
14
15
  # They are stored in YAML files, one file per model, which are placed in the directory
15
16
  # appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
16
17
  # configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
17
- # The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
18
- # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a fixture file looks
19
- # like this:
18
+ # The fixture file ends with the +.yml+ file extension, for example:
19
+ # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>).
20
+ #
21
+ # The format of a fixture file looks like this:
20
22
  #
21
23
  # rubyonrails:
22
24
  # id: 1
@@ -32,7 +34,7 @@ module ActiveRecord
32
34
  # is followed by an indented list of key/value pairs in the "key: value" format. Records are
33
35
  # separated by a blank line for your viewing pleasure.
34
36
  #
35
- # Note that fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
37
+ # Note: Fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
36
38
  # See http://yaml.org/type/omap.html
37
39
  # for the specification. You will need ordered fixtures when you have foreign key constraints
38
40
  # on keys in the same table. This is commonly needed for tree structures. Example:
@@ -60,8 +62,8 @@ module ActiveRecord
60
62
  # end
61
63
  # end
62
64
  #
63
- # By default, <tt>test_helper.rb</tt> will load all of your fixtures into your test database,
64
- # so this test will succeed.
65
+ # By default, +test_helper.rb+ will load all of your fixtures into your test
66
+ # database, so this test will succeed.
65
67
  #
66
68
  # The testing environment will automatically load the all fixtures into the database before each
67
69
  # test. To ensure consistent data, the environment deletes the fixtures before running the load.
@@ -124,7 +126,7 @@ module ActiveRecord
124
126
  # that is included in <tt>ActiveRecord::FixtureSet.context_class</tt>.
125
127
  #
126
128
  # - define a helper method in `test_helper.rb`
127
- # module FixtureFileHelpers
129
+ # class FixtureFileHelpers
128
130
  # def file_sha(path)
129
131
  # Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
130
132
  # end
@@ -361,6 +363,7 @@ module ActiveRecord
361
363
  # geeksomnia:
362
364
  # name: Geeksomnia's Account
363
365
  # subdomain: $LABEL
366
+ # email: $LABEL@email.com
364
367
  #
365
368
  # Also, sometimes (like when porting older join table fixtures) you'll need
366
369
  # to be able to get a hold of the identifier for a given label. ERB
@@ -372,8 +375,9 @@ module ActiveRecord
372
375
  #
373
376
  # == Support for YAML defaults
374
377
  #
375
- # You probably already know how to use YAML to set and reuse defaults in
376
- # your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
378
+ # You can set and reuse defaults in your fixtures YAML file.
379
+ # This is the same technique used in the +database.yml+ file to specify
380
+ # defaults:
377
381
  #
378
382
  # DEFAULTS: &DEFAULTS
379
383
  # created_on: <%= 3.weeks.ago.to_s(:db) %>
@@ -389,7 +393,8 @@ module ActiveRecord
389
393
  # Any fixture labeled "DEFAULTS" is safely ignored.
390
394
  class FixtureSet
391
395
  #--
392
- # An instance of FixtureSet is normally stored in a single YAML file and possibly in a folder with the same name.
396
+ # An instance of FixtureSet is normally stored in a single YAML file and
397
+ # possibly in a folder with the same name.
393
398
  #++
394
399
 
395
400
  MAX_ID = 2 ** 30 - 1
@@ -459,13 +464,7 @@ module ActiveRecord
459
464
  @config = config
460
465
 
461
466
  # Remove string values that aren't constants or subclasses of AR
462
- @class_names.delete_if { |k,klass|
463
- unless klass.is_a? Class
464
- klass = klass.safe_constantize
465
- ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name to `set_fixture_class` will be removed in Rails 4.2. Use the class itself instead.")
466
- end
467
- !insert_class(@class_names, k, klass)
468
- }
467
+ @class_names.delete_if { |klass_name, klass| !insert_class(@class_names, klass_name, klass) }
469
468
  end
470
469
 
471
470
  def [](fs_name)
@@ -532,10 +531,12 @@ module ActiveRecord
532
531
  conn.insert_fixture(row, fixture_set_name)
533
532
  end
534
533
  end
534
+ end
535
535
 
536
- # Cap primary key sequences to max(pk).
537
- if conn.respond_to?(:reset_pk_sequence!)
538
- conn.reset_pk_sequence!(fs.table_name)
536
+ # Cap primary key sequences to max(pk).
537
+ if connection.respond_to?(:reset_pk_sequence!)
538
+ fixture_sets.each do |fs|
539
+ connection.reset_pk_sequence!(fs.table_name)
539
540
  end
540
541
  end
541
542
  end
@@ -547,9 +548,13 @@ module ActiveRecord
547
548
  end
548
549
 
549
550
  # Returns a consistent, platform-independent identifier for +label+.
550
- # Identifiers are positive integers less than 2^32.
551
- def self.identify(label)
552
- Zlib.crc32(label.to_s) % MAX_ID
551
+ # Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
552
+ def self.identify(label, column_type = :integer)
553
+ if column_type == :uuid
554
+ Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
555
+ else
556
+ Zlib.crc32(label.to_s) % MAX_ID
557
+ end
553
558
  end
554
559
 
555
560
  # Superclass for the evaluation contexts used by ERB fixtures.
@@ -565,10 +570,6 @@ module ActiveRecord
565
570
  @config = config
566
571
  @model_class = nil
567
572
 
568
- if class_name.is_a?(String)
569
- ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name to `FixtureSet.new` will be removed in Rails 4.2. Use the class itself instead.")
570
- end
571
-
572
573
  if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
573
574
  @model_class = class_name
574
575
  else
@@ -625,12 +626,12 @@ module ActiveRecord
625
626
 
626
627
  # interpolate the fixture label
627
628
  row.each do |key, value|
628
- row[key] = label if "$LABEL" == value
629
+ row[key] = value.gsub("$LABEL", label) if value.is_a?(String)
629
630
  end
630
631
 
631
632
  # generate a primary key if necessary
632
633
  if has_primary_key_column? && !row.include?(primary_key_name)
633
- row[primary_key_name] = ActiveRecord::FixtureSet.identify(label)
634
+ row[primary_key_name] = ActiveRecord::FixtureSet.identify(label, primary_key_type)
634
635
  end
635
636
 
636
637
  # If STI is used, find the correct subclass for association reflection
@@ -648,12 +649,13 @@ module ActiveRecord
648
649
  fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
649
650
 
650
651
  if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
651
- if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
652
+ if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
652
653
  # support polymorphic belongs_to as "label (Type)"
653
654
  row[association.foreign_type] = $1
654
655
  end
655
656
 
656
- row[fk_name] = ActiveRecord::FixtureSet.identify(value)
657
+ fk_type = association.active_record.columns_hash[association.foreign_key].type
658
+ row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
657
659
  end
658
660
  when :has_many
659
661
  if association.options[:through]
@@ -680,6 +682,10 @@ module ActiveRecord
680
682
  def name
681
683
  @association.name
682
684
  end
685
+
686
+ def primary_key_type
687
+ @association.klass.column_types[@association.klass.primary_key].type
688
+ end
683
689
  end
684
690
 
685
691
  class HasManyThroughProxy < ReflectionProxy # :nodoc:
@@ -690,10 +696,6 @@ module ActiveRecord
690
696
  def lhs_key
691
697
  @association.through_reflection.foreign_key
692
698
  end
693
-
694
- def join_table
695
- @association.through_reflection.table_name
696
- end
697
699
  end
698
700
 
699
701
  private
@@ -701,17 +703,22 @@ module ActiveRecord
701
703
  @primary_key_name ||= model_class && model_class.primary_key
702
704
  end
703
705
 
706
+ def primary_key_type
707
+ @primary_key_type ||= model_class && model_class.column_types[model_class.primary_key].type
708
+ end
709
+
704
710
  def add_join_records(rows, row, association)
705
711
  # This is the case when the join table has no fixtures file
706
712
  if (targets = row.delete(association.name.to_s))
707
- table_name = association.join_table
708
- lhs_key = association.lhs_key
709
- rhs_key = association.rhs_key
713
+ table_name = association.join_table
714
+ column_type = association.primary_key_type
715
+ lhs_key = association.lhs_key
716
+ rhs_key = association.rhs_key
710
717
 
711
718
  targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
712
719
  rows[table_name].concat targets.map { |target|
713
720
  { lhs_key => row[primary_key_name],
714
- rhs_key => ActiveRecord::FixtureSet.identify(target) }
721
+ rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
715
722
  }
716
723
  end
717
724
  end
@@ -792,7 +799,7 @@ module ActiveRecord
792
799
 
793
800
  def find
794
801
  if model_class
795
- model_class.unscoped.find(fixture[model_class.primary_key])
802
+ model_class.find(fixture[model_class.primary_key])
796
803
  else
797
804
  raise FixtureClassNotFound, "No class attached to find."
798
805
  end
@@ -863,11 +870,11 @@ module ActiveRecord
863
870
  def try_to_load_dependency(file_name)
864
871
  require_dependency file_name
865
872
  rescue LoadError => e
866
- unless fixture_class_names.key?(file_name.pluralize)
867
- if ActiveRecord::Base.logger
868
- ActiveRecord::Base.logger.warn("Unable to load #{file_name}, make sure you added it to ActiveSupport::TestCase.set_fixture_class")
869
- ActiveRecord::Base.logger.warn("underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
870
- end
873
+ # Let's hope the developer has included it
874
+ # Let's warn in case this is a subdependency, otherwise
875
+ # subdependency error messages are totally cryptic
876
+ if ActiveRecord::Base.logger
877
+ ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
871
878
  end
872
879
  end
873
880
 
@@ -6,9 +6,9 @@ module ActiveRecord
6
6
 
7
7
  module VERSION
8
8
  MAJOR = 4
9
- MINOR = 1
10
- TINY = 15
11
- PRE = nil
9
+ MINOR = 2
10
+ TINY = 0
11
+ PRE = "beta1"
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
14
  end
@@ -1,6 +1,37 @@
1
1
  require 'active_support/core_ext/hash/indifferent_access'
2
2
 
3
3
  module ActiveRecord
4
+ # == Single table inheritance
5
+ #
6
+ # Active Record allows inheritance by storing the name of the class in a column that by
7
+ # default is named "type" (can be changed by overwriting <tt>Base.inheritance_column</tt>).
8
+ # This means that an inheritance looking like this:
9
+ #
10
+ # class Company < ActiveRecord::Base; end
11
+ # class Firm < Company; end
12
+ # class Client < Company; end
13
+ # class PriorityClient < Client; end
14
+ #
15
+ # When you do <tt>Firm.create(name: "37signals")</tt>, this record will be saved in
16
+ # the companies table with type = "Firm". You can then fetch this row again using
17
+ # <tt>Company.where(name: '37signals').first</tt> and it will return a Firm object.
18
+ #
19
+ # Be aware that because the type column is an attribute on the record every new
20
+ # subclass will instantly be marked as dirty and the type column will be included
21
+ # in the list of changed attributes on the record. This is different from non
22
+ # STI classes:
23
+ #
24
+ # Company.new.changed? # => false
25
+ # Firm.new.changed? # => true
26
+ # Firm.new.changes # => {"type"=>["","Firm"]}
27
+ #
28
+ # If you don't have a type column defined in your table, single-table inheritance won't
29
+ # be triggered. In that case, it'll work just like normal subclasses with no special magic
30
+ # for differentiating between them or reloading the right type with find.
31
+ #
32
+ # Note, all the attributes for all the cases are kept in the same table. Read more:
33
+ # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
34
+ #
4
35
  module Inheritance
5
36
  extend ActiveSupport::Concern
6
37
 
@@ -120,14 +151,8 @@ module ActiveRecord
120
151
  candidates << type_name
121
152
 
122
153
  candidates.each do |candidate|
123
- begin
124
- constant = ActiveSupport::Dependencies.constantize(candidate)
125
- return constant if candidate == constant.to_s
126
- # We don't want to swallow NoMethodError < NameError errors
127
- rescue NoMethodError
128
- raise
129
- rescue NameError
130
- end
154
+ constant = ActiveSupport::Dependencies.safe_constantize(candidate)
155
+ return constant if candidate == constant.to_s
131
156
  end
132
157
 
133
158
  raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
@@ -55,16 +55,16 @@ module ActiveRecord
55
55
  def cache_key(*timestamp_names)
56
56
  case
57
57
  when new_record?
58
- "#{self.class.model_name.cache_key}/new"
58
+ "#{model_name.cache_key}/new"
59
59
  when timestamp_names.any?
60
60
  timestamp = max_updated_column_timestamp(timestamp_names)
61
61
  timestamp = timestamp.utc.to_s(cache_timestamp_format)
62
- "#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
62
+ "#{model_name.cache_key}/#{id}-#{timestamp}"
63
63
  when timestamp = max_updated_column_timestamp
64
64
  timestamp = timestamp.utc.to_s(cache_timestamp_format)
65
- "#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
65
+ "#{model_name.cache_key}/#{id}-#{timestamp}"
66
66
  else
67
- "#{self.class.model_name.cache_key}/#{id}"
67
+ "#{model_name.cache_key}/#{id}"
68
68
  end
69
69
  end
70
70
 
@@ -66,7 +66,7 @@ module ActiveRecord
66
66
  send(lock_col + '=', previous_lock_value + 1)
67
67
  end
68
68
 
69
- def _update_record(attribute_names = @attributes.keys) #:nodoc:
69
+ def _update_record(attribute_names = self.attribute_names) #:nodoc:
70
70
  return super unless locking_enabled?
71
71
  return 0 if attribute_names.empty?
72
72
 
@@ -141,7 +141,7 @@ module ActiveRecord
141
141
 
142
142
  # Set the column to use for optimistic locking. Defaults to +lock_version+.
143
143
  def locking_column=(value)
144
- @column_defaults = nil
144
+ clear_caches_calculated_from_columns
145
145
  @locking_column = value.to_s
146
146
  end
147
147
 
@@ -151,12 +151,6 @@ module ActiveRecord
151
151
  @locking_column
152
152
  end
153
153
 
154
- # Quote the column name used for optimistic locking.
155
- def quoted_locking_column
156
- ActiveSupport::Deprecation.warn "ActiveRecord::Base.quoted_locking_column is deprecated and will be removed in Rails 4.2 or later."
157
- connection.quote_column_name(locking_column)
158
- end
159
-
160
154
  # Reset the column used for optimistic locking back to the +lock_version+ default.
161
155
  def reset_locking_column
162
156
  self.locking_column = DEFAULT_LOCKING_COLUMN
@@ -169,18 +163,42 @@ module ActiveRecord
169
163
  super
170
164
  end
171
165
 
172
- def column_defaults
173
- @column_defaults ||= begin
174
- defaults = super
175
-
176
- if defaults.key?(locking_column) && lock_optimistically
177
- defaults[locking_column] ||= 0
166
+ private
167
+
168
+ # We need to apply this decorator here, rather than on module inclusion. The closure
169
+ # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
170
+ # sub class being decorated. As such, changes to `lock_optimistically`, or
171
+ # `locking_column` would not be picked up.
172
+ def inherited(subclass)
173
+ subclass.class_eval do
174
+ is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
175
+ decorate_matching_attribute_types(is_lock_column, :_optimistic_locking) do |type|
176
+ LockingType.new(type)
178
177
  end
179
-
180
- defaults
181
178
  end
179
+ super
182
180
  end
183
181
  end
184
182
  end
183
+
184
+ class LockingType < SimpleDelegator # :nodoc:
185
+ def type_cast_from_database(value)
186
+ # `nil` *should* be changed to 0
187
+ super.to_i
188
+ end
189
+
190
+ def changed?(old_value, *)
191
+ # Ensure we save if the default was `nil`
192
+ super || old_value == 0
193
+ end
194
+
195
+ def init_with(coder)
196
+ __setobj__(coder['subtype'])
197
+ end
198
+
199
+ def encode_with(coder)
200
+ coder['subtype'] = __getobj__
201
+ end
202
+ end
185
203
  end
186
204
  end
@@ -74,7 +74,9 @@ module ActiveRecord
74
74
  :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
75
75
  :change_column_default, :add_reference, :remove_reference, :transaction,
76
76
  :drop_join_table, :drop_table, :execute_block, :enable_extension,
77
- :change_column, :execute, :remove_columns, :change_column_null # irreversible methods need to be here too
77
+ :change_column, :execute, :remove_columns, :change_column_null,
78
+ :add_foreign_key, :remove_foreign_key
79
+ # irreversible methods need to be here too
78
80
  ].each do |method|
79
81
  class_eval <<-EOV, __FILE__, __LINE__ + 1
80
82
  def #{method}(*args, &block) # def create_table(*args, &block)
@@ -85,7 +87,7 @@ module ActiveRecord
85
87
  alias :add_belongs_to :add_reference
86
88
  alias :remove_belongs_to :remove_reference
87
89
 
88
- def change_table(table_name, options = {})
90
+ def change_table(table_name, options = {}) # :nodoc:
89
91
  yield delegate.update_table_definition(table_name, self)
90
92
  end
91
93
 
@@ -167,6 +169,21 @@ module ActiveRecord
167
169
  [:change_column_null, args]
168
170
  end
169
171
 
172
+ def invert_add_foreign_key(args)
173
+ from_table, to_table, add_options = args
174
+ add_options ||= {}
175
+
176
+ if add_options[:name]
177
+ options = { name: add_options[:name] }
178
+ elsif add_options[:column]
179
+ options = { column: add_options[:column] }
180
+ else
181
+ options = to_table
182
+ end
183
+
184
+ [:remove_foreign_key, [from_table, options]]
185
+ end
186
+
170
187
  # Forwards any missing method call to the \target.
171
188
  def method_missing(method, *args, &block)
172
189
  if @delegate.respond_to?(method)
@@ -8,7 +8,7 @@ module ActiveRecord
8
8
  end
9
9
 
10
10
  def join_table_name(table_1, table_2)
11
- [table_1.to_s, table_2.to_s].sort.join("_").to_sym
11
+ ModelSchema.derive_join_table_name(table_1, table_2).to_sym
12
12
  end
13
13
  end
14
14
  end