activerecord 5.2.0 → 5.2.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 +4 -4
- data/CHANGELOG.md +214 -0
- data/lib/active_record/association_relation.rb +3 -3
- data/lib/active_record/associations.rb +9 -9
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +25 -10
- data/lib/active_record/associations/belongs_to_association.rb +14 -5
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -1
- data/lib/active_record/associations/builder/belongs_to.rb +11 -2
- data/lib/active_record/associations/collection_association.rb +19 -15
- data/lib/active_record/associations/collection_proxy.rb +8 -34
- data/lib/active_record/associations/has_many_association.rb +9 -0
- data/lib/active_record/associations/has_many_through_association.rb +25 -1
- data/lib/active_record/associations/has_one_association.rb +8 -0
- data/lib/active_record/associations/has_one_through_association.rb +5 -1
- data/lib/active_record/associations/join_dependency.rb +39 -64
- data/lib/active_record/associations/join_dependency/join_association.rb +12 -18
- data/lib/active_record/associations/join_dependency/join_part.rb +7 -0
- data/lib/active_record/associations/singular_association.rb +4 -10
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +15 -10
- data/lib/active_record/attribute_methods/read.rb +1 -1
- data/lib/active_record/autosave_association.rb +7 -2
- data/lib/active_record/callbacks.rb +4 -0
- data/lib/active_record/collection_cache_key.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +14 -8
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +19 -6
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +5 -14
- data/lib/active_record/connection_adapters/abstract/transaction.rb +23 -14
- data/lib/active_record/connection_adapters/abstract_adapter.rb +3 -1
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +18 -19
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +11 -2
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +9 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -4
- data/lib/active_record/core.rb +2 -1
- data/lib/active_record/counter_cache.rb +17 -13
- data/lib/active_record/enum.rb +1 -0
- data/lib/active_record/errors.rb +18 -12
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration.rb +1 -1
- data/lib/active_record/migration/compatibility.rb +15 -15
- data/lib/active_record/model_schema.rb +1 -1
- data/lib/active_record/persistence.rb +6 -5
- data/lib/active_record/query_cache.rb +4 -11
- data/lib/active_record/querying.rb +1 -1
- data/lib/active_record/railtie.rb +1 -3
- data/lib/active_record/relation.rb +39 -20
- data/lib/active_record/relation/calculations.rb +11 -8
- data/lib/active_record/relation/delegation.rb +30 -0
- data/lib/active_record/relation/finder_methods.rb +10 -8
- data/lib/active_record/relation/merger.rb +10 -11
- data/lib/active_record/relation/predicate_builder.rb +20 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/query_attribute.rb +5 -3
- data/lib/active_record/relation/query_methods.rb +45 -19
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/scoping/named.rb +2 -0
- data/lib/active_record/tasks/database_tasks.rb +1 -1
- data/lib/active_record/timestamp.rb +8 -1
- data/lib/active_record/transactions.rb +23 -20
- data/lib/active_record/type/serialized.rb +4 -0
- metadata +9 -10
data/lib/active_record/enum.rb
CHANGED
data/lib/active_record/errors.rb
CHANGED
@@ -117,16 +117,27 @@ module ActiveRecord
|
|
117
117
|
|
118
118
|
# Raised when a foreign key constraint cannot be added because the column type does not match the referenced column type.
|
119
119
|
class MismatchedForeignKey < StatementInvalid
|
120
|
-
def initialize(
|
121
|
-
|
120
|
+
def initialize(
|
121
|
+
adapter = nil,
|
122
|
+
message: nil,
|
123
|
+
sql: nil,
|
124
|
+
binds: nil,
|
125
|
+
table: nil,
|
126
|
+
foreign_key: nil,
|
127
|
+
target_table: nil,
|
128
|
+
primary_key: nil,
|
129
|
+
primary_key_column: nil
|
130
|
+
)
|
122
131
|
if table
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
132
|
+
type = primary_key_column.bigint? ? :bigint : primary_key_column.type
|
133
|
+
msg = <<-EOM.squish
|
134
|
+
Column `#{foreign_key}` on table `#{table}` does not match column `#{primary_key}` on `#{target_table}`,
|
135
|
+
which has type `#{primary_key_column.sql_type}`.
|
136
|
+
To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :#{type}.
|
137
|
+
(For example `t.#{type} :#{foreign_key}`).
|
127
138
|
EOM
|
128
139
|
else
|
129
|
-
msg = <<-EOM
|
140
|
+
msg = <<-EOM.squish
|
130
141
|
There is a mismatch between the foreign key and primary key column types.
|
131
142
|
Verify that the foreign key column type and the primary key of the associated table match types.
|
132
143
|
EOM
|
@@ -136,11 +147,6 @@ module ActiveRecord
|
|
136
147
|
end
|
137
148
|
super(msg)
|
138
149
|
end
|
139
|
-
|
140
|
-
private
|
141
|
-
def column_type(table, column)
|
142
|
-
@adapter.columns(table).detect { |c| c.name == column }.sql_type
|
143
|
-
end
|
144
150
|
end
|
145
151
|
|
146
152
|
# Raised when a record cannot be inserted or updated because it would violate a not null constraint.
|
@@ -1163,7 +1163,7 @@ module ActiveRecord
|
|
1163
1163
|
|
1164
1164
|
def migrations_path=(path)
|
1165
1165
|
ActiveSupport::Deprecation.warn \
|
1166
|
-
"ActiveRecord::Migrator.
|
1166
|
+
"`ActiveRecord::Migrator.migrations_path=` is now deprecated and will be removed in Rails 6.0. " \
|
1167
1167
|
"You can set the `migrations_paths` on the `connection` instead through the `database.yml`."
|
1168
1168
|
self.migrations_paths = [path]
|
1169
1169
|
end
|
@@ -17,20 +17,18 @@ module ActiveRecord
|
|
17
17
|
|
18
18
|
class V5_1 < V5_2
|
19
19
|
def change_column(table_name, column_name, type, options = {})
|
20
|
-
if adapter_name == "PostgreSQL"
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
26
|
-
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
20
|
+
if connection.adapter_name == "PostgreSQL"
|
21
|
+
super(table_name, column_name, type, options.except(:default, :null, :comment))
|
22
|
+
connection.change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
|
23
|
+
connection.change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
24
|
+
connection.change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
27
25
|
else
|
28
26
|
super
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
32
30
|
def create_table(table_name, options = {})
|
33
|
-
if adapter_name == "Mysql2"
|
31
|
+
if connection.adapter_name == "Mysql2"
|
34
32
|
super(table_name, options: "ENGINE=InnoDB", **options)
|
35
33
|
else
|
36
34
|
super
|
@@ -52,13 +50,13 @@ module ActiveRecord
|
|
52
50
|
end
|
53
51
|
|
54
52
|
def create_table(table_name, options = {})
|
55
|
-
if adapter_name == "PostgreSQL"
|
53
|
+
if connection.adapter_name == "PostgreSQL"
|
56
54
|
if options[:id] == :uuid && !options.key?(:default)
|
57
55
|
options[:default] = "uuid_generate_v4()"
|
58
56
|
end
|
59
57
|
end
|
60
58
|
|
61
|
-
unless adapter_name == "Mysql2" && options[:id] == :bigint
|
59
|
+
unless connection.adapter_name == "Mysql2" && options[:id] == :bigint
|
62
60
|
if [:integer, :bigint].include?(options[:id]) && !options.key?(:default)
|
63
61
|
options[:default] = nil
|
64
62
|
end
|
@@ -175,7 +173,7 @@ module ActiveRecord
|
|
175
173
|
if options[:name].present?
|
176
174
|
options[:name].to_s
|
177
175
|
else
|
178
|
-
index_name(table_name, column: column_names)
|
176
|
+
connection.index_name(table_name, column: column_names)
|
179
177
|
end
|
180
178
|
super
|
181
179
|
end
|
@@ -195,15 +193,17 @@ module ActiveRecord
|
|
195
193
|
end
|
196
194
|
|
197
195
|
def index_name_for_remove(table_name, options = {})
|
198
|
-
index_name = index_name(table_name, options)
|
196
|
+
index_name = connection.index_name(table_name, options)
|
199
197
|
|
200
|
-
unless index_name_exists?(table_name, index_name)
|
198
|
+
unless connection.index_name_exists?(table_name, index_name)
|
201
199
|
if options.is_a?(Hash) && options.has_key?(:name)
|
202
200
|
options_without_column = options.dup
|
203
201
|
options_without_column.delete :column
|
204
|
-
index_name_without_column = index_name(table_name, options_without_column)
|
202
|
+
index_name_without_column = connection.index_name(table_name, options_without_column)
|
205
203
|
|
206
|
-
|
204
|
+
if connection.index_name_exists?(table_name, index_name_without_column)
|
205
|
+
return index_name_without_column
|
206
|
+
end
|
207
207
|
end
|
208
208
|
|
209
209
|
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
|
@@ -375,7 +375,7 @@ module ActiveRecord
|
|
375
375
|
# default values when instantiating the Active Record object for this table.
|
376
376
|
def column_defaults
|
377
377
|
load_schema
|
378
|
-
@column_defaults ||= _default_attributes.to_hash
|
378
|
+
@column_defaults ||= _default_attributes.deep_dup.to_hash
|
379
379
|
end
|
380
380
|
|
381
381
|
def _default_attributes # :nodoc:
|
@@ -373,7 +373,7 @@ module ActiveRecord
|
|
373
373
|
became = klass.allocate
|
374
374
|
became.send(:initialize)
|
375
375
|
became.instance_variable_set("@attributes", @attributes)
|
376
|
-
became.instance_variable_set("@mutations_from_database", @mutations_from_database
|
376
|
+
became.instance_variable_set("@mutations_from_database", @mutations_from_database ||= nil)
|
377
377
|
became.instance_variable_set("@changed_attributes", attributes_changed_by_setter)
|
378
378
|
became.instance_variable_set("@new_record", new_record?)
|
379
379
|
became.instance_variable_set("@destroyed", destroyed?)
|
@@ -473,15 +473,16 @@ module ActiveRecord
|
|
473
473
|
verify_readonly_attribute(key.to_s)
|
474
474
|
end
|
475
475
|
|
476
|
+
id_in_database = self.id_in_database
|
477
|
+
attributes.each do |k, v|
|
478
|
+
write_attribute_without_type_cast(k, v)
|
479
|
+
end
|
480
|
+
|
476
481
|
affected_rows = self.class._update_record(
|
477
482
|
attributes,
|
478
483
|
self.class.primary_key => id_in_database
|
479
484
|
)
|
480
485
|
|
481
|
-
attributes.each do |k, v|
|
482
|
-
write_attribute_without_type_cast(k, v)
|
483
|
-
end
|
484
|
-
|
485
486
|
affected_rows == 1
|
486
487
|
end
|
487
488
|
|
@@ -26,19 +26,12 @@ module ActiveRecord
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def self.run
|
29
|
-
ActiveRecord::Base.connection_handler.connection_pool_list.
|
30
|
-
|
31
|
-
|
32
|
-
pool.enable_query_cache!
|
33
|
-
|
34
|
-
[pool, caching_was_enabled]
|
35
|
-
end
|
29
|
+
ActiveRecord::Base.connection_handler.connection_pool_list.
|
30
|
+
reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! }
|
36
31
|
end
|
37
32
|
|
38
|
-
def self.complete(
|
39
|
-
|
40
|
-
pool.disable_query_cache! unless caching_was_enabled
|
41
|
-
end
|
33
|
+
def self.complete(pools)
|
34
|
+
pools.each { |pool| pool.disable_query_cache! }
|
42
35
|
|
43
36
|
ActiveRecord::Base.connection_handler.connection_pool_list.each do |pool|
|
44
37
|
pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
|
@@ -40,7 +40,7 @@ module ActiveRecord
|
|
40
40
|
def find_by_sql(sql, binds = [], preparable: nil, &block)
|
41
41
|
result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable)
|
42
42
|
column_types = result_set.column_types.dup
|
43
|
-
|
43
|
+
attribute_types.each_key { |k| column_types.delete k }
|
44
44
|
message_bus = ActiveSupport::Notifications.instrumenter
|
45
45
|
|
46
46
|
payload = {
|
@@ -169,9 +169,7 @@ end_warning
|
|
169
169
|
end
|
170
170
|
|
171
171
|
initializer "active_record.set_executor_hooks" do
|
172
|
-
|
173
|
-
ActiveRecord::QueryCache.install_executor_hooks
|
174
|
-
end
|
172
|
+
ActiveRecord::QueryCache.install_executor_hooks
|
175
173
|
end
|
176
174
|
|
177
175
|
initializer "active_record.add_watchable_files" do |app|
|
@@ -54,7 +54,7 @@ module ActiveRecord
|
|
54
54
|
# user = users.new { |user| user.name = 'Oscar' }
|
55
55
|
# user.name # => Oscar
|
56
56
|
def new(attributes = nil, &block)
|
57
|
-
scoping { klass.new(
|
57
|
+
scoping { klass.new(values_for_create(attributes), &block) }
|
58
58
|
end
|
59
59
|
|
60
60
|
alias build new
|
@@ -82,7 +82,7 @@ module ActiveRecord
|
|
82
82
|
if attributes.is_a?(Array)
|
83
83
|
attributes.collect { |attr| create(attr, &block) }
|
84
84
|
else
|
85
|
-
scoping { klass.create(
|
85
|
+
scoping { klass.create(values_for_create(attributes), &block) }
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
@@ -96,7 +96,7 @@ module ActiveRecord
|
|
96
96
|
if attributes.is_a?(Array)
|
97
97
|
attributes.collect { |attr| create!(attr, &block) }
|
98
98
|
else
|
99
|
-
scoping { klass.create!(
|
99
|
+
scoping { klass.create!(values_for_create(attributes), &block) }
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
@@ -277,10 +277,10 @@ module ActiveRecord
|
|
277
277
|
# Please check unscoped if you want to remove all previous scopes (including
|
278
278
|
# the default_scope) during the execution of a block.
|
279
279
|
def scoping
|
280
|
-
previous, klass.current_scope = klass.current_scope(true), self
|
280
|
+
previous, klass.current_scope = klass.current_scope(true), self unless @delegate_to_klass
|
281
281
|
yield
|
282
282
|
ensure
|
283
|
-
klass.current_scope = previous
|
283
|
+
klass.current_scope = previous unless @delegate_to_klass
|
284
284
|
end
|
285
285
|
|
286
286
|
def _exec_scope(*args, &block) # :nodoc:
|
@@ -337,6 +337,14 @@ module ActiveRecord
|
|
337
337
|
@klass.connection.update stmt, "#{@klass} Update All"
|
338
338
|
end
|
339
339
|
|
340
|
+
def update(id = :all, attributes) # :nodoc:
|
341
|
+
if id == :all
|
342
|
+
each { |record| record.update(attributes) }
|
343
|
+
else
|
344
|
+
klass.update(id, attributes)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
340
348
|
# Destroys the records by instantiating each
|
341
349
|
# record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method.
|
342
350
|
# Each object's callbacks are executed (including <tt>:dependent</tt> association options).
|
@@ -436,17 +444,16 @@ module ActiveRecord
|
|
436
444
|
# # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
|
437
445
|
def to_sql
|
438
446
|
@to_sql ||= begin
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
end
|
447
|
+
if eager_loading?
|
448
|
+
apply_join_dependency do |relation, join_dependency|
|
449
|
+
relation = join_dependency.apply_column_aliases(relation)
|
450
|
+
relation.to_sql
|
451
|
+
end
|
452
|
+
else
|
453
|
+
conn = klass.connection
|
454
|
+
conn.unprepared_statement { conn.to_sql(arel) }
|
455
|
+
end
|
456
|
+
end
|
450
457
|
end
|
451
458
|
|
452
459
|
# Returns a hash of where conditions.
|
@@ -457,10 +464,8 @@ module ActiveRecord
|
|
457
464
|
where_clause.to_h(relation_table_name)
|
458
465
|
end
|
459
466
|
|
460
|
-
def scope_for_create
|
461
|
-
|
462
|
-
scope.merge!(attributes) if attributes
|
463
|
-
scope
|
467
|
+
def scope_for_create
|
468
|
+
where_values_hash.merge!(create_with_value.stringify_keys)
|
464
469
|
end
|
465
470
|
|
466
471
|
# Returns true if relation needs eager loading.
|
@@ -546,6 +551,7 @@ module ActiveRecord
|
|
546
551
|
if ActiveRecord::NullRelation === relation
|
547
552
|
[]
|
548
553
|
else
|
554
|
+
relation = join_dependency.apply_column_aliases(relation)
|
549
555
|
rows = connection.select_all(relation.arel, "SQL")
|
550
556
|
join_dependency.instantiate(rows, &block)
|
551
557
|
end.freeze
|
@@ -606,5 +612,18 @@ module ActiveRecord
|
|
606
612
|
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
|
607
613
|
string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ["raw_sql_"]
|
608
614
|
end
|
615
|
+
|
616
|
+
def values_for_create(attributes = nil)
|
617
|
+
result = attributes ? where_values_hash.merge!(attributes) : where_values_hash
|
618
|
+
|
619
|
+
# NOTE: if there are same keys in both create_with and result, create_with should be used.
|
620
|
+
# This is to make sure nested attributes don't get passed to the klass.new,
|
621
|
+
# while keeping the precedence of the duplicate keys in create_with.
|
622
|
+
create_with_value.stringify_keys.each do |k, v|
|
623
|
+
result[k] = v if result.key?(k)
|
624
|
+
end
|
625
|
+
|
626
|
+
result
|
627
|
+
end
|
609
628
|
end
|
610
629
|
end
|
@@ -190,11 +190,9 @@ module ActiveRecord
|
|
190
190
|
relation = apply_join_dependency
|
191
191
|
relation.pluck(*column_names)
|
192
192
|
else
|
193
|
-
enforce_raw_sql_whitelist(column_names)
|
193
|
+
klass.enforce_raw_sql_whitelist(column_names)
|
194
194
|
relation = spawn
|
195
|
-
relation.select_values = column_names
|
196
|
-
@klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
|
197
|
-
}
|
195
|
+
relation.select_values = column_names
|
198
196
|
result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
|
199
197
|
result.cast_values(klass.attribute_types)
|
200
198
|
end
|
@@ -209,7 +207,6 @@ module ActiveRecord
|
|
209
207
|
end
|
210
208
|
|
211
209
|
private
|
212
|
-
|
213
210
|
def has_include?(column_name)
|
214
211
|
eager_loading? || (includes_values.present? && column_name && column_name != :all)
|
215
212
|
end
|
@@ -224,10 +221,12 @@ module ActiveRecord
|
|
224
221
|
if operation == "count"
|
225
222
|
column_name ||= select_for_count
|
226
223
|
if column_name == :all
|
227
|
-
if distinct
|
224
|
+
if !distinct
|
225
|
+
distinct = distinct_select?(select_for_count) if group_values.empty?
|
226
|
+
elsif group_values.any? || select_values.empty? && order_values.empty?
|
228
227
|
column_name = primary_key
|
229
228
|
end
|
230
|
-
elsif column_name
|
229
|
+
elsif distinct_select?(column_name)
|
231
230
|
distinct = nil
|
232
231
|
end
|
233
232
|
end
|
@@ -239,6 +238,10 @@ module ActiveRecord
|
|
239
238
|
end
|
240
239
|
end
|
241
240
|
|
241
|
+
def distinct_select?(column_name)
|
242
|
+
column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
|
243
|
+
end
|
244
|
+
|
242
245
|
def aggregate_column(column_name)
|
243
246
|
return column_name if Arel::Expressions === column_name
|
244
247
|
|
@@ -383,7 +386,7 @@ module ActiveRecord
|
|
383
386
|
case operation
|
384
387
|
when "count" then value.to_i
|
385
388
|
when "sum" then type.deserialize(value || 0)
|
386
|
-
when "average" then value.respond_to?(:to_d) ? value.to_d : value
|
389
|
+
when "average" then value && value.respond_to?(:to_d) ? value.to_d : value
|
387
390
|
else type.deserialize(value)
|
388
391
|
end
|
389
392
|
end
|
@@ -17,6 +17,7 @@ module ActiveRecord
|
|
17
17
|
delegate = Class.new(klass) {
|
18
18
|
include ClassSpecificRelation
|
19
19
|
}
|
20
|
+
include_relation_methods(delegate)
|
20
21
|
mangled_name = klass.name.gsub("::".freeze, "_".freeze)
|
21
22
|
const_set mangled_name, delegate
|
22
23
|
private_constant mangled_name
|
@@ -29,6 +30,35 @@ module ActiveRecord
|
|
29
30
|
child_class.initialize_relation_delegate_cache
|
30
31
|
super
|
31
32
|
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
def include_relation_methods(delegate)
|
36
|
+
superclass.include_relation_methods(delegate) unless base_class == self
|
37
|
+
delegate.include generated_relation_methods
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def generated_relation_methods
|
42
|
+
@generated_relation_methods ||= Module.new.tap do |mod|
|
43
|
+
mod_name = "GeneratedRelationMethods"
|
44
|
+
const_set mod_name, mod
|
45
|
+
private_constant mod_name
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def generate_relation_method(method)
|
50
|
+
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
|
51
|
+
generated_relation_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
52
|
+
def #{method}(*args, &block)
|
53
|
+
scoping { klass.#{method}(*args, &block) }
|
54
|
+
end
|
55
|
+
RUBY
|
56
|
+
else
|
57
|
+
generated_relation_methods.send(:define_method, method) do |*args, &block|
|
58
|
+
scoping { klass.public_send(method, *args, &block) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
32
62
|
end
|
33
63
|
|
34
64
|
extend ActiveSupport::Concern
|