activerecord 4.1.16 → 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 -2185
  3. data/README.rdoc +15 -10
  4. data/lib/active_record.rb +2 -1
  5. data/lib/active_record/aggregations.rb +12 -8
  6. data/lib/active_record/associations.rb +58 -33
  7. data/lib/active_record/associations/association.rb +1 -1
  8. data/lib/active_record/associations/association_scope.rb +53 -21
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  10. data/lib/active_record/associations/builder/association.rb +16 -5
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
  13. data/lib/active_record/associations/builder/has_one.rb +2 -2
  14. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  15. data/lib/active_record/associations/collection_association.rb +32 -44
  16. data/lib/active_record/associations/collection_proxy.rb +1 -10
  17. data/lib/active_record/associations/has_many_association.rb +60 -14
  18. data/lib/active_record/associations/has_many_through_association.rb +34 -23
  19. data/lib/active_record/associations/has_one_association.rb +0 -1
  20. data/lib/active_record/associations/join_dependency.rb +7 -9
  21. data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
  22. data/lib/active_record/associations/preloader.rb +2 -2
  23. data/lib/active_record/associations/preloader/association.rb +9 -5
  24. data/lib/active_record/associations/preloader/through_association.rb +3 -3
  25. data/lib/active_record/associations/singular_association.rb +16 -1
  26. data/lib/active_record/associations/through_association.rb +6 -22
  27. data/lib/active_record/attribute.rb +131 -0
  28. data/lib/active_record/attribute_assignment.rb +19 -11
  29. data/lib/active_record/attribute_decorators.rb +66 -0
  30. data/lib/active_record/attribute_methods.rb +53 -90
  31. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  32. data/lib/active_record/attribute_methods/dirty.rb +85 -42
  33. data/lib/active_record/attribute_methods/primary_key.rb +6 -8
  34. data/lib/active_record/attribute_methods/read.rb +14 -57
  35. data/lib/active_record/attribute_methods/serialization.rb +12 -146
  36. data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
  37. data/lib/active_record/attribute_methods/write.rb +8 -23
  38. data/lib/active_record/attribute_set.rb +77 -0
  39. data/lib/active_record/attribute_set/builder.rb +32 -0
  40. data/lib/active_record/attributes.rb +122 -0
  41. data/lib/active_record/autosave_association.rb +11 -21
  42. data/lib/active_record/base.rb +9 -19
  43. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
  44. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
  53. data/lib/active_record/connection_adapters/column.rb +13 -244
  54. data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
  55. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
  56. data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
  57. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  58. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  59. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
  60. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  61. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
  62. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  64. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  67. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  86. data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
  87. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
  89. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
  90. data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
  91. data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
  92. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
  94. data/lib/active_record/connection_handling.rb +1 -1
  95. data/lib/active_record/core.rb +119 -22
  96. data/lib/active_record/counter_cache.rb +60 -6
  97. data/lib/active_record/enum.rb +9 -10
  98. data/lib/active_record/errors.rb +27 -26
  99. data/lib/active_record/explain.rb +1 -1
  100. data/lib/active_record/fixtures.rb +52 -45
  101. data/lib/active_record/gem_version.rb +3 -3
  102. data/lib/active_record/inheritance.rb +33 -8
  103. data/lib/active_record/integration.rb +4 -4
  104. data/lib/active_record/locking/optimistic.rb +34 -16
  105. data/lib/active_record/migration.rb +22 -32
  106. data/lib/active_record/migration/command_recorder.rb +19 -2
  107. data/lib/active_record/migration/join_table.rb +1 -1
  108. data/lib/active_record/model_schema.rb +39 -48
  109. data/lib/active_record/nested_attributes.rb +8 -18
  110. data/lib/active_record/persistence.rb +39 -22
  111. data/lib/active_record/query_cache.rb +3 -3
  112. data/lib/active_record/querying.rb +1 -8
  113. data/lib/active_record/railtie.rb +17 -10
  114. data/lib/active_record/railties/databases.rake +47 -42
  115. data/lib/active_record/readonly_attributes.rb +0 -1
  116. data/lib/active_record/reflection.rb +225 -92
  117. data/lib/active_record/relation.rb +35 -11
  118. data/lib/active_record/relation/batches.rb +0 -2
  119. data/lib/active_record/relation/calculations.rb +28 -32
  120. data/lib/active_record/relation/delegation.rb +1 -1
  121. data/lib/active_record/relation/finder_methods.rb +42 -20
  122. data/lib/active_record/relation/merger.rb +0 -1
  123. data/lib/active_record/relation/predicate_builder.rb +1 -22
  124. data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
  125. data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
  126. data/lib/active_record/relation/query_methods.rb +98 -62
  127. data/lib/active_record/relation/spawn_methods.rb +6 -7
  128. data/lib/active_record/result.rb +16 -9
  129. data/lib/active_record/sanitization.rb +8 -1
  130. data/lib/active_record/schema.rb +0 -1
  131. data/lib/active_record/schema_dumper.rb +51 -9
  132. data/lib/active_record/schema_migration.rb +4 -0
  133. data/lib/active_record/scoping/default.rb +5 -4
  134. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  135. data/lib/active_record/statement_cache.rb +79 -5
  136. data/lib/active_record/store.rb +5 -5
  137. data/lib/active_record/tasks/database_tasks.rb +37 -5
  138. data/lib/active_record/tasks/mysql_database_tasks.rb +10 -16
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
  140. data/lib/active_record/timestamp.rb +9 -7
  141. data/lib/active_record/transactions.rb +35 -21
  142. data/lib/active_record/type.rb +20 -0
  143. data/lib/active_record/type/binary.rb +40 -0
  144. data/lib/active_record/type/boolean.rb +19 -0
  145. data/lib/active_record/type/date.rb +46 -0
  146. data/lib/active_record/type/date_time.rb +43 -0
  147. data/lib/active_record/type/decimal.rb +40 -0
  148. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  149. data/lib/active_record/type/float.rb +19 -0
  150. data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
  151. data/lib/active_record/type/integer.rb +23 -0
  152. data/lib/active_record/type/mutable.rb +16 -0
  153. data/lib/active_record/type/numeric.rb +36 -0
  154. data/lib/active_record/type/serialized.rb +51 -0
  155. data/lib/active_record/type/string.rb +36 -0
  156. data/lib/active_record/type/text.rb +11 -0
  157. data/lib/active_record/type/time.rb +26 -0
  158. data/lib/active_record/type/time_value.rb +38 -0
  159. data/lib/active_record/type/type_map.rb +48 -0
  160. data/lib/active_record/type/value.rb +101 -0
  161. data/lib/active_record/validations.rb +21 -16
  162. data/lib/active_record/validations/uniqueness.rb +9 -23
  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
