activerecord 4.0.13 → 4.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +745 -2700
- data/README.rdoc +2 -2
- data/examples/performance.rb +30 -18
- data/examples/simple.rb +4 -4
- data/lib/active_record.rb +2 -6
- data/lib/active_record/aggregations.rb +2 -1
- data/lib/active_record/association_relation.rb +0 -4
- data/lib/active_record/associations.rb +87 -43
- data/lib/active_record/associations/alias_tracker.rb +1 -3
- data/lib/active_record/associations/association.rb +8 -16
- data/lib/active_record/associations/association_scope.rb +5 -16
- data/lib/active_record/associations/belongs_to_association.rb +34 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +78 -54
- data/lib/active_record/associations/builder/belongs_to.rb +91 -58
- data/lib/active_record/associations/builder/collection_association.rb +47 -45
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -25
- data/lib/active_record/associations/builder/has_many.rb +2 -2
- data/lib/active_record/associations/builder/has_one.rb +5 -7
- data/lib/active_record/associations/builder/singular_association.rb +6 -7
- data/lib/active_record/associations/collection_association.rb +68 -105
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/has_many_association.rb +11 -9
- data/lib/active_record/associations/has_many_through_association.rb +16 -12
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +204 -165
- data/lib/active_record/associations/join_dependency/join_association.rb +43 -101
- data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
- data/lib/active_record/associations/join_helper.rb +2 -11
- data/lib/active_record/associations/preloader.rb +89 -34
- data/lib/active_record/associations/preloader/association.rb +43 -25
- data/lib/active_record/associations/preloader/collection_association.rb +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +58 -26
- data/lib/active_record/associations/singular_association.rb +6 -5
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +5 -2
- data/lib/active_record/attribute_methods.rb +45 -40
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
- data/lib/active_record/attribute_methods/dirty.rb +8 -22
- data/lib/active_record/attribute_methods/primary_key.rb +1 -7
- data/lib/active_record/attribute_methods/read.rb +55 -28
- data/lib/active_record/attribute_methods/serialization.rb +12 -33
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -13
- data/lib/active_record/attribute_methods/write.rb +37 -12
- data/lib/active_record/autosave_association.rb +207 -207
- data/lib/active_record/base.rb +5 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +2 -7
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +11 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -5
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +84 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +52 -83
- data/lib/active_record/connection_adapters/abstract/transaction.rb +0 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +14 -97
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +58 -60
- data/lib/active_record/connection_adapters/column.rb +1 -35
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +3 -4
- data/lib/active_record/connection_adapters/mysql_adapter.rb +16 -15
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +24 -18
- data/lib/active_record/connection_adapters/postgresql/cast.rb +20 -16
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +23 -43
- data/lib/active_record/connection_adapters/postgresql/oid.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +28 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +8 -30
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +92 -75
- data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +31 -64
- data/lib/active_record/connection_handling.rb +2 -2
- data/lib/active_record/core.rb +22 -43
- data/lib/active_record/counter_cache.rb +7 -7
- data/lib/active_record/enum.rb +100 -0
- data/lib/active_record/errors.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +2 -1
- data/lib/active_record/fixtures.rb +171 -74
- data/lib/active_record/inheritance.rb +16 -22
- data/lib/active_record/integration.rb +52 -1
- data/lib/active_record/locking/optimistic.rb +7 -2
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -12
- data/lib/active_record/migration.rb +62 -46
- data/lib/active_record/migration/command_recorder.rb +7 -13
- data/lib/active_record/model_schema.rb +7 -14
- data/lib/active_record/nested_attributes.rb +10 -8
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +3 -3
- data/lib/active_record/persistence.rb +16 -34
- data/lib/active_record/querying.rb +14 -12
- data/lib/active_record/railtie.rb +0 -50
- data/lib/active_record/railties/databases.rake +12 -15
- data/lib/active_record/readonly_attributes.rb +0 -6
- data/lib/active_record/reflection.rb +189 -75
- data/lib/active_record/relation.rb +69 -94
- data/lib/active_record/relation/batches.rb +57 -23
- data/lib/active_record/relation/calculations.rb +36 -43
- data/lib/active_record/relation/delegation.rb +54 -39
- data/lib/active_record/relation/finder_methods.rb +107 -62
- data/lib/active_record/relation/merger.rb +7 -20
- data/lib/active_record/relation/predicate_builder.rb +57 -38
- data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/query_methods.rb +110 -98
- data/lib/active_record/relation/spawn_methods.rb +1 -2
- data/lib/active_record/result.rb +45 -6
- data/lib/active_record/runtime_registry.rb +5 -0
- data/lib/active_record/sanitization.rb +6 -8
- data/lib/active_record/schema_dumper.rb +16 -5
- data/lib/active_record/schema_migration.rb +24 -25
- data/lib/active_record/scoping/default.rb +5 -18
- data/lib/active_record/scoping/named.rb +8 -29
- data/lib/active_record/store.rb +56 -28
- data/lib/active_record/tasks/database_tasks.rb +8 -4
- data/lib/active_record/timestamp.rb +4 -4
- data/lib/active_record/transactions.rb +8 -10
- data/lib/active_record/validations/presence.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -6
- data/lib/active_record/version.rb +1 -1
- data/lib/rails/generators/active_record.rb +2 -8
- data/lib/rails/generators/active_record/migration.rb +18 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
- metadata +32 -45
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
- data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
- data/lib/active_record/test_case.rb +0 -102
@@ -1,3 +1,5 @@
|
|
1
|
+
# This class is inherited by the has_many and has_many_and_belongs_to_many association classes
|
2
|
+
|
1
3
|
require 'active_record/associations'
|
2
4
|
|
3
5
|
module ActiveRecord::Associations::Builder
|
@@ -6,67 +8,57 @@ module ActiveRecord::Associations::Builder
|
|
6
8
|
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
|
7
9
|
|
8
10
|
def valid_options
|
9
|
-
super + [:table_name, :
|
11
|
+
super + [:table_name, :before_add,
|
10
12
|
:after_add, :before_remove, :after_remove, :extend]
|
11
13
|
end
|
12
14
|
|
13
|
-
attr_reader :block_extension
|
14
|
-
|
15
|
-
def initialize(*args, &extension)
|
16
|
-
super(*args)
|
17
|
-
@block_extension = extension
|
18
|
-
end
|
15
|
+
attr_reader :block_extension
|
19
16
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
def initialize(model, name, scope, options)
|
18
|
+
super
|
19
|
+
@mod = nil
|
20
|
+
if block_given?
|
21
|
+
@mod = Module.new(&Proc.new)
|
22
|
+
@scope = wrap_scope @scope, @mod
|
23
|
+
end
|
26
24
|
end
|
27
25
|
|
28
|
-
def
|
29
|
-
|
26
|
+
def self.define_callbacks(model, reflection)
|
27
|
+
super
|
28
|
+
name = reflection.name
|
29
|
+
options = reflection.options
|
30
|
+
CALLBACKS.each { |callback_name|
|
31
|
+
define_callback(model, callback_name, name, options)
|
32
|
+
}
|
30
33
|
end
|
31
34
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
35
|
+
def define_extensions(model)
|
36
|
+
if @mod
|
37
|
+
extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
|
38
|
+
model.parent.const_set(extension_module_name, @mod)
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
40
|
-
def
|
41
|
-
|
42
|
-
@extension_module = mod = Module.new(&block_extension)
|
43
|
-
silence_warnings do
|
44
|
-
model.parent.const_set(extension_module_name, mod)
|
45
|
-
end
|
46
|
-
|
47
|
-
prev_scope = @scope
|
42
|
+
def self.define_callback(model, callback_name, name, options)
|
43
|
+
full_callback_name = "#{callback_name}_for_#{name}"
|
48
44
|
|
49
|
-
|
50
|
-
|
45
|
+
# TODO : why do i need method_defined? I think its because of the inheritance chain
|
46
|
+
model.class_attribute full_callback_name unless model.method_defined?(full_callback_name)
|
47
|
+
callbacks = Array(options[callback_name.to_sym]).map do |callback|
|
48
|
+
case callback
|
49
|
+
when Symbol
|
50
|
+
->(method, owner, record) { owner.send(callback, record) }
|
51
|
+
when Proc
|
52
|
+
->(method, owner, record) { callback.call(owner, record) }
|
51
53
|
else
|
52
|
-
|
54
|
+
->(method, owner, record) { callback.send(method, owner, record) }
|
53
55
|
end
|
54
56
|
end
|
57
|
+
model.send "#{full_callback_name}=", callbacks
|
55
58
|
end
|
56
59
|
|
57
|
-
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
def define_callback(callback_name)
|
62
|
-
full_callback_name = "#{callback_name}_for_#{name}"
|
63
|
-
|
64
|
-
# TODO : why do i need method_defined? I think its because of the inheritance chain
|
65
|
-
model.class_attribute full_callback_name.to_sym unless model.method_defined?(full_callback_name)
|
66
|
-
model.send("#{full_callback_name}=", Array(options[callback_name.to_sym]))
|
67
|
-
end
|
68
|
-
|
69
|
-
def define_readers
|
60
|
+
# Defines the setter and getter methods for the collection_singular_ids.
|
61
|
+
def self.define_readers(mixin, name)
|
70
62
|
super
|
71
63
|
|
72
64
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
@@ -76,7 +68,7 @@ module ActiveRecord::Associations::Builder
|
|
76
68
|
CODE
|
77
69
|
end
|
78
70
|
|
79
|
-
def define_writers
|
71
|
+
def self.define_writers(mixin, name)
|
80
72
|
super
|
81
73
|
|
82
74
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
@@ -85,5 +77,15 @@ module ActiveRecord::Associations::Builder
|
|
85
77
|
end
|
86
78
|
CODE
|
87
79
|
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def wrap_scope(scope, mod)
|
84
|
+
if scope
|
85
|
+
proc { |owner| instance_exec(owner, &scope).extending(mod) }
|
86
|
+
else
|
87
|
+
proc { extending(mod) }
|
88
|
+
end
|
89
|
+
end
|
88
90
|
end
|
89
91
|
end
|
@@ -1,39 +1,121 @@
|
|
1
1
|
module ActiveRecord::Associations::Builder
|
2
|
-
class HasAndBelongsToMany
|
3
|
-
|
4
|
-
:
|
5
|
-
|
2
|
+
class HasAndBelongsToMany # :nodoc:
|
3
|
+
class JoinTableResolver
|
4
|
+
KnownTable = Struct.new :join_table
|
5
|
+
|
6
|
+
class KnownClass
|
7
|
+
def initialize(lhs_class, rhs_class_name)
|
8
|
+
@lhs_class = lhs_class
|
9
|
+
@rhs_class_name = rhs_class_name
|
10
|
+
@join_table = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def join_table
|
14
|
+
@join_table ||= [@lhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').gsub("\0", "_")
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def klass; @rhs_class_name.constantize; end
|
19
|
+
end
|
6
20
|
|
7
|
-
|
8
|
-
|
21
|
+
def self.build(lhs_class, name, options)
|
22
|
+
if options[:join_table]
|
23
|
+
KnownTable.new options[:join_table].to_s
|
24
|
+
else
|
25
|
+
class_name = options.fetch(:class_name) {
|
26
|
+
name.to_s.camelize.singularize
|
27
|
+
}
|
28
|
+
KnownClass.new lhs_class, class_name
|
29
|
+
end
|
30
|
+
end
|
9
31
|
end
|
10
32
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
33
|
+
attr_reader :lhs_model, :association_name, :options
|
34
|
+
|
35
|
+
def initialize(association_name, lhs_model, options)
|
36
|
+
@association_name = association_name
|
37
|
+
@lhs_model = lhs_model
|
38
|
+
@options = options
|
15
39
|
end
|
16
40
|
|
17
|
-
def
|
18
|
-
|
41
|
+
def through_model
|
42
|
+
habtm = JoinTableResolver.build lhs_model, association_name, options
|
43
|
+
|
44
|
+
join_model = Class.new(ActiveRecord::Base) {
|
45
|
+
class << self;
|
46
|
+
attr_accessor :class_resolver
|
47
|
+
attr_accessor :name
|
48
|
+
attr_accessor :table_name_resolver
|
49
|
+
attr_accessor :left_reflection
|
50
|
+
attr_accessor :right_reflection
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.table_name
|
54
|
+
table_name_resolver.join_table
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.compute_type(class_name)
|
58
|
+
class_resolver.compute_type class_name
|
59
|
+
end
|
19
60
|
|
20
|
-
|
21
|
-
|
22
|
-
|
61
|
+
def self.add_left_association(name, options)
|
62
|
+
belongs_to name, options
|
63
|
+
self.left_reflection = reflect_on_association(name)
|
23
64
|
end
|
65
|
+
|
66
|
+
def self.add_right_association(name, options)
|
67
|
+
rhs_name = name.to_s.singularize.to_sym
|
68
|
+
belongs_to rhs_name, options
|
69
|
+
self.right_reflection = reflect_on_association(rhs_name)
|
70
|
+
end
|
71
|
+
|
72
|
+
}
|
73
|
+
|
74
|
+
join_model.name = "HABTM_#{association_name.to_s.camelize}"
|
75
|
+
join_model.table_name_resolver = habtm
|
76
|
+
join_model.class_resolver = lhs_model
|
77
|
+
|
78
|
+
join_model.add_left_association :left_side, class: lhs_model
|
79
|
+
join_model.add_right_association association_name, belongs_to_options(options)
|
80
|
+
join_model
|
81
|
+
end
|
82
|
+
|
83
|
+
def middle_reflection(join_model)
|
84
|
+
middle_name = [lhs_model.name.downcase.pluralize,
|
85
|
+
association_name].join('_').gsub(/::/, '_').to_sym
|
86
|
+
middle_options = middle_options join_model
|
87
|
+
hm_builder = HasMany.create_builder(lhs_model,
|
88
|
+
middle_name,
|
89
|
+
nil,
|
90
|
+
middle_options)
|
91
|
+
hm_builder.build lhs_model
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def middle_options(join_model)
|
97
|
+
middle_options = {}
|
98
|
+
middle_options[:class] = join_model
|
99
|
+
middle_options[:source] = join_model.left_reflection.name
|
100
|
+
if options.key? :foreign_key
|
101
|
+
middle_options[:foreign_key] = options[:foreign_key]
|
24
102
|
end
|
103
|
+
middle_options
|
25
104
|
end
|
26
105
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
106
|
+
def belongs_to_options(options)
|
107
|
+
rhs_options = {}
|
108
|
+
|
109
|
+
if options.key? :class_name
|
110
|
+
rhs_options[:foreign_key] = options[:class_name].foreign_key
|
111
|
+
rhs_options[:class_name] = options[:class_name]
|
112
|
+
end
|
113
|
+
|
114
|
+
if options.key? :association_foreign_key
|
115
|
+
rhs_options[:foreign_key] = options[:association_foreign_key]
|
116
|
+
end
|
117
|
+
|
118
|
+
rhs_options
|
37
119
|
end
|
38
120
|
end
|
39
121
|
end
|
@@ -8,8 +8,8 @@ module ActiveRecord::Associations::Builder
|
|
8
8
|
super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache]
|
9
9
|
end
|
10
10
|
|
11
|
-
def valid_dependent_options
|
12
|
-
[:destroy, :delete_all, :nullify, :
|
11
|
+
def self.valid_dependent_options
|
12
|
+
[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -10,16 +10,14 @@ module ActiveRecord::Associations::Builder
|
|
10
10
|
valid
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
14
|
-
|
13
|
+
def self.valid_dependent_options
|
14
|
+
[:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
super unless options[:through]
|
19
|
-
end
|
17
|
+
private
|
20
18
|
|
21
|
-
def
|
22
|
-
|
19
|
+
def self.add_before_destroy_callbacks(model, reflection)
|
20
|
+
super unless reflection.options[:through]
|
23
21
|
end
|
24
22
|
end
|
25
23
|
end
|
@@ -1,19 +1,18 @@
|
|
1
|
+
# This class is inherited by the has_one and belongs_to association classes
|
2
|
+
|
1
3
|
module ActiveRecord::Associations::Builder
|
2
4
|
class SingularAssociation < Association #:nodoc:
|
3
5
|
def valid_options
|
4
6
|
super + [:remote, :dependent, :primary_key, :inverse_of]
|
5
7
|
end
|
6
8
|
|
7
|
-
def
|
8
|
-
true
|
9
|
-
end
|
10
|
-
|
11
|
-
def define_accessors
|
9
|
+
def self.define_accessors(model, reflection)
|
12
10
|
super
|
13
|
-
define_constructors if constructable?
|
11
|
+
define_constructors(model.generated_association_methods, reflection.name) if reflection.constructable?
|
14
12
|
end
|
15
13
|
|
16
|
-
|
14
|
+
# Defines the (build|create)_association methods for belongs_to or has_one association
|
15
|
+
def self.define_constructors(mixin, name)
|
17
16
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
18
17
|
def build_#{name}(*args, &block)
|
19
18
|
association(:#{name}).build(*args, &block)
|
@@ -7,7 +7,6 @@ module ActiveRecord
|
|
7
7
|
# collections. See the class hierarchy in AssociationProxy.
|
8
8
|
#
|
9
9
|
# CollectionAssociation:
|
10
|
-
# HasAndBelongsToManyAssociation => has_and_belongs_to_many
|
11
10
|
# HasManyAssociation => has_many
|
12
11
|
# HasManyThroughAssociation + ThroughAssociation => has_many :through
|
13
12
|
#
|
@@ -34,13 +33,7 @@ module ActiveRecord
|
|
34
33
|
reload
|
35
34
|
end
|
36
35
|
|
37
|
-
|
38
|
-
# Cache the proxy separately before the owner has an id
|
39
|
-
# or else a post-save proxy will still lack the id
|
40
|
-
@new_record_proxy ||= CollectionProxy.new(klass, self)
|
41
|
-
else
|
42
|
-
@proxy ||= CollectionProxy.new(klass, self)
|
43
|
-
end
|
36
|
+
@proxy ||= CollectionProxy.create(klass, self)
|
44
37
|
end
|
45
38
|
|
46
39
|
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
@@ -50,7 +43,7 @@ module ActiveRecord
|
|
50
43
|
|
51
44
|
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
52
45
|
def ids_reader
|
53
|
-
if loaded?
|
46
|
+
if loaded?
|
54
47
|
load_target.map do |record|
|
55
48
|
record.send(reflection.association_primary_key)
|
56
49
|
end
|
@@ -85,12 +78,9 @@ module ActiveRecord
|
|
85
78
|
if block_given?
|
86
79
|
load_target.find(*args) { |*block_args| yield(*block_args) }
|
87
80
|
else
|
88
|
-
if options[:
|
89
|
-
find_by_scan(*args)
|
90
|
-
elsif options[:inverse_of] && loaded?
|
81
|
+
if options[:inverse_of] && loaded?
|
91
82
|
args_flatten = args.flatten
|
92
83
|
raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
|
93
|
-
|
94
84
|
result = find_by_scan(*args)
|
95
85
|
|
96
86
|
result_size = Array(result).size
|
@@ -124,11 +114,11 @@ module ActiveRecord
|
|
124
114
|
end
|
125
115
|
|
126
116
|
def create(attributes = {}, &block)
|
127
|
-
|
117
|
+
create_record(attributes, &block)
|
128
118
|
end
|
129
119
|
|
130
120
|
def create!(attributes = {}, &block)
|
131
|
-
|
121
|
+
create_record(attributes, true, &block)
|
132
122
|
end
|
133
123
|
|
134
124
|
# Add +records+ to this association. Returns +self+ so method calls may
|
@@ -159,11 +149,33 @@ module ActiveRecord
|
|
159
149
|
end
|
160
150
|
end
|
161
151
|
|
162
|
-
#
|
152
|
+
# Removes all records from the association without calling callbacks
|
153
|
+
# on the associated records. It honors the `:dependent` option. However
|
154
|
+
# if the `:dependent` value is `:destroy` then in that case the `:delete_all`
|
155
|
+
# deletion strategy for the association is applied.
|
156
|
+
#
|
157
|
+
# You can force a particular deletion strategy by passing a parameter.
|
158
|
+
#
|
159
|
+
# Example:
|
160
|
+
#
|
161
|
+
# @author.books.delete_all(:nullify)
|
162
|
+
# @author.books.delete_all(:delete_all)
|
163
163
|
#
|
164
164
|
# See delete for more info.
|
165
|
-
def delete_all
|
166
|
-
|
165
|
+
def delete_all(dependent = nil)
|
166
|
+
if dependent.present? && ![:nullify, :delete_all].include?(dependent)
|
167
|
+
raise ArgumentError, "Valid values are :nullify or :delete_all"
|
168
|
+
end
|
169
|
+
|
170
|
+
dependent = if dependent.present?
|
171
|
+
dependent
|
172
|
+
elsif options[:dependent] == :destroy
|
173
|
+
:delete_all
|
174
|
+
else
|
175
|
+
options[:dependent]
|
176
|
+
end
|
177
|
+
|
178
|
+
delete(:all, dependent: dependent).tap do
|
167
179
|
reset
|
168
180
|
loaded!
|
169
181
|
end
|
@@ -179,36 +191,29 @@ module ActiveRecord
|
|
179
191
|
end
|
180
192
|
end
|
181
193
|
|
182
|
-
# Count all records using SQL.
|
183
|
-
# association, it will be used for the query. Otherwise, construct options and pass them with
|
194
|
+
# Count all records using SQL. Construct options and pass them with
|
184
195
|
# scope to the target class's +count+.
|
185
196
|
def count(column_name = nil, count_options = {})
|
197
|
+
# TODO: Remove count_options argument as soon we remove support to
|
198
|
+
# activerecord-deprecated_finders.
|
186
199
|
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
|
187
200
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
else
|
195
|
-
relation = scope
|
196
|
-
if association_scope.distinct_value
|
197
|
-
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
198
|
-
column_name ||= reflection.klass.primary_key
|
199
|
-
relation = relation.distinct
|
200
|
-
end
|
201
|
+
relation = scope
|
202
|
+
if association_scope.distinct_value
|
203
|
+
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
204
|
+
column_name ||= reflection.klass.primary_key
|
205
|
+
relation = relation.distinct
|
206
|
+
end
|
201
207
|
|
202
|
-
|
208
|
+
value = relation.count(column_name)
|
203
209
|
|
204
|
-
|
205
|
-
|
210
|
+
limit = options[:limit]
|
211
|
+
offset = options[:offset]
|
206
212
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
end
|
213
|
+
if limit || offset
|
214
|
+
[ [value - offset.to_i, 0].max, limit.to_i ].min
|
215
|
+
else
|
216
|
+
value
|
212
217
|
end
|
213
218
|
end
|
214
219
|
|
@@ -220,19 +225,11 @@ module ActiveRecord
|
|
220
225
|
# are actually removed from the database, that depends precisely on
|
221
226
|
# +delete_records+. They are in any case removed from the collection.
|
222
227
|
def delete(*records)
|
223
|
-
|
228
|
+
_options = records.extract_options!
|
229
|
+
dependent = _options[:dependent] || options[:dependent]
|
224
230
|
|
225
231
|
if records.first == :all
|
226
|
-
|
227
|
-
if dependent && dependent == :destroy
|
228
|
-
message = 'In Rails 4.1 delete_all on associations would not fire callbacks. ' \
|
229
|
-
'It means if the :dependent option is :destroy then the associated ' \
|
230
|
-
'records would be deleted without loading and invoking callbacks.'
|
231
|
-
|
232
|
-
ActiveRecord::Base.logger ? ActiveRecord::Base.logger.warn(message) : $stderr.puts(message)
|
233
|
-
end
|
234
|
-
|
235
|
-
if (loaded? || dependent == :destroy) && dependent != :delete_all
|
232
|
+
if loaded? || dependent == :destroy
|
236
233
|
delete_or_destroy(load_target, dependent)
|
237
234
|
else
|
238
235
|
delete_records(:all, dependent)
|
@@ -243,11 +240,11 @@ module ActiveRecord
|
|
243
240
|
end
|
244
241
|
end
|
245
242
|
|
246
|
-
#
|
247
|
-
# +before_remove+
|
243
|
+
# Deletes the +records+ and removes them from this association calling
|
244
|
+
# +before_remove+ , +after_remove+ , +before_destroy+ and +after_destroy+ callbacks.
|
248
245
|
#
|
249
|
-
# Note that this method
|
250
|
-
#
|
246
|
+
# Note that this method removes records from the database ignoring the
|
247
|
+
# +:dependent+ option.
|
251
248
|
def destroy(*records)
|
252
249
|
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
253
250
|
delete_or_destroy(records, :destroy)
|
@@ -291,14 +288,14 @@ module ActiveRecord
|
|
291
288
|
|
292
289
|
# Returns true if the collection is empty.
|
293
290
|
#
|
294
|
-
# If the collection has been loaded
|
295
|
-
#
|
291
|
+
# If the collection has been loaded
|
292
|
+
# it is equivalent to <tt>collection.size.zero?</tt>. If the
|
296
293
|
# collection has not been loaded, it is equivalent to
|
297
294
|
# <tt>collection.exists?</tt>. If the collection has not already been
|
298
295
|
# loaded and you are going to fetch the records anyway it is better to
|
299
296
|
# check <tt>collection.length.zero?</tt>.
|
300
297
|
def empty?
|
301
|
-
if loaded?
|
298
|
+
if loaded?
|
302
299
|
size.zero?
|
303
300
|
else
|
304
301
|
@target.blank? && !scope.exists?
|
@@ -351,7 +348,6 @@ module ActiveRecord
|
|
351
348
|
if record.new_record?
|
352
349
|
include_in_memory?(record)
|
353
350
|
else
|
354
|
-
load_target if options[:finder_sql]
|
355
351
|
loaded? ? target.include?(record) : scope.exists?(record)
|
356
352
|
end
|
357
353
|
else
|
@@ -368,8 +364,8 @@ module ActiveRecord
|
|
368
364
|
target
|
369
365
|
end
|
370
366
|
|
371
|
-
def add_to_target(record)
|
372
|
-
callback(:before_add, record)
|
367
|
+
def add_to_target(record, skip_callbacks = false)
|
368
|
+
callback(:before_add, record) unless skip_callbacks
|
373
369
|
yield(record) if block_given?
|
374
370
|
|
375
371
|
if association_scope.distinct_value && index = @target.index(record)
|
@@ -378,7 +374,7 @@ module ActiveRecord
|
|
378
374
|
@target << record
|
379
375
|
end
|
380
376
|
|
381
|
-
callback(:after_add, record)
|
377
|
+
callback(:after_add, record) unless skip_callbacks
|
382
378
|
set_inverse_instance(record)
|
383
379
|
|
384
380
|
record
|
@@ -396,31 +392,8 @@ module ActiveRecord
|
|
396
392
|
|
397
393
|
private
|
398
394
|
|
399
|
-
def custom_counter_sql
|
400
|
-
if options[:counter_sql]
|
401
|
-
interpolate(options[:counter_sql])
|
402
|
-
else
|
403
|
-
# replace the SELECT clause with COUNT(SELECTS), preserving any hints within /* ... */
|
404
|
-
interpolate(options[:finder_sql]).sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) do
|
405
|
-
count_with = $2.to_s
|
406
|
-
count_with = '*' if count_with.blank? || count_with =~ /,/ || count_with =~ /\.\*/
|
407
|
-
"SELECT #{$1}COUNT(#{count_with}) FROM"
|
408
|
-
end
|
409
|
-
end
|
410
|
-
end
|
411
|
-
|
412
|
-
def custom_finder_sql
|
413
|
-
interpolate(options[:finder_sql])
|
414
|
-
end
|
415
|
-
|
416
395
|
def find_target
|
417
|
-
records =
|
418
|
-
if options[:finder_sql]
|
419
|
-
reflection.klass.find_by_sql(custom_finder_sql)
|
420
|
-
else
|
421
|
-
scope.to_a
|
422
|
-
end
|
423
|
-
|
396
|
+
records = scope.to_a
|
424
397
|
records.each { |record| set_inverse_instance(record) }
|
425
398
|
records
|
426
399
|
end
|
@@ -455,13 +428,13 @@ module ActiveRecord
|
|
455
428
|
persisted + memory
|
456
429
|
end
|
457
430
|
|
458
|
-
def
|
431
|
+
def create_record(attributes, raise = false, &block)
|
459
432
|
unless owner.persisted?
|
460
433
|
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
|
461
434
|
end
|
462
435
|
|
463
436
|
if attributes.is_a?(Array)
|
464
|
-
attributes.collect { |attr|
|
437
|
+
attributes.collect { |attr| create_record(attr, raise, &block) }
|
465
438
|
else
|
466
439
|
transaction do
|
467
440
|
add_to_target(build_record(attributes)) do |record|
|
@@ -535,20 +508,13 @@ module ActiveRecord
|
|
535
508
|
|
536
509
|
def callback(method, record)
|
537
510
|
callbacks_for(method).each do |callback|
|
538
|
-
|
539
|
-
when Symbol
|
540
|
-
owner.send(callback, record)
|
541
|
-
when Proc
|
542
|
-
callback.call(owner, record)
|
543
|
-
else
|
544
|
-
callback.send(method, owner, record)
|
545
|
-
end
|
511
|
+
callback.call(method, owner, record)
|
546
512
|
end
|
547
513
|
end
|
548
514
|
|
549
515
|
def callbacks_for(callback_name)
|
550
516
|
full_callback_name = "#{callback_name}_for_#{reflection.name}"
|
551
|
-
owner.class.send(full_callback_name
|
517
|
+
owner.class.send(full_callback_name)
|
552
518
|
end
|
553
519
|
|
554
520
|
# Should we deal with assoc.first or assoc.last by issuing an independent query to
|
@@ -559,24 +525,21 @@ module ActiveRecord
|
|
559
525
|
# Otherwise, go to the database only if none of the following are true:
|
560
526
|
# * target already loaded
|
561
527
|
# * owner is new record
|
562
|
-
# * custom :finder_sql exists
|
563
528
|
# * target contains new or changed record(s)
|
564
|
-
# * the first arg is an integer (which indicates the number of records to be returned)
|
565
529
|
def fetch_first_or_last_using_find?(args)
|
566
530
|
if args.first.is_a?(Hash)
|
567
531
|
true
|
568
532
|
else
|
569
533
|
!(loaded? ||
|
570
534
|
owner.new_record? ||
|
571
|
-
|
572
|
-
target.any? { |record| record.new_record? || record.changed? } ||
|
573
|
-
args.first.kind_of?(Integer))
|
535
|
+
target.any? { |record| record.new_record? || record.changed? })
|
574
536
|
end
|
575
537
|
end
|
576
538
|
|
577
539
|
def include_in_memory?(record)
|
578
540
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
579
|
-
owner.
|
541
|
+
assoc = owner.association(reflection.through_reflection.name)
|
542
|
+
assoc.reader.any? { |source|
|
580
543
|
target = source.send(reflection.source_reflection.name)
|
581
544
|
target.respond_to?(:include?) ? target.include?(record) : target == record
|
582
545
|
} || target.include?(record)
|
@@ -585,7 +548,7 @@ module ActiveRecord
|
|
585
548
|
end
|
586
549
|
end
|
587
550
|
|
588
|
-
# If
|
551
|
+
# If the :inverse_of option has been
|
589
552
|
# specified, then #find scans the entire collection.
|
590
553
|
def find_by_scan(*args)
|
591
554
|
expects_array = args.first.kind_of?(Array)
|