activerecord 4.2.4 → 4.2.10

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.

Potentially problematic release.


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

Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +249 -0
  3. data/lib/active_record/aggregations.rb +6 -3
  4. data/lib/active_record/associations/association_scope.rb +1 -1
  5. data/lib/active_record/associations/collection_association.rb +17 -6
  6. data/lib/active_record/associations/collection_proxy.rb +2 -2
  7. data/lib/active_record/associations/has_many_through_association.rb +5 -0
  8. data/lib/active_record/associations/join_dependency/join_association.rb +7 -1
  9. data/lib/active_record/associations/join_dependency.rb +2 -1
  10. data/lib/active_record/associations/preloader/association.rb +5 -1
  11. data/lib/active_record/attribute_assignment.rb +1 -1
  12. data/lib/active_record/attribute_methods/dirty.rb +1 -0
  13. data/lib/active_record/attribute_methods/write.rb +1 -1
  14. data/lib/active_record/attribute_methods.rb +4 -8
  15. data/lib/active_record/attribute_set/builder.rb +21 -11
  16. data/lib/active_record/attribute_set.rb +4 -0
  17. data/lib/active_record/attributes.rb +1 -0
  18. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -1
  19. data/lib/active_record/connection_adapters/abstract/database_statements.rb +7 -2
  20. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -0
  21. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -2
  22. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +21 -7
  23. data/lib/active_record/connection_adapters/abstract_adapter.rb +12 -1
  24. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +44 -9
  25. data/lib/active_record/connection_adapters/column.rb +1 -1
  26. data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -19
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +9 -2
  28. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -0
  29. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  30. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +13 -2
  31. data/lib/active_record/connection_adapters/postgresql_adapter.rb +2 -6
  32. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -12
  33. data/lib/active_record/core.rb +2 -0
  34. data/lib/active_record/enum.rb +2 -3
  35. data/lib/active_record/errors.rb +4 -3
  36. data/lib/active_record/gem_version.rb +1 -1
  37. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  38. data/lib/active_record/migration.rb +34 -6
  39. data/lib/active_record/model_schema.rb +3 -1
  40. data/lib/active_record/nested_attributes.rb +12 -2
  41. data/lib/active_record/railtie.rb +4 -2
  42. data/lib/active_record/railties/databases.rake +7 -17
  43. data/lib/active_record/reflection.rb +37 -25
  44. data/lib/active_record/relation/calculations.rb +10 -3
  45. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -1
  46. data/lib/active_record/relation/query_methods.rb +1 -1
  47. data/lib/active_record/relation/spawn_methods.rb +7 -3
  48. data/lib/active_record/schema_migration.rb +1 -4
  49. data/lib/active_record/tasks/database_tasks.rb +4 -1
  50. data/lib/active_record/tasks/mysql_database_tasks.rb +30 -16
  51. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -8
  52. data/lib/active_record/type/date.rb +4 -0
  53. data/lib/active_record/type/decimal.rb +19 -3
  54. data/lib/active_record/type/value.rb +5 -0
  55. data/lib/active_record/validations/uniqueness.rb +7 -1
  56. data/lib/active_record.rb +2 -0
  57. metadata +8 -8
@@ -245,7 +245,7 @@ module ActiveRecord
245
245
  return @client_encoding if @client_encoding
246
246
 
247
247
  result = exec_query(
248
- "SHOW VARIABLES WHERE Variable_name = 'character_set_client'",
248
+ "select @@character_set_client",
249
249
  'SCHEMA')
250
250
  @client_encoding = ENCODINGS[result.rows.last.last]
251
251
  end
@@ -282,6 +282,10 @@ module ActiveRecord
282
282
  super
283
283
  end
284
284
  end
285
+
286
+ def has_precision?
287
+ precision || 0
288
+ end
285
289
  end
286
290
 
287
291
  class Time < Type::Time # :nodoc:
@@ -328,8 +332,11 @@ module ActiveRecord
328
332
 
329
333
  def initialize_type_map(m) # :nodoc:
330
334
  super