@@ -6,9 +6,9 @@ module ActiveRecord
6
6
 
7
7
  module VERSION
8
8
  MAJOR = 4
9
- MINOR = 1
10
- TINY = 16
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
@@ -195,7 +195,7 @@ module ActiveRecord
195
195
  # == Database support
196
196
  #
197
197
  # Migrations are currently supported in MySQL, PostgreSQL, SQLite,
198
- # SQL Server, Sybase, and Oracle (all supported databases except DB2).
198
+ # SQL Server, and Oracle (all supported databases except DB2).
199
199
  #
200
200
  # == More examples
201
201
  #
@@ -372,13 +372,21 @@ module ActiveRecord
372
372
  end
373
373
 
374
374
  def call(env)
375
- mtime = ActiveRecord::Migrator.last_migration.mtime.to_i
376
- if @last_check < mtime
377
- ActiveRecord::Migration.check_pending!
378
- @last_check = mtime
375
+ if connection.supports_migrations?
376
+ mtime = ActiveRecord::Migrator.last_migration.mtime.to_i
377
+ if @last_check < mtime
378
+ ActiveRecord::Migration.check_pending!(connection)
379
+ @last_check = mtime
380
+ end
379
381
  end
380
382
  @app.call(env)
381
383
  end
384
+
385
+ private
386
+
387
+ def connection
388
+ ActiveRecord::Base.connection
389
+ end
382
390
  end
383
391
 
384
392
  class << self
@@ -391,14 +399,7 @@ module ActiveRecord
391
399
 
392
400
  def load_schema_if_pending!
393
401
  if ActiveRecord::Migrator.needs_migration?
394
- # Roundrip to Rake to allow plugins to hook into database initialization.
395
- FileUtils.cd Rails.root do
396
- current_config = Base.connection_config
397
- Base.clear_all_connections!
398
- system("bin/rake db:test:prepare")
399
- # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
400
- Base.establish_connection(current_config)
401
- end
402
+ ActiveRecord::Tasks::DatabaseTasks.load_schema_current
402
403
  check_pending!
403
404
  end
404
405
  end
@@ -649,7 +650,9 @@ module ActiveRecord
649
650
  unless @connection.respond_to? :revert
650
651
  unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
651
652
  arguments[0] = proper_table_name(arguments.first, table_name_options)
