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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +249 -0
- data/lib/active_record/aggregations.rb +6 -3
- data/lib/active_record/associations/association_scope.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +17 -6
- data/lib/active_record/associations/collection_proxy.rb +2 -2
- data/lib/active_record/associations/has_many_through_association.rb +5 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +7 -1
- data/lib/active_record/associations/join_dependency.rb +2 -1
- data/lib/active_record/associations/preloader/association.rb +5 -1
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +1 -0
- data/lib/active_record/attribute_methods/write.rb +1 -1
- data/lib/active_record/attribute_methods.rb +4 -8
- data/lib/active_record/attribute_set/builder.rb +21 -11
- data/lib/active_record/attribute_set.rb +4 -0
- data/lib/active_record/attributes.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +21 -7
- data/lib/active_record/connection_adapters/abstract_adapter.rb +12 -1
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +44 -9
- data/lib/active_record/connection_adapters/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -19
- data/lib/active_record/connection_adapters/mysql_adapter.rb +9 -2
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +13 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +2 -6
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -12
- data/lib/active_record/core.rb +2 -0
- data/lib/active_record/enum.rb +2 -3
- data/lib/active_record/errors.rb +4 -3
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/migration.rb +34 -6
- data/lib/active_record/model_schema.rb +3 -1
- data/lib/active_record/nested_attributes.rb +12 -2
- data/lib/active_record/railtie.rb +4 -2
- data/lib/active_record/railties/databases.rake +7 -17
- data/lib/active_record/reflection.rb +37 -25
- data/lib/active_record/relation/calculations.rb +10 -3
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +1 -1
- data/lib/active_record/relation/spawn_methods.rb +7 -3
- data/lib/active_record/schema_migration.rb +1 -4
- data/lib/active_record/tasks/database_tasks.rb +4 -1
- data/lib/active_record/tasks/mysql_database_tasks.rb +30 -16
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -8
- data/lib/active_record/type/date.rb +4 -0
- data/lib/active_record/type/decimal.rb +19 -3
- data/lib/active_record/type/value.rb +5 -0
- data/lib/active_record/validations/uniqueness.rb +7 -1
- data/lib/active_record.rb +2 -0
- 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
|
-
"
|
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:
|
@@ -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
|
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
|
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
|
-
|
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
|
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:
|
data/lib/active_record/core.rb
CHANGED
@@ -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+
|
data/lib/active_record/enum.rb
CHANGED
@@ -18,10 +18,9 @@ module ActiveRecord
|
|
18
18
|
# conversation.archived? # => true
|
19
19
|
# conversation.status # => "archived"
|
20
20
|
#
|
21
|
-
# # conversation.
|
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
|
|
data/lib/active_record/errors.rb
CHANGED
@@ -219,11 +219,12 @@ module ActiveRecord
|
|
219
219
|
class UnknownPrimaryKey < ActiveRecordError
|
220
220
|
attr_reader :model
|
221
221
|
|
222
|
-
def initialize(model)
|
223
|
-
|
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.
|
@@ -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
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
61
|
-
|
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
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|