331
- m.register_type %r(datetime)i, Fields::DateTime.new
332
335
  m.register_type %r(time)i, Fields::Time.new
336
+ m.register_type(%r(datetime)i) do |sql_type|
337
+ precision = extract_precision(sql_type)
338
+ Fields::DateTime.new(precision: precision)
339
+ end
333
340
  end
334
341
 
335
342
  def exec_without_stmt(sql, name = 'SQL') # :nodoc:
@@ -80,6 +80,7 @@ module ActiveRecord
80
80
  value
81
81
  end
82
82
  when nil then "NULL"
83
+ when ::Date, ::DateTime, ::Time then subtype.type_cast_for_schema(value)
83
84
  else value
84
85
  end
85
86
  end
@@ -36,7 +36,7 @@ module ActiveRecord
36
36
  WHERE
37
37
  t.typname IN (%s)
38
38
  OR t.typtype IN (%s)
39
- OR t.typinput::varchar = 'array_in'
39
+ OR t.typinput = 'array_in(cstring,oid,integer)'::regprocedure
40
40
  OR t.typelem != 0
41
41
  SQL
42
42
  end
@@ -86,7 +86,7 @@ module ActiveRecord
86
86
  execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
87
87
  end
88
88
 
89
- # Returns the list of all tables in the schema search path or a specified schema.
89
+ # Returns the list of all tables in the schema search path.
90
90
  def tables(name = nil)
91
91
  query(<<-SQL, 'SCHEMA').map { |row| row[0] }
92
92
  SELECT tablename
@@ -95,6 +95,16 @@ module ActiveRecord
95
95
  SQL
96
96
  end
97
97
 
98
+ def data_sources # :nodoc
99
+ select_values(<<-SQL, 'SCHEMA')
100
+ SELECT c.relname
101
+ FROM pg_class c
102
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
103
+ WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
104
+ AND n.nspname = ANY (current_schemas(false))
105
+ SQL
106
+ end
107
+
98
108
  # Returns true if table exists.
99
109
  # If the schema is not specified as part of +name+ then it will only find tables within
100
110
  # the current schema search path (regardless of permissions to access tables in other schemas)
@@ -111,6 +121,7 @@ module ActiveRecord
111
121
  AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
112
122
  SQL
113
123
  end
124
+ alias data_source_exists? table_exists?
114
125
 
115
126
  def drop_table(table_name, options = {})
116
127
  execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
@@ -552,7 +563,7 @@ module ActiveRecord
552
563
  when 1, 2; 'smallint'
553
564
  when 3, 4; 'integer'
554
565
  when 5..8; 'bigint'
555
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
566
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
556
567
  end
557
568
  when 'datetime'
558
569
  return super unless precision
@@ -308,12 +308,8 @@ module ActiveRecord
308
308
  true
309
309
  end
310
310
 
311
- # Enable standard-conforming strings if available.
312
311
  def set_standard_conforming_strings
313
- old, self.client_min_messages = client_min_messages, 'panic'
314
- execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
315
- ensure
316
- self.client_min_messages = old
312
+ execute('SET standard_conforming_strings = on', 'SCHEMA')
317
313
  end
318
314
 
319
315
  def supports_ddl_transactions?
@@ -677,7 +673,7 @@ module ActiveRecord
677
673
  self.client_min_messages = @config[:min_messages] || 'warning'
678
674
  self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
679
675
 
680
- # Use standard-conforming strings if available so we don't have to do the E'...' dance.
676
+ # Use standard-conforming strings so we don't have to do the E'...' dance.
681
677
  set_standard_conforming_strings
682
678
 
683
679
  # If using Active Record's time zone support configure the connection to return
@@ -74,18 +74,6 @@ module ActiveRecord
74
74
  boolean: { name: "boolean" }
75
75
  }
76
76
 
77
- class Version
78
- include Comparable
79
-
80
- def initialize(version_string)
81
- @version = version_string.split('.').map { |v| v.to_i }
82
- end
83
-
84
- def <=>(version_string)
85
- @version <=> version_string.split('.').map { |v| v.to_i }
86
- end
87
- end
88
-
89
77
  class StatementPool < ConnectionAdapters::StatementPool