652
- arguments[1] = proper_table_name(arguments.second, table_name_options) if method == :rename_table
653
+ if [:rename_table, :add_foreign_key].include?(method)
654
+ arguments[1] = proper_table_name(arguments.second, table_name_options)
655
+ end
653
656
  end
654
657
  end
655
658
  return super unless connection.respond_to?(method)
@@ -811,22 +814,22 @@ module ActiveRecord
811
814
  migrations = migrations(migrations_paths)
812
815
  migrations.select! { |m| yield m } if block_given?
813
816
 
814
- self.new(:up, migrations, target_version).migrate
817
+ new(:up, migrations, target_version).migrate
815
818
  end
816
819
 
817
820
  def down(migrations_paths, target_version = nil, &block)
818
821
  migrations = migrations(migrations_paths)
819
822
  migrations.select! { |m| yield m } if block_given?
820
823
 
821
- self.new(:down, migrations, target_version).migrate
824
+ new(:down, migrations, target_version).migrate
822
825
  end
823
826
 
824
827
  def run(direction, migrations_paths, target_version)
825
- self.new(direction, migrations(migrations_paths), target_version).run
828
+ new(direction, migrations(migrations_paths), target_version).run
826
829
  end
827
830
 
828
831
  def open(migrations_paths)
829
- self.new(:up, migrations(migrations_paths), nil)
832
+ new(:up, migrations(migrations_paths), nil)
830
833
  end
831
834
 
832
835
  def schema_migrations_table_name
@@ -857,19 +860,6 @@ module ActiveRecord
857
860
  migrations(migrations_paths).last || NullMigration.new
858
861
  end
859
862
 
860
- def proper_table_name(name, options = {})
861
- ActiveSupport::Deprecation.warn "ActiveRecord::Migrator.proper_table_name is deprecated and will be removed in Rails 4.2. Use the proper_table_name instance method on ActiveRecord::Migration instead"
862
- options = {
863
- table_name_prefix: ActiveRecord::Base.table_name_prefix,
864
- table_name_suffix: ActiveRecord::Base.table_name_suffix
865
- }.merge(options)
866
- if name.respond_to? :table_name
867
- name.table_name
868
- else
869
- "#{options[:table_name_prefix]}#{name}#{options[:table_name_suffix]}"
870
- end
871
- end
872
-
873
863
  def migrations_paths
874
864
  @migrations_paths ||= ['db/migrate']
875
865
  # just to not break things if someone uses: migration_path = some_string
@@ -901,7 +891,7 @@ module ActiveRecord
901
891
  private
902
892
 
903
893
  def move(direction, migrations_paths, steps)
904
- migrator = self.new(direction, migrations(migrations_paths))
894
+ migrator = new(direction, migrations(migrations_paths))
905
895
  start_index = migrator.migrations.index(migrator.current_migration)
906
896
 
907
897
  if start_index
@@ -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
@@ -29,6 +29,10 @@ module ActiveRecord
29
29
  # :singleton-method:
30
30
  # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
31
31
  # "people_basecamp"). By default, the suffix is the empty string.
32
+ #
33
+ # If you are organising your models within modules, you can add a suffix to the models within
34
+ # a namespace by defining a singleton method in the parent module called table_name_suffix which
35
+ # returns your chosen suffix.
32
36
  class_attribute :table_name_suffix, instance_writer: false
33
37
  self.table_name_suffix = ""
34
38
 
@@ -47,6 +51,19 @@ module ActiveRecord
47
51
  self.pluralize_table_names = true
48
52
 
49
53
  self.inheritance_column = 'type'
54
+
55
+ delegate :type_for_attribute, to: :class
56
+ end
57
+
58
+ # Derives the join table name for +first_table+ and +second_table+. The
59
+ # table names appear in alphabetical order. A common prefix is removed
60
+ # (useful for namespaced models like Music::Artist and Music::Record):
61
+ #
62
+ # artists, records => artists_records
63
+ # records, artists => artists_records
64
+ # music_artists, music_records => music_artists_records
65
+ def self.derive_join_table_name(first_table, second_table) # :nodoc:
66
+ [first_table.to_s, second_table.to_s].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').gsub("\0", "_")
50
67
  end
51
68
 
52
69
  module ClassMethods
@@ -153,6 +170,10 @@ module ActiveRecord
153
170
  (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
154
171
  end
155
172
 
173
+ def full_table_name_suffix #:nodoc:
174
+ (parents.detect {|p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix
175
+ end
176
+
156
177
  # Defines the name of the table column which will store the class name on single-table
157
178
  # inheritance situations.
158
179
  #
@@ -190,7 +211,7 @@ module ActiveRecord
190
211
  # given block. This is required for Oracle and is useful for any
191
212
  # database which relies on sequences for primary key generation.
192
213
  #
193
- # If a sequence name is not explicitly set when using Oracle or Firebird,
214
+ # If a sequence name is not explicitly set when using Oracle,
194
215
  # it will default to the commonly used pattern of: #{table_name}_seq
195
216
  #
196
217
  # If a sequence name is not explicitly set when using PostgreSQL, it
@@ -209,50 +230,29 @@ module ActiveRecord
209
230
  connection.schema_cache.table_exists?(table_name)
210
231
  end
211
232
 
212
- # Returns an array of column objects for the table associated with this class.
213
- def columns
214
- @columns ||= connection.schema_cache.columns(table_name).map do |col|
215
- col = col.dup
216
- col.primary = (col.name == primary_key)
217
- col
218
- end
219
- end
220
-
221
- # Returns a hash of column objects for the table associated with this class.
222
- def columns_hash
223
- @columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
233
+ def attributes_builder # :nodoc:
234
+ @attributes_builder ||= AttributeSet::Builder.new(column_types)
224
235
  end
225
236
 
226
237
  def column_types # :nodoc:
227
- @column_types ||= decorate_columns(columns_hash.dup)
228
- end
229
-
230
- def decorate_columns(columns_hash) # :nodoc:
231
- return if columns_hash.empty?
232
-
233
- @serialized_column_names ||= self.columns_hash.keys.find_all do |name|
234
- serialized_attributes.key?(name)
235
- end
236
-
237
- @serialized_column_names.each do |name|
238
- columns_hash[name] = AttributeMethods::Serialization::Type.new(columns_hash[name])
239
- end
240
-
241
- @time_zone_column_names ||= self.columns_hash.find_all do |name, col|
242
- create_time_zone_conversion_attribute?(name, col)
243
- end.map!(&:first)
244
-
245
- @time_zone_column_names.each do |name|
246
- columns_hash[name] = AttributeMethods::TimeZoneConversion::Type.new(columns_hash[name])
238
+ @column_types ||= columns_hash.transform_values(&:cast_type).tap do |h|
239
+ h.default = Type::Value.new
247
240
  end
241
+ end
248
242
 
249
- columns_hash
243
+ def type_for_attribute(attr_name) # :nodoc:
244
+ column_types[attr_name]
250
245
  end
251
246
 
252
247
  # Returns a hash where the keys are column names and the values are
253
248
  # default values when instantiating the AR object for this table.
254
249
  def column_defaults
255
- @column_defaults ||= Hash[columns.map { |c| [c.name, c.default] }]
250
+ default_attributes.to_hash
251
+ end
252
+
253
+ def default_attributes # :nodoc:
254
+ @default_attributes ||= attributes_builder.build_from_database(
255
+ columns_hash.transform_values(&:default))
256
256
  end
257
257
 
258
258
  # Returns an array of column names as strings.
@@ -263,7 +263,7 @@ module ActiveRecord
263
263
  # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
264
264
  # and columns used for single table inheritance have been removed.
265
265
  def content_columns
266
- @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
266
+ @content_columns ||= columns.reject { |c| c.name == primary_key || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
267
267
  end
268
268
 
269
269
  # Resets all the cached information about columns, which will cause them
@@ -298,27 +298,17 @@ module ActiveRecord
298
298
  connection.schema_cache.clear_table_cache!(table_name) if table_exists?
299
299
 
300
300
  @arel_engine = nil
301
- @column_defaults = nil
302
301
  @column_names = nil
303
- @columns = nil
304
- @columns_hash = nil
305
302
  @column_types = nil
306
303
  @content_columns = nil
304
+ @default_attributes = nil
307
305
  @dynamic_methods_hash = nil
308
306
  @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
309
307
  @relation = nil
310
- @serialized_column_names = nil
311
308
  @time_zone_column_names = nil
312
309
  @cached_time_zone = nil
313
310
  end
314
311
 
315
- # This is a hook for use by modules that need to do extra stuff to
316
- # attributes when they are initialized. (e.g. attribute
317
- # serialization)
318
- def initialize_attributes(attributes, options = {}) #:nodoc:
319
- attributes
320
- end
321
-
322
312
  private
323
313
 
324
314
  # Guesses the table name, but does not decorate it with prefix and suffix information.
@@ -337,7 +327,8 @@ module ActiveRecord
337
327
  contained = contained.singularize if parent.pluralize_table_names
338
328
  contained += '_'
339
329
  end
340
- "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}"
330
+
331
+ "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{full_table_name_suffix}"
341
332
  else
342
333
  # STI subclasses always use their superclass' table.
343
334
  base.table_name