activerecord 4.1.15 → 4.2.11.3
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1162 -1792
- data/README.rdoc +15 -10
- data/lib/active_record.rb +4 -0
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +83 -38
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +63 -27
- data/lib/active_record/associations/collection_proxy.rb +29 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +26 -13
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/preloader.rb +36 -26
- data/lib/active_record/associations/preloader/association.rb +14 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +5 -12
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods.rb +56 -94
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +19 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -39
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -11
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +55 -69
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- data/lib/active_record/migration.rb +71 -46
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +5 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +46 -26
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +18 -11
- data/lib/active_record/railties/databases.rake +50 -51
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +273 -114
- data/lib/active_record/relation.rb +57 -25
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/finder_methods.rb +70 -47
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder.rb +16 -8
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
- data/lib/active_record/relation/query_methods.rb +114 -65
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +53 -27
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +5 -3
- data/lib/active_record/validations/uniqueness.rb +25 -29
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +66 -11
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
module ActiveRecord
|
3
2
|
# = Active Record Query Cache
|
4
3
|
class QueryCache
|
@@ -29,9 +28,10 @@ module ActiveRecord
|
|
29
28
|
end
|
30
29
|
|
31
30
|
def call(env)
|
32
|
-
|
31
|
+
connection = ActiveRecord::Base.connection
|
32
|
+
enabled = connection.query_cache_enabled
|
33
33
|
connection_id = ActiveRecord::Base.connection_id
|
34
|
-
|
34
|
+
connection.enable_query_cache!
|
35
35
|
|
36
36
|
response = @app.call(env)
|
37
37
|
response[2] = Rack::BodyProxy.new(response[2]) do
|
@@ -37,15 +37,18 @@ module ActiveRecord
|
|
37
37
|
# Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
|
38
38
|
def find_by_sql(sql, binds = [])
|
39
39
|
result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds)
|
40
|
-
column_types =
|
40
|
+
column_types = result_set.column_types.dup
|
41
|
+
columns_hash.each_key { |k| column_types.delete k }
|
42
|
+
message_bus = ActiveSupport::Notifications.instrumenter
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
44
|
+
payload = {
|
45
|
+
record_count: result_set.length,
|
46
|
+
class_name: name
|
47
|
+
}
|
47
48
|
|
48
|
-
|
49
|
+
message_bus.instrument('instantiation.active_record', payload) do
|
50
|
+
result_set.map { |record| instantiate(record, column_types) }
|
51
|
+
end
|
49
52
|
end
|
50
53
|
|
51
54
|
# Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
|
@@ -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
|
@@ -114,17 +116,22 @@ module ActiveRecord
|
|
114
116
|
# and then establishes the connection.
|
115
117
|
initializer "active_record.initialize_database" do |app|
|
116
118
|
ActiveSupport.on_load(:active_record) do
|
119
|
+
self.configurations = Rails.application.config.database_configuration
|
117
120
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
end
|
124
|
-
end
|
121
|
+
begin
|
122
|
+
establish_connection
|
123
|
+
rescue ActiveRecord::NoDatabaseError
|
124
|
+
warn <<-end_warning
|
125
|
+
Oops - You have a database configured, but it doesn't exist yet!
|
125
126
|
|
126
|
-
|
127
|
-
|
127
|
+
Here's how to get started:
|
128
|
+
|
129
|
+
1. Configure your database in config/database.yml.
|
130
|
+
2. Run `bin/rake db:create` to create the database.
|
131
|
+
3. Run `bin/rake db:setup` to load your database schema.
|
132
|
+
end_warning
|
133
|
+
raise
|
134
|
+
end
|
128
135
|
end
|
129
136
|
end
|
130
137
|
|
@@ -28,21 +28,32 @@ db_namespace = namespace :db do
|
|
28
28
|
ActiveRecord::Tasks::DatabaseTasks.drop_current
|
29
29
|
end
|
30
30
|
|
31
|
+
namespace :purge do
|
32
|
+
task :all => :load_config do
|
33
|
+
ActiveRecord::Tasks::DatabaseTasks.purge_all
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# desc "Empty the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV it defaults to purging the development and test databases."
|
38
|
+
task :purge => [:load_config] do
|
39
|
+
ActiveRecord::Tasks::DatabaseTasks.purge_current
|
40
|
+
end
|
41
|
+
|
31
42
|
desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
|
32
43
|
task :migrate => [:environment, :load_config] do
|
33
|
-
ActiveRecord::
|
34
|
-
|
35
|
-
ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
|
36
|
-
end
|
37
|
-
db_namespace['_dump'].invoke if ActiveRecord::Base.dump_schema_after_migration
|
44
|
+
ActiveRecord::Tasks::DatabaseTasks.migrate
|
45
|
+
db_namespace['_dump'].invoke
|
38
46
|
end
|
39
47
|
|
48
|
+
# IMPORTANT: This task won't dump the schema if ActiveRecord::Base.dump_schema_after_migration is set to false
|
40
49
|
task :_dump do
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
50
|
+
if ActiveRecord::Base.dump_schema_after_migration
|
51
|
+
case ActiveRecord::Base.schema_format
|
52
|
+
when :ruby then db_namespace["schema:dump"].invoke
|
53
|
+
when :sql then db_namespace["structure:dump"].invoke
|
54
|
+
else
|
55
|
+
raise "unknown schema format #{ActiveRecord::Base.schema_format}"
|
56
|
+
end
|
46
57
|
end
|
47
58
|
# Allow this task to be called as many times as required. An example is the
|
48
59
|
# migrate:redo task, which calls other two internally that depend on this one.
|
@@ -52,6 +63,8 @@ db_namespace = namespace :db do
|
|
52
63
|
namespace :migrate do
|
53
64
|
# desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
|
54
65
|
task :redo => [:environment, :load_config] do
|
66
|
+
raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
|
67
|
+
|
55
68
|
if ENV['VERSION']
|
56
69
|
db_namespace['migrate:down'].invoke
|
57
70
|
db_namespace['migrate:up'].invoke
|
@@ -66,48 +79,34 @@ db_namespace = namespace :db do
|
|
66
79
|
|
67
80
|
# desc 'Runs the "up" for a given migration VERSION.'
|
68
81
|
task :up => [:environment, :load_config] do
|
82
|
+
raise "VERSION is required" if ENV["VERSION"] && ENV["VERSION"].empty?
|
83
|
+
|
69
84
|
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
|
70
|
-
raise 'VERSION is required' unless version
|
71
85
|
ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version)
|
72
86
|
db_namespace['_dump'].invoke
|
73
87
|
end
|
74
88
|
|
75
89
|
# desc 'Runs the "down" for a given migration VERSION.'
|
76
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?
|
77
92
|
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
|
78
|
-
raise 'VERSION is required - To go down one migration, run db:rollback' unless version
|
79
93
|
ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version)
|
80
94
|
db_namespace['_dump'].invoke
|
81
95
|
end
|
82
96
|
|
83
97
|
desc 'Display status of migrations'
|
84
98
|
task :status => [:environment, :load_config] do
|
85
|
-
unless ActiveRecord::
|
86
|
-
|
87
|
-
next # means "return" for rake task
|
88
|
-
end
|
89
|
-
db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}")
|
90
|
-
db_list.map! { |version| ActiveRecord::SchemaMigration.normalize_migration_number(version) }
|
91
|
-
file_list = []
|
92
|
-
ActiveRecord::Migrator.migrations_paths.each do |path|
|
93
|
-
Dir.foreach(path) do |file|
|
94
|
-
# match "20091231235959_some_name.rb" and "001_some_name.rb" pattern
|
95
|
-
if match_data = /^(\d{3,})_(.+)\.rb$/.match(file)
|
96
|
-
version = ActiveRecord::SchemaMigration.normalize_migration_number(match_data[1])
|
97
|
-
status = db_list.delete(version) ? 'up' : 'down'
|
98
|
-
file_list << [status, version, match_data[2].humanize]
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
db_list.map! do |version|
|
103
|
-
['up', version, '********** NO FILE **********']
|
99
|
+
unless ActiveRecord::SchemaMigration.table_exists?
|
100
|
+
abort 'Schema migrations table does not exist yet.'
|
104
101
|
end
|
102
|
+
|
105
103
|
# output
|
106
104
|
puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n"
|
107
105
|
puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
|
108
106
|
puts "-" * 50
|
109
|
-
|
110
|
-
|
107
|
+
paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
|
108
|
+
ActiveRecord::Migrator.migrations_status(paths).each do |status, version, name|
|
109
|
+
puts "#{status.center(8)} #{version.ljust(14)} #{name}"
|
111
110
|
end
|
112
111
|
puts
|
113
112
|
end
|
@@ -179,17 +178,22 @@ db_namespace = namespace :db do
|
|
179
178
|
task :load => [:environment, :load_config] do
|
180
179
|
require 'active_record/fixtures'
|
181
180
|
|
182
|
-
base_dir =
|
183
|
-
File.join [Rails.root, ENV['FIXTURES_PATH'] || %w{test fixtures}].flatten
|
184
|
-
else
|
185
|
-
ActiveRecord::Tasks::DatabaseTasks.fixtures_path
|
186
|
-
end
|
181
|
+
base_dir = ActiveRecord::Tasks::DatabaseTasks.fixtures_path
|
187
182
|
|
188
|
-
fixtures_dir =
|
183
|
+
fixtures_dir = if ENV['FIXTURES_DIR']
|
184
|
+
File.join base_dir, ENV['FIXTURES_DIR']
|
185
|
+
else
|
186
|
+
base_dir
|
187
|
+
end
|
189
188
|
|
190
|
-
|
191
|
-
|
192
|
-
|
189
|
+
fixture_files = if ENV['FIXTURES']
|
190
|
+
ENV['FIXTURES'].split(',')
|
191
|
+
else
|
192
|
+
# The use of String#[] here is to support namespaced fixtures
|
193
|
+
Dir["#{fixtures_dir}/**/*.yml"].map {|f| f[(fixtures_dir.size + 1)..-5] }
|
194
|
+
end
|
195
|
+
|
196
|
+
ActiveRecord::FixtureSet.create_fixtures(fixtures_dir, fixture_files)
|
193
197
|
end
|
194
198
|
|
195
199
|
# desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
|
@@ -201,16 +205,11 @@ db_namespace = namespace :db do
|
|
201
205
|
|
202
206
|
puts %Q(The fixture ID for "#{label}" is #{ActiveRecord::FixtureSet.identify(label)}.) if label
|
203
207
|
|
204
|
-
base_dir =
|
205
|
-
File.join [Rails.root, ENV['FIXTURES_PATH'] || %w{test fixtures}].flatten
|
206
|
-
else
|
207
|
-
ActiveRecord::Tasks::DatabaseTasks.fixtures_path
|
208
|
-
end
|
209
|
-
|
208
|
+
base_dir = ActiveRecord::Tasks::DatabaseTasks.fixtures_path
|
210
209
|
|
211
210
|
Dir["#{base_dir}/**/*.yml"].each do |file|
|
212
211
|
if data = YAML::load(ERB.new(IO.read(file)).result)
|
213
|
-
data.
|
212
|
+
data.each_key do |key|
|
214
213
|
key_id = ActiveRecord::FixtureSet.identify(key)
|
215
214
|
|
216
215
|
if key == label || key_id == id.to_i
|
@@ -279,8 +278,8 @@ db_namespace = namespace :db do
|
|
279
278
|
db_namespace['structure:dump'].reenable
|
280
279
|
end
|
281
280
|
|
282
|
-
|
283
|
-
task :load => [:
|
281
|
+
desc "Recreate the databases from the structure.sql file"
|
282
|
+
task :load => [:load_config] do
|
284
283
|
ActiveRecord::Tasks::DatabaseTasks.load_schema_current(:sql, ENV['DB_STRUCTURE'])
|
285
284
|
end
|
286
285
|
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'active_support/core_ext/string/filters'
|
3
|
+
|
1
4
|
module ActiveRecord
|
2
5
|
# = Active Record Reflection
|
3
6
|
module Reflection # :nodoc:
|
@@ -11,25 +14,33 @@ module ActiveRecord
|
|
11
14
|
end
|
12
15
|
|
13
16
|
def self.create(macro, name, scope, options, ar)
|
14
|
-
case macro
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
klass = case macro
|
18
|
+
when :composed_of
|
19
|
+
AggregateReflection
|
20
|
+
when :has_many
|
21
|
+
HasManyReflection
|
22
|
+
when :has_one
|
23
|
+
HasOneReflection
|
24
|
+
when :belongs_to
|
25
|
+
BelongsToReflection
|
26
|
+
else
|
27
|
+
raise "Unsupported Macro: #{macro}"
|
28
|
+
end
|
29
|
+
|
30
|
+
reflection = klass.new(name, scope, options, ar)
|
31
|
+
options[:through] ? ThroughReflection.new(reflection) : reflection
|
22
32
|
end
|
23
33
|
|
24
34
|
def self.add_reflection(ar, name, reflection)
|
25
|
-
ar.
|
35
|
+
ar.clear_reflections_cache
|
36
|
+
ar._reflections = ar._reflections.merge(name.to_s => reflection)
|
26
37
|
end
|
27
38
|
|
28
39
|
def self.add_aggregate_reflection(ar, name, reflection)
|
29
|
-
ar.aggregate_reflections = ar.aggregate_reflections.merge(name.
|
40
|
+
ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
|
30
41
|
end
|
31
42
|
|
32
|
-
# \Reflection enables
|
43
|
+
# \Reflection enables interrogating of Active Record classes and objects
|
33
44
|
# about their associations and aggregations. This information can,
|
34
45
|
# for example, be used in a form builder that takes an Active Record object
|
35
46
|
# and creates input fields for all of the attributes depending on their type
|
@@ -48,25 +59,30 @@ module ActiveRecord
|
|
48
59
|
# Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
|
49
60
|
#
|
50
61
|
def reflect_on_aggregation(aggregation)
|
51
|
-
aggregate_reflections[aggregation.
|
62
|
+
aggregate_reflections[aggregation.to_s]
|
52
63
|
end
|
53
64
|
|
54
65
|
# Returns a Hash of name of the reflection as the key and a AssociationReflection as the value.
|
55
66
|
#
|
56
|
-
# Account.reflections # => {balance
|
67
|
+
# Account.reflections # => {"balance" => AggregateReflection}
|
57
68
|
#
|
58
69
|
# @api public
|
59
70
|
def reflections
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
67
82
|
end
|
83
|
+
|
84
|
+
ref
|
68
85
|
end
|
69
|
-
ref
|
70
86
|
end
|
71
87
|
|
72
88
|
# Returns an array of AssociationReflection objects for all the
|
@@ -92,12 +108,12 @@ module ActiveRecord
|
|
92
108
|
#
|
93
109
|
# @api public
|
94
110
|
def reflect_on_association(association)
|
95
|
-
reflections[association.
|
111
|
+
reflections[association.to_s]
|
96
112
|
end
|
97
113
|
|
98
114
|
# @api private
|
99
115
|
def _reflect_on_association(association) #:nodoc:
|
100
|
-
_reflections[association.
|
116
|
+
_reflections[association.to_s]
|
101
117
|
end
|
102
118
|
|
103
119
|
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
|
@@ -106,28 +122,87 @@ module ActiveRecord
|
|
106
122
|
def reflect_on_all_autosave_associations
|
107
123
|
reflections.values.select { |reflection| reflection.options[:autosave] }
|
108
124
|
end
|
125
|
+
|
126
|
+
def clear_reflections_cache #:nodoc:
|
127
|
+
@__reflections = nil
|
128
|
+
end
|
109
129
|
end
|
110
130
|
|
131
|
+
# Holds all the methods that are shared between MacroReflection, AssociationReflection
|
132
|
+
# and ThroughReflection
|
133
|
+
class AbstractReflection # :nodoc:
|
134
|
+
def table_name
|
135
|
+
klass.table_name
|
136
|
+
end
|
137
|
+
|
138
|
+
# Returns a new, unsaved instance of the associated class. +attributes+ will
|
139
|
+
# be passed to the class's constructor.
|
140
|
+
def build_association(attributes, &block)
|
141
|
+
klass.new(attributes, &block)
|
142
|
+
end
|
143
|
+
|
144
|
+
def quoted_table_name
|
145
|
+
klass.quoted_table_name
|
146
|
+
end
|
147
|
+
|
148
|
+
def primary_key_type
|
149
|
+
klass.type_for_attribute(klass.primary_key)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns the class name for the macro.
|
153
|
+
#
|
154
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
|
155
|
+
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
156
|
+
def class_name
|
157
|
+
@class_name ||= (options[:class_name] || derive_class_name).to_s
|
158
|
+
end
|
159
|
+
|
160
|
+
JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
|
161
|
+
|
162
|
+
def join_keys(assoc_klass)
|
163
|
+
JoinKeys.new(foreign_key, active_record_primary_key)
|
164
|
+
end
|
165
|
+
|
166
|
+
def source_macro
|
167
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
168
|
+
ActiveRecord::Base.source_macro is deprecated and will be removed
|
169
|
+
without replacement.
|
170
|
+
MSG
|
171
|
+
|
172
|
+
macro
|
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
|
188
|
+
end
|
111
189
|
# Base class for AggregateReflection and AssociationReflection. Objects of
|
112
190
|
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
|
113
191
|
#
|
114
192
|
# MacroReflection
|
115
|
-
# AggregateReflection
|
116
193
|
# AssociationReflection
|
117
|
-
#
|
118
|
-
|
194
|
+
# AggregateReflection
|
195
|
+
# HasManyReflection
|
196
|
+
# HasOneReflection
|
197
|
+
# BelongsToReflection
|
198
|
+
# ThroughReflection
|
199
|
+
class MacroReflection < AbstractReflection
|
119
200
|
# Returns the name of the macro.
|
120
201
|
#
|
121
202
|
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt>
|
122
203
|
# <tt>has_many :clients</tt> returns <tt>:clients</tt>
|
123
204
|
attr_reader :name
|
124
205
|
|
125
|
-
# Returns the macro type.
|
126
|
-
#
|
127
|
-
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:composed_of</tt>
|
128
|
-
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
|
129
|
-
attr_reader :macro
|
130
|
-
|
131
206
|
attr_reader :scope
|
132
207
|
|
133
208
|
# Returns the hash of options used for the macro.
|
@@ -140,8 +215,7 @@ module ActiveRecord
|
|
140
215
|
|
141
216
|
attr_reader :plural_name # :nodoc:
|
142
217
|
|
143
|
-
def initialize(
|
144
|
-
@macro = macro
|
218
|
+
def initialize(name, scope, options, active_record)
|
145
219
|
@name = name
|
146
220
|
@scope = scope
|
147
221
|
@options = options
|
@@ -165,15 +239,11 @@ module ActiveRecord
|
|
165
239
|
# <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
|
166
240
|
# <tt>has_many :clients</tt> returns the Client class
|
167
241
|
def klass
|
168
|
-
@klass ||= class_name
|
242
|
+
@klass ||= compute_class(class_name)
|
169
243
|
end
|
170
244
|
|
171
|
-
|
172
|
-
|
173
|
-
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
|
174
|
-
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
175
|
-
def class_name
|
176
|
-
@class_name ||= (options[:class_name] || derive_class_name).to_s
|
245
|
+
def compute_class(name)
|
246
|
+
name.constantize
|
177
247
|
end
|
178
248
|
|
179
249
|
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
|
@@ -218,49 +288,46 @@ module ActiveRecord
|
|
218
288
|
# a new association object. Use +build_association+ or +create_association+
|
219
289
|
# instead. This allows plugins to hook into association object creation.
|
220
290
|
def klass
|
221
|
-
@klass ||=
|
291
|
+
@klass ||= compute_class(class_name)
|
292
|
+
end
|
293
|
+
|
294
|
+
def compute_class(name)
|
295
|
+
active_record.send(:compute_type, name)
|
222
296
|
end
|
223
297
|
|
224
298
|
attr_reader :type, :foreign_type
|
225
299
|
attr_accessor :parent_reflection # [:name, Reflection]
|
226
300
|
|
227
|
-
def initialize(
|
301
|
+
def initialize(name, scope, options, active_record)
|
228
302
|
super
|
229
|
-
@collection = [:has_many, :has_and_belongs_to_many].include?(macro)
|
230
303
|
@automatic_inverse_of = nil
|
231
|
-
@type = options[:as] && "#{options[:as]}_type"
|
304
|
+
@type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
|
232
305
|
@foreign_type = options[:foreign_type] || "#{name}_type"
|
233
306
|
@constructable = calculate_constructable(macro, options)
|
307
|
+
@association_scope_cache = {}
|
308
|
+
@scope_lock = Mutex.new
|
234
309
|
end
|
235
310
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
311
|
+
def association_scope_cache(conn, owner)
|
312
|
+
key = conn.prepared_statements
|
313
|
+
if polymorphic?
|
314
|
+
key = [key, owner._read_attribute(@foreign_type)]
|
315
|
+
end
|
316
|
+
@association_scope_cache[key] ||= @scope_lock.synchronize {
|
317
|
+
@association_scope_cache[key] ||= yield
|
318
|
+
}
|
240
319
|
end
|
241
320
|
|
242
321
|
def constructable? # :nodoc:
|
243
322
|
@constructable
|
244
323
|
end
|
245
324
|
|
246
|
-
def table_name
|
247
|
-
klass.table_name
|
248
|
-
end
|
249
|
-
|
250
|
-
def quoted_table_name
|
251
|
-
klass.quoted_table_name
|
252
|
-
end
|
253
|
-
|
254
325
|
def join_table
|
255
326
|
@join_table ||= options[:join_table] || derive_join_table
|
256
327
|
end
|
257
328
|
|
258
329
|
def foreign_key
|
259
|
-
@foreign_key ||= options[:foreign_key] || derive_foreign_key
|
260
|
-
end
|
261
|
-
|
262
|
-
def primary_key_column
|
263
|
-
klass.columns_hash[klass.primary_key]
|
330
|
+
@foreign_key ||= options[:foreign_key] || derive_foreign_key.freeze
|
264
331
|
end
|
265
332
|
|
266
333
|
def association_foreign_key
|
@@ -288,13 +355,25 @@ module ActiveRecord
|
|
288
355
|
check_validity_of_inverse!
|
289
356
|
end
|
290
357
|
|
291
|
-
def
|
292
|
-
unless
|
293
|
-
|
294
|
-
|
295
|
-
|
358
|
+
def check_preloadable!
|
359
|
+
return unless scope
|
360
|
+
|
361
|
+
if scope.arity > 0
|
362
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
363
|
+
The association scope '#{name}' is instance dependent (the scope
|
364
|
+
block takes an argument). Preloading happens before the individual
|
365
|
+
instances are created. This means that there is no instance being
|
366
|
+
passed to the association scope. This will most likely result in
|
367
|
+
broken or incorrect behavior. Joining, Preloading and eager loading
|
368
|
+
of these associations is deprecated and will be removed in the future.
|
369
|
+
MSG
|
296
370
|
end
|
297
371
|
end
|
372
|
+
alias :check_eager_loadable! :check_preloadable!
|
373
|
+
|
374
|
+
def join_id_for(owner) # :nodoc:
|
375
|
+
owner[active_record_primary_key]
|
376
|
+
end
|
298
377
|
|
299
378
|
def through_reflection
|
300
379
|
nil
|
@@ -320,18 +399,10 @@ module ActiveRecord
|
|
320
399
|
scope ? [[scope]] : [[]]
|
321
400
|
end
|
322
401
|
|
323
|
-
alias :source_macro :macro
|
324
|
-
|
325
402
|
def has_inverse?
|
326
403
|
inverse_name
|
327
404
|
end
|
328
405
|
|
329
|
-
def inverse_of
|
330
|
-
return unless inverse_name
|
331
|
-
|
332
|
-
@inverse_of ||= klass._reflect_on_association inverse_name
|
333
|
-
end
|
334
|
-
|
335
406
|
def polymorphic_inverse_of(associated_class)
|
336
407
|
if has_inverse?
|
337
408
|
if inverse_relationship = associated_class._reflect_on_association(options[:inverse_of])
|
@@ -342,11 +413,16 @@ module ActiveRecord
|
|
342
413
|
end
|
343
414
|
end
|
344
415
|
|
416
|
+
# Returns the macro type.
|
417
|
+
#
|
418
|
+
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
|
419
|
+
def macro; raise NotImplementedError; end
|
420
|
+
|
345
421
|
# Returns whether or not this association reflection is for a collection
|
346
422
|
# association. Returns +true+ if the +macro+ is either +has_many+ or
|
347
423
|
# +has_and_belongs_to_many+, +false+ otherwise.
|
348
424
|
def collection?
|
349
|
-
|
425
|
+
false
|
350
426
|
end
|
351
427
|
|
352
428
|
# Returns whether or not the association should be validated as part of
|
@@ -359,18 +435,19 @@ module ActiveRecord
|
|
359
435
|
# * you use autosave; <tt>autosave: true</tt>
|
360
436
|
# * the association is a +has_many+ association
|
361
437
|
def validate?
|
362
|
-
!options[:validate].nil? ? options[:validate] : (options[:autosave] == true ||
|
438
|
+
!options[:validate].nil? ? options[:validate] : (options[:autosave] == true || collection?)
|
363
439
|
end
|
364
440
|
|
365
441
|
# Returns +true+ if +self+ is a +belongs_to+ reflection.
|
366
|
-
def belongs_to
|
367
|
-
|
368
|
-
|
442
|
+
def belongs_to?; false; end
|
443
|
+
|
444
|
+
# Returns +true+ if +self+ is a +has_one+ reflection.
|
445
|
+
def has_one?; false; end
|
369
446
|
|
370
447
|
def association_class
|
371
448
|
case macro
|
372
449
|
when :belongs_to
|
373
|
-
if
|
450
|
+
if polymorphic?
|
374
451
|
Associations::BelongsToPolymorphicAssociation
|
375
452
|
else
|
376
453
|
Associations::BelongsToAssociation
|
@@ -391,7 +468,7 @@ module ActiveRecord
|
|
391
468
|
end
|
392
469
|
|
393
470
|
def polymorphic?
|
394
|
-
options
|
471
|
+
options[:polymorphic]
|
395
472
|
end
|
396
473
|
|
397
474
|
VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
|
@@ -408,7 +485,7 @@ module ActiveRecord
|
|
408
485
|
def calculate_constructable(macro, options)
|
409
486
|
case macro
|
410
487
|
when :belongs_to
|
411
|
-
!
|
488
|
+
!polymorphic?
|
412
489
|
when :has_one
|
413
490
|
!options[:through]
|
414
491
|
else
|
@@ -497,7 +574,7 @@ module ActiveRecord
|
|
497
574
|
end
|
498
575
|
|
499
576
|
def derive_join_table
|
500
|
-
|
577
|
+
ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
|
501
578
|
end
|
502
579
|
|
503
580
|
def primary_key(klass)
|
@@ -505,15 +582,72 @@ module ActiveRecord
|
|
505
582
|
end
|
506
583
|
end
|
507
584
|
|
585
|
+
class HasManyReflection < AssociationReflection # :nodoc:
|
586
|
+
def initialize(name, scope, options, active_record)
|
587
|
+
super(name, scope, options, active_record)
|
588
|
+
end
|
589
|
+
|
590
|
+
def macro; :has_many; end
|
591
|
+
|
592
|
+
def collection?; true; end
|
593
|
+
end
|
594
|
+
|
595
|
+
class HasOneReflection < AssociationReflection # :nodoc:
|
596
|
+
def initialize(name, scope, options, active_record)
|
597
|
+
super(name, scope, options, active_record)
|
598
|
+
end
|
599
|
+
|
600
|
+
def macro; :has_one; end
|
601
|
+
|
602
|
+
def has_one?; true; end
|
603
|
+
end
|
604
|
+
|
605
|
+
class BelongsToReflection < AssociationReflection # :nodoc:
|
606
|
+
def initialize(name, scope, options, active_record)
|
607
|
+
super(name, scope, options, active_record)
|
608
|
+
end
|
609
|
+
|
610
|
+
def macro; :belongs_to; end
|
611
|
+
|
612
|
+
def belongs_to?; true; end
|
613
|
+
|
614
|
+
def join_keys(assoc_klass)
|
615
|
+
key = polymorphic? ? association_primary_key(assoc_klass) : association_primary_key
|
616
|
+
JoinKeys.new(key, foreign_key)
|
617
|
+
end
|
618
|
+
|
619
|
+
def join_id_for(owner) # :nodoc:
|
620
|
+
owner[foreign_key]
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
|
625
|
+
def initialize(name, scope, options, active_record)
|
626
|
+
super
|
627
|
+
end
|
628
|
+
|
629
|
+
def macro; :has_and_belongs_to_many; end
|
630
|
+
|
631
|
+
def collection?
|
632
|
+
true
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
508
636
|
# Holds all the meta-data about a :through association as it was specified
|
509
637
|
# in the Active Record class.
|
510
|
-
class ThroughReflection <
|
638
|
+
class ThroughReflection < AbstractReflection #:nodoc:
|
639
|
+
attr_reader :delegate_reflection
|
511
640
|
delegate :foreign_key, :foreign_type, :association_foreign_key,
|
512
641
|
:active_record_primary_key, :type, :to => :source_reflection
|
513
642
|
|
514
|
-
def initialize(
|
515
|
-
|
516
|
-
@
|
643
|
+
def initialize(delegate_reflection)
|
644
|
+
@delegate_reflection = delegate_reflection
|
645
|
+
@klass = delegate_reflection.options[:anonymous_class]
|
646
|
+
@source_reflection_name = delegate_reflection.options[:source]
|
647
|
+
end
|
648
|
+
|
649
|
+
def klass
|
650
|
+
@klass ||= delegate_reflection.compute_class(class_name)
|
517
651
|
end
|
518
652
|
|
519
653
|
# Returns the source of the through reflection. It checks both a singularized
|
@@ -531,12 +665,10 @@ module ActiveRecord
|
|
531
665
|
#
|
532
666
|
# tags_reflection = Post.reflect_on_association(:tags)
|
533
667
|
# tags_reflection.source_reflection
|
534
|
-
# # => <ActiveRecord::Reflection::
|
668
|
+
# # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
|
535
669
|
#
|
536
670
|
def source_reflection
|
537
|
-
|
538
|
-
through_reflection.klass._reflect_on_association(source_reflection_name)
|
539
|
-
end
|
671
|
+
through_reflection.klass._reflect_on_association(source_reflection_name)
|
540
672
|
end
|
541
673
|
|
542
674
|
# Returns the AssociationReflection object specified in the <tt>:through</tt> option
|
@@ -549,7 +681,7 @@ module ActiveRecord
|
|
549
681
|
#
|
550
682
|
# tags_reflection = Post.reflect_on_association(:tags)
|
551
683
|
# tags_reflection.through_reflection
|
552
|
-
# # => <ActiveRecord::Reflection::
|
684
|
+
# # => <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @active_record=Post, @plural_name="taggings">
|
553
685
|
#
|
554
686
|
def through_reflection
|
555
687
|
active_record._reflect_on_association(options[:through])
|
@@ -569,8 +701,8 @@ module ActiveRecord
|
|
569
701
|
#
|
570
702
|
# tags_reflection = Post.reflect_on_association(:tags)
|
571
703
|
# tags_reflection.chain
|
572
|
-
# # => [<ActiveRecord::Reflection::ThroughReflection: @
|
573
|
-
# <ActiveRecord::Reflection::
|
704
|
+
# # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
|
705
|
+
# <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
|
574
706
|
#
|
575
707
|
def chain
|
576
708
|
@chain ||= begin
|
@@ -623,8 +755,17 @@ module ActiveRecord
|
|
623
755
|
end
|
624
756
|
end
|
625
757
|
|
758
|
+
def join_keys(assoc_klass)
|
759
|
+
source_reflection.join_keys(assoc_klass)
|
760
|
+
end
|
761
|
+
|
626
762
|
# The macro used by the source association
|
627
763
|
def source_macro
|
764
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
765
|
+
ActiveRecord::Base.source_macro is deprecated and will be removed
|
766
|
+
without replacement.
|
767
|
+
MSG
|
768
|
+
|
628
769
|
source_reflection.source_macro
|
629
770
|
end
|
630
771
|
|
@@ -654,11 +795,11 @@ module ActiveRecord
|
|
654
795
|
# # => [:tag, :tags]
|
655
796
|
#
|
656
797
|
def source_reflection_names
|
657
|
-
|
798
|
+
options[:source] ? [options[:source]] : [name.to_s.singularize, name].uniq
|
658
799
|
end
|
659
800
|
|
660
801
|
def source_reflection_name # :nodoc:
|
661
|
-
return @source_reflection_name
|
802
|
+
return @source_reflection_name if @source_reflection_name
|
662
803
|
|
663
804
|
names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq
|
664
805
|
names = names.find_all { |n|
|
@@ -668,15 +809,13 @@ module ActiveRecord
|
|
668
809
|
if names.length > 1
|
669
810
|
example_options = options.dup
|
670
811
|
example_options[:source] = source_reflection_names.first
|
671
|
-
ActiveSupport::Deprecation.warn
|
672
|
-
Ambiguous source reflection for through association. Please
|
673
|
-
directive on your declaration like
|
674
|
-
|
675
|
-
class #{active_record.name} < ActiveRecord::Base
|
676
|
-
#{macro} :#{name}, #{example_options}
|
677
|
-
end
|
678
|
-
|
679
|
-
eowarn
|
812
|
+
ActiveSupport::Deprecation.warn \
|
813
|
+
"Ambiguous source reflection for through association. Please " \
|
814
|
+
"specify a :source directive on your declaration like:\n" \
|
815
|
+
"\n" \
|
816
|
+
" class #{active_record.name} < ActiveRecord::Base\n" \
|
817
|
+
" #{macro} :#{name}, #{example_options}\n" \
|
818
|
+
" end"
|
680
819
|
end
|
681
820
|
|
682
821
|
@source_reflection_name = names.first
|
@@ -690,28 +829,36 @@ directive on your declaration like:
|
|
690
829
|
through_reflection.options
|
691
830
|
end
|
692
831
|
|
832
|
+
def join_id_for(owner) # :nodoc:
|
833
|
+
source_reflection.join_id_for(owner)
|
834
|
+
end
|
835
|
+
|
693
836
|
def check_validity!
|
694
837
|
if through_reflection.nil?
|
695
838
|
raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
|
696
839
|
end
|
697
840
|
|
698
|
-
if through_reflection.
|
699
|
-
|
841
|
+
if through_reflection.polymorphic?
|
842
|
+
if has_one?
|
843
|
+
raise HasOneAssociationPolymorphicThroughError.new(active_record.name, self)
|
844
|
+
else
|
845
|
+
raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
|
846
|
+
end
|
700
847
|
end
|
701
848
|
|
702
849
|
if source_reflection.nil?
|
703
850
|
raise HasManyThroughSourceAssociationNotFoundError.new(self)
|
704
851
|
end
|
705
852
|
|
706
|
-
if options[:source_type] && source_reflection.
|
853
|
+
if options[:source_type] && !source_reflection.polymorphic?
|
707
854
|
raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
|
708
855
|
end
|
709
856
|
|
710
|
-
if source_reflection.
|
857
|
+
if source_reflection.polymorphic? && options[:source_type].nil?
|
711
858
|
raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
|
712
859
|
end
|
713
860
|
|
714
|
-
if
|
861
|
+
if has_one? && through_reflection.collection?
|
715
862
|
raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
|
716
863
|
end
|
717
864
|
|
@@ -720,15 +867,27 @@ directive on your declaration like:
|
|
720
867
|
|
721
868
|
protected
|
722
869
|
|
723
|
-
|
724
|
-
|
725
|
-
|
870
|
+
def actual_source_reflection # FIXME: this is a horrible name
|
871
|
+
source_reflection.send(:actual_source_reflection)
|
872
|
+
end
|
873
|
+
|
874
|
+
def primary_key(klass)
|
875
|
+
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
|
876
|
+
end
|
877
|
+
|
878
|
+
def inverse_name; delegate_reflection.send(:inverse_name); end
|
726
879
|
|
727
880
|
private
|
728
881
|
def derive_class_name
|
729
882
|
# get the class_name of the belongs_to association of the through reflection
|
730
883
|
options[:source_type] || source_reflection.class_name
|
731
884
|
end
|
885
|
+
|
886
|
+
delegate_methods = AssociationReflection.public_instance_methods -
|
887
|
+
public_instance_methods
|
888
|
+
|
889
|
+
delegate(*delegate_methods, to: :delegate_reflection)
|
890
|
+
|
732
891
|
end
|
733
892
|
end
|
734
893
|
end
|