90
78
  def initialize(connection, max)
91
79
  super
@@ -375,10 +363,12 @@ module ActiveRecord
375
363
  row['name']
376
364
  end
377
365
  end
366
+ alias data_sources tables
378
367
 
379
368
  def table_exists?(table_name)
380
369
  table_name && tables(nil, table_name).any?
381
370
  end
371
+ alias data_source_exists? table_exists?
382
372
 
383
373
  # Returns an array of +Column+ objects for the table specified by +table_name+.
384
374
  def columns(table_name) #:nodoc:
@@ -299,6 +299,7 @@ module ActiveRecord
299
299
  # post.init_with(coder)
300
300
  # post.title # => 'hello world'
301
301
  def init_with(coder)
302
+ coder = LegacyYamlAdapter.convert(self.class, coder)
302
303
  @attributes = coder['attributes']
303
304
 
304
305
  init_internals
@@ -372,6 +373,7 @@ module ActiveRecord
372
373
  coder['raw_attributes'] = attributes_before_type_cast
373
374
  coder['attributes'] = @attributes
374
375
  coder['new_record'] = new_record?
376
+ coder['active_record_yaml_version'] = 0
375
377
  end
376
378
 
377
379
  # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
@@ -18,10 +18,9 @@ module ActiveRecord
18
18
  # conversation.archived? # => true
19
19
  # conversation.status # => "archived"
20
20
  #
21
- # # conversation.update! status: 1
21
+ # # conversation.status = 1
22
22
  # conversation.status = "archived"
23
23
  #
24
- # # conversation.update! status: nil
25
24
  # conversation.status = nil
26
25
  # conversation.status.nil? # => true
27
26
  # conversation.status # => nil
@@ -70,7 +69,7 @@ module ActiveRecord
70
69
  # Where conditions on an enum attribute must use the ordinal value of an enum.
71
70
  module Enum
72
71
  def self.extended(base) # :nodoc:
73
- base.class_attribute(:defined_enums)
72
+ base.class_attribute(:defined_enums, instance_writer: false)
74
73
  base.defined_enums = {}
75
74
  end
76
75
 
@@ -219,11 +219,12 @@ module ActiveRecord
219
219
  class UnknownPrimaryKey < ActiveRecordError
220
220
  attr_reader :model
221
221
 
222
- def initialize(model)
223
- super("Unknown primary key for table #{model.table_name} in model #{model}.")
222
+ def initialize(model, description = nil)
223
+ message = "Unknown primary key for table #{model.table_name} in model #{model}."
224
+ message += "\n#{description}" if description
225
+ super(message)
224
226
  @model = model
225
227
  end
226
-
227
228
  end
228
229
 
229
230
  # Raised when a relation cannot be mutated because it's already loaded.
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  module VERSION
8
8
  MAJOR = 4
9
9
  MINOR = 2
10
- TINY = 4
10
+ TINY = 10
11
11
  PRE = nil
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -0,0 +1,30 @@
1
+ module ActiveRecord
2
+ module LegacyYamlAdapter
3
+ def self.convert(klass, coder)
4
+ return coder unless coder.is_a?(Psych::Coder)
5
+
6
+ case coder["active_record_yaml_version"]
7
+ when 0 then coder
8
+ else
9
+ if coder["attributes"].is_a?(AttributeSet)
10
+ coder
11
+ else
12
+ Rails41.convert(klass, coder)
13
+ end
14
+ end
15
+ end
16
+
17
+ module Rails41
18
+ def self.convert(klass, coder)
19
+ attributes = klass.attributes_builder
20
+ .build_from_database(coder["attributes"])
21
+ new_record = coder["attributes"][klass.primary_key].blank?
22
+
23
+ {
24
+ "attributes" => attributes,
25
+ "new_record" => new_record,
26
+ }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -420,7 +420,10 @@ module ActiveRecord
420
420
  new.migrate direction
421
421
  end
422
422
 
423
- # Disable DDL transactions for this migration.
423
+ # Disable the transaction wrapping this migration.
424
+ # You can still create your own transactions even after calling #disable_ddl_transaction!
425
+ #
426
+ # For more details read the {"Transactional Migrations" section above}[rdoc-ref:Migration].
424
427
  def disable_ddl_transaction!
425
428
  @disable_ddl_transaction = true
426
429
  end
@@ -877,14 +880,15 @@ module ActiveRecord
877
880
  migrations_paths.first
878
881
  end
879
882
 
883
+ def parse_migration_filename(filename) # :nodoc:
884
+ File.basename(filename).scan(/\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
885
+ end
886
+
880
887
  def migrations(paths)
881
888
  paths = Array(paths)
882
889
 
883
- files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
884
-
885
- migrations = files.map do |file|
886
- version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
887
-
890
+ migrations = migration_files(paths).map do |file|
891
+ version, name, scope = parse_migration_filename(file)
888
892
  raise IllegalMigrationNameError.new(file) unless version
889
893
  version = version.to_i
890
894
  name = name.camelize
@@ -895,6 +899,30 @@ module ActiveRecord
895
899
  migrations.sort_by(&:version)
896
900
  end
897
901
 
902
+ def migrations_status(paths)
903
+ paths = Array(paths)
904
+
905
+ db_list = ActiveRecord::SchemaMigration.normalized_versions
906
+
907
+ file_list = migration_files(paths).map do |file|
908
+ version, name, scope = parse_migration_filename(file)
909
+ raise IllegalMigrationNameError.new(file) unless version
910
+ version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
911
+ status = db_list.delete(version) ? "up" : "down"
912
+ [status, version, (name + scope).humanize]
913
+ end.compact
914
+
915
+ db_list.map! do |version|
916
+ ["up", version, "********** NO FILE **********"]
917
+ end
918
+
919
+ (db_list + file_list).sort_by { |_, version, _| version }
920
+ end
921
+
922
+ def migration_files(paths)
923
+ Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
924
+ end
925
+
898
926
  private
899
927
 
900
928
  def move(direction, migrations_paths, steps)
@@ -247,7 +247,7 @@ module ActiveRecord
247
247
  # Returns a hash where the keys are column names and the values are
248
248
  # default values when instantiating the AR object for this table.
249
249
  def column_defaults
250
- _default_attributes.to_hash
250
+ _default_attributes.dup.to_hash
251
251
  end
252
252
 
253
253
  def _default_attributes # :nodoc:
@@ -304,6 +304,8 @@ module ActiveRecord
304
304
  @default_attributes = nil
305
305
  @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
306
306
  @relation = nil
307
+
308
+ initialize_find_by_cache
307
309
  end
308
310
 
309
311
  private
@@ -523,7 +523,7 @@ module ActiveRecord
523
523
  # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
524
524
  # association and evaluates to +true+.
525
525
  def reject_new_record?(association_name, attributes)
526
- has_destroy_flag?(attributes) || call_reject_if(association_name, attributes)
526
+ will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
527
527
  end
528
528
 
529
529
  # Determines if a record with the particular +attributes+ should be
@@ -532,7 +532,8 @@ module ActiveRecord
532
532
  #
533
533
  # Returns false if there is a +destroy_flag+ on the attributes.
534
534
  def call_reject_if(association_name, attributes)
535
- return false if has_destroy_flag?(attributes)
535
+ return false if will_be_destroyed?(association_name, attributes)
536
+
536
537
  case callback = self.nested_attributes_options[association_name][:reject_if]
537
538
  when Symbol
538
539
  method(callback).arity == 0 ? send(callback) : send(callback, attributes)
@@ -541,6 +542,15 @@ module ActiveRecord
541
542
  end
542
543
  end
543
544
 
545
+ # Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
546
+ def will_be_destroyed?(association_name, attributes)
547
+ allow_destroy?(association_name) && has_destroy_flag?(attributes)
548
+ end
549
+
550
+ def allow_destroy?(association_name)
551
+ self.nested_attributes_options[association_name][:allow_destroy]
552
+ end
553
+
544
554
  def raise_nested_attributes_record_not_found!(association_name, record_id)
545
555
  raise RecordNotFound, "Couldn't find #{self.class._reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
546
556
  end
@@ -57,8 +57,10 @@ module ActiveRecord
57
57
  console do |app|
58
58
  require "active_record/railties/console_sandbox" if app.sandbox?
59
59
  require "active_record/base"
60
- console = ActiveSupport::Logger.new(STDERR)
61
- Rails.logger.extend ActiveSupport::Logger.broadcast console
60
+ unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDERR, STDOUT)
61
+ console = ActiveSupport::Logger.new(STDERR)
62
+ Rails.logger.extend ActiveSupport::Logger.broadcast console
63
+ end
62
64
  end
63
65
 
64
66
  runner do
@@ -63,6 +63,8 @@ db_namespace = namespace :db do
63
63
  namespace :migrate do
64
64
  # desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
65
65
  task :redo => [:environment, :load_config] do
66
+ raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
67
+
66
68
  if ENV['VERSION']
67
69
  db_namespace['migrate:down'].invoke
68
70
  db_namespace['migrate:up'].invoke
@@ -77,16 +79,17 @@ db_namespace = namespace :db do
77
79
 
78
80
  # desc 'Runs the "up" for a given migration VERSION.'
79
81
  task :up => [:environment, :load_config] do
82
+ raise "VERSION is required" if ENV["VERSION"] && ENV["VERSION"].empty?
83
+
80
84
  version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
81
- raise 'VERSION is required' unless version
82
85
  ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version)
83
86
  db_namespace['_dump'].invoke
84
87
  end
85
88
 
86
89
  # desc 'Runs the "down" for a given migration VERSION.'
87
90
  task :down => [:environment, :load_config] do
91
+ raise "VERSION is required - To go down one migration, use db:rollback" if ENV["VERSION"] && ENV["VERSION"].empty?
88
92
  version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
89
- raise 'VERSION is required - To go down one migration, run db:rollback' unless version
90
93
  ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version)
91
94
  db_namespace['_dump'].invoke
92
95
  end
@@ -96,26 +99,13 @@ db_namespace = namespace :db do
96
99
  unless ActiveRecord::SchemaMigration.table_exists?
97
100
  abort 'Schema migrations table does not exist yet.'
98
101
  end
99
- db_list = ActiveRecord::SchemaMigration.normalized_versions
100
-
101
- file_list =
102
- ActiveRecord::Migrator.migrations_paths.flat_map do |path|
103
- # match "20091231235959_some_name.rb" and "001_some_name.rb" pattern
104
- Dir.foreach(path).grep(/^(\d{3,})_(.+)\.rb$/) do
105
- version = ActiveRecord::SchemaMigration.normalize_migration_number($1)
106
- status = db_list.delete(version) ? 'up' : 'down'
107
- [status, version, $2.humanize]
108
- end
109
- end
110
102
 
111
- db_list.map! do |version|
112
- ['up', version, '********** NO FILE **********']
113
- end
114
103
  # output
115
104
  puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n"
116
105
  puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
117
106
  puts "-" * 50
118
- (db_list + file_list).sort_by { |_, version, _| version }.each do |status, version, name|
107
+ paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
108
+ ActiveRecord::Migrator.migrations_status(paths).each do |status, version, name|
119
109
  puts "#{status.center(8)} #{version.ljust(14)} #{name}"
120
110
  end
121
111
  puts
@@ -7,8 +7,8 @@ module ActiveRecord
7
7
  extend ActiveSupport::Concern
8
8
 
9
9
  included do
10
- class_attribute :_reflections
11
- class_attribute :aggregate_reflections
10
+ class_attribute :_reflections, instance_writer: false
11
+ class_attribute :aggregate_reflections, instance_writer: false
12
12
  self._reflections = {}
13
13
  self.aggregate_reflections = {}
14
14
  end
@@ -32,6 +32,7 @@ module ActiveRecord
32
32
  end
33
33
 
34
34
  def self.add_reflection(ar, name, reflection)
35
+ ar.clear_reflections_cache
35
36
  ar._reflections = ar._reflections.merge(name.to_s => reflection)
36
37
  end
37
38
 
@@ -67,16 +68,21 @@ module ActiveRecord
67
68
  #
68
69
  # @api public
69
70
  def reflections
70
- ref = {}
71
- _reflections.each do |name, reflection|
72
- parent_name, parent_reflection = reflection.parent_reflection
73
- if parent_name
74
- ref[parent_name] = parent_reflection
75
- else
76
- ref[name] = reflection
71
+ @__reflections ||= begin
72
+ ref = {}
73
+
74
+ _reflections.each do |name, reflection|
75
+ parent_name, parent_reflection = reflection.parent_reflection
76
+
77
+ if parent_name
78
+ ref[parent_name] = parent_reflection
79
+ else
80
+ ref[name] = reflection
81
+ end
77
82
  end
83
+
84
+ ref
78
85
  end
79
- ref
80
86
  end
81
87
 
82
88
  # Returns an array of AssociationReflection objects for all the
@@ -116,6 +122,10 @@ module ActiveRecord
116
122
  def reflect_on_all_autosave_associations
117
123
  reflections.values.select { |reflection| reflection.options[:autosave] }
118
124
  end
125
+
126
+ def clear_reflections_cache #:nodoc:
127
+ @__reflections = nil
128
+ end
119
129
  end
120
130
 
121
131
  # Holds all the methods that are shared between MacroReflection, AssociationReflection
@@ -161,6 +171,20 @@ module ActiveRecord
161
171
 
162
172
  macro
163
173
  end
174
+
175
+ def inverse_of
176
+ return unless inverse_name
177
+
178
+ @inverse_of ||= klass._reflect_on_association inverse_name
179
+ end
180
+
181
+ def check_validity_of_inverse!
182
+ unless polymorphic?
183
+ if has_inverse? && inverse_of.nil?
184
+ raise InverseOfAssociationNotFoundError.new(self)
185
+ end
186
+ end
187
+ end
164
188
  end
165
189
  # Base class for AggregateReflection and AssociationReflection. Objects of
166
190
  # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
@@ -303,7 +327,7 @@ module ActiveRecord
303
327
  end
304
328
 
305
329
  def foreign_key
306
- @foreign_key ||= options[:foreign_key] || derive_foreign_key
330
+ @foreign_key ||= options[:foreign_key] || derive_foreign_key.freeze
307
331
  end
308
332
 
309
333
  def association_foreign_key
@@ -331,14 +355,6 @@ module ActiveRecord
331
355
  check_validity_of_inverse!
332
356
  end
333
357
 
334
- def check_validity_of_inverse!
335
- unless polymorphic?
336
- if has_inverse? && inverse_of.nil?
337
- raise InverseOfAssociationNotFoundError.new(self)
338
- end
339
- end
340
- end
341
-
342
358
  def check_preloadable!
343
359
  return unless scope
344
360
 
@@ -387,12 +403,6 @@ module ActiveRecord
387
403
  inverse_name
388
404
  end
389
405
 
390
- def inverse_of
391
- return unless inverse_name
392
-
393
- @inverse_of ||= klass._reflect_on_association inverse_name
394
- end
395
-
396
406
  def polymorphic_inverse_of(associated_class)
397
407
  if has_inverse?
398
408
  if inverse_relationship = associated_class._reflect_on_association(options[:inverse_of])
@@ -865,6 +875,8 @@ module ActiveRecord
865
875
  klass.primary_key || raise(UnknownPrimaryKey.new(klass))
866
876
  end
867
877
 
878
+ def inverse_name; delegate_reflection.send(:inverse_name); end
879
+
868
880
  private
869
881
  def derive_class_name
870
882
  # get the class_name of the belongs_to association of the through reflection