activerecord 3.0.0 → 4.0.0
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 +7 -0
- data/CHANGELOG.md +2102 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +35 -44
- data/examples/performance.rb +110 -100
- data/lib/active_record/aggregations.rb +59 -75
- data/lib/active_record/associations/alias_tracker.rb +76 -0
- data/lib/active_record/associations/association.rb +248 -0
- data/lib/active_record/associations/association_scope.rb +135 -0
- data/lib/active_record/associations/belongs_to_association.rb +60 -59
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -59
- data/lib/active_record/associations/builder/association.rb +108 -0
- data/lib/active_record/associations/builder/belongs_to.rb +98 -0
- data/lib/active_record/associations/builder/collection_association.rb +89 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
- data/lib/active_record/associations/builder/has_many.rb +15 -0
- data/lib/active_record/associations/builder/has_one.rb +25 -0
- data/lib/active_record/associations/builder/singular_association.rb +32 -0
- data/lib/active_record/associations/collection_association.rb +608 -0
- data/lib/active_record/associations/collection_proxy.rb +986 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +40 -112
- data/lib/active_record/associations/has_many_association.rb +83 -76
- data/lib/active_record/associations/has_many_through_association.rb +147 -66
- data/lib/active_record/associations/has_one_association.rb +67 -108
- data/lib/active_record/associations/has_one_through_association.rb +21 -25
- data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
- data/lib/active_record/associations/join_dependency.rb +235 -0
- data/lib/active_record/associations/join_helper.rb +45 -0
- data/lib/active_record/associations/preloader/association.rb +121 -0
- data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
- data/lib/active_record/associations/preloader/collection_association.rb +24 -0
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
- data/lib/active_record/associations/preloader/has_many.rb +17 -0
- data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
- data/lib/active_record/associations/preloader/has_one.rb +23 -0
- data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
- data/lib/active_record/associations/preloader/singular_association.rb +21 -0
- data/lib/active_record/associations/preloader/through_association.rb +63 -0
- data/lib/active_record/associations/preloader.rb +178 -0
- data/lib/active_record/associations/singular_association.rb +64 -0
- data/lib/active_record/associations/through_association.rb +87 -0
- data/lib/active_record/associations.rb +512 -1224
- data/lib/active_record/attribute_assignment.rb +201 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +49 -12
- data/lib/active_record/attribute_methods/dirty.rb +51 -28
- data/lib/active_record/attribute_methods/primary_key.rb +94 -22
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +63 -72
- data/lib/active_record/attribute_methods/serialization.rb +162 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -41
- data/lib/active_record/attribute_methods/write.rb +39 -13
- data/lib/active_record/attribute_methods.rb +362 -29
- data/lib/active_record/autosave_association.rb +132 -75
- data/lib/active_record/base.rb +83 -1627
- data/lib/active_record/callbacks.rb +69 -47
- data/lib/active_record/coders/yaml_column.rb +38 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +411 -138
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +21 -11
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -173
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +82 -25
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +176 -414
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +562 -232
- data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +281 -53
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
- data/lib/active_record/connection_adapters/column.rb +318 -0
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +365 -450
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +672 -752
- data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +588 -17
- data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +463 -0
- data/lib/active_record/counter_cache.rb +108 -101
- data/lib/active_record/dynamic_matchers.rb +131 -0
- data/lib/active_record/errors.rb +54 -13
- data/lib/active_record/explain.rb +38 -0
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +29 -0
- data/lib/active_record/fixture_set/file.rb +55 -0
- data/lib/active_record/fixtures.rb +703 -785
- data/lib/active_record/inheritance.rb +200 -0
- data/lib/active_record/integration.rb +60 -0
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +69 -60
- data/lib/active_record/locking/pessimistic.rb +34 -12
- data/lib/active_record/log_subscriber.rb +40 -6
- data/lib/active_record/migration/command_recorder.rb +164 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +614 -216
- data/lib/active_record/model_schema.rb +345 -0
- data/lib/active_record/nested_attributes.rb +248 -119
- data/lib/active_record/null_relation.rb +65 -0
- data/lib/active_record/persistence.rb +275 -57
- data/lib/active_record/query_cache.rb +29 -9
- data/lib/active_record/querying.rb +62 -0
- data/lib/active_record/railtie.rb +135 -21
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +17 -5
- data/lib/active_record/railties/databases.rake +249 -359
- data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
- data/lib/active_record/readonly_attributes.rb +30 -0
- data/lib/active_record/reflection.rb +283 -103
- data/lib/active_record/relation/batches.rb +38 -34
- data/lib/active_record/relation/calculations.rb +252 -139
- data/lib/active_record/relation/delegation.rb +125 -0
- data/lib/active_record/relation/finder_methods.rb +182 -188
- data/lib/active_record/relation/merger.rb +161 -0
- data/lib/active_record/relation/predicate_builder.rb +86 -21
- data/lib/active_record/relation/query_methods.rb +917 -134
- data/lib/active_record/relation/spawn_methods.rb +53 -92
- data/lib/active_record/relation.rb +405 -143
- data/lib/active_record/result.rb +67 -0
- data/lib/active_record/runtime_registry.rb +17 -0
- data/lib/active_record/sanitization.rb +168 -0
- data/lib/active_record/schema.rb +20 -14
- data/lib/active_record/schema_dumper.rb +55 -46
- data/lib/active_record/schema_migration.rb +39 -0
- data/lib/active_record/scoping/default.rb +146 -0
- data/lib/active_record/scoping/named.rb +175 -0
- data/lib/active_record/scoping.rb +82 -0
- data/lib/active_record/serialization.rb +8 -46
- data/lib/active_record/serializers/xml_serializer.rb +21 -68
- data/lib/active_record/statement_cache.rb +26 -0
- data/lib/active_record/store.rb +156 -0
- data/lib/active_record/tasks/database_tasks.rb +203 -0
- data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
- data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
- data/lib/active_record/test_case.rb +57 -28
- data/lib/active_record/timestamp.rb +49 -18
- data/lib/active_record/transactions.rb +106 -63
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations/associated.rb +25 -24
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +123 -83
- data/lib/active_record/validations.rb +29 -29
- data/lib/active_record/version.rb +7 -5
- data/lib/active_record.rb +83 -34
- data/lib/rails/generators/active_record/migration/migration_generator.rb +46 -9
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +30 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -5
- data/lib/rails/generators/active_record/model/templates/model.rb +7 -2
- data/lib/rails/generators/active_record/model/templates/module.rb +3 -1
- data/lib/rails/generators/active_record.rb +4 -8
- metadata +163 -121
- data/CHANGELOG +0 -6023
- data/examples/associations.png +0 -0
- data/lib/active_record/association_preload.rb +0 -403
- data/lib/active_record/associations/association_collection.rb +0 -562
- data/lib/active_record/associations/association_proxy.rb +0 -295
- data/lib/active_record/associations/through_association_scope.rb +0 -154
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -113
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -401
- data/lib/active_record/dynamic_finder_match.rb +0 -53
- data/lib/active_record/dynamic_scope_match.rb +0 -32
- data/lib/active_record/named_scope.rb +0 -138
- data/lib/active_record/observer.rb +0 -140
- data/lib/active_record/session_store.rb +0 -340
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -16
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -2
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -24
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -0,0 +1,65 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Validations
|
3
|
+
class PresenceValidator < ActiveModel::Validations::PresenceValidator # :nodoc:
|
4
|
+
def validate(record)
|
5
|
+
super
|
6
|
+
attributes.each do |attribute|
|
7
|
+
next unless record.class.reflect_on_association(attribute)
|
8
|
+
associated_records = Array(record.send(attribute))
|
9
|
+
|
10
|
+
# Superclass validates presence. Ensure present records aren't about to be destroyed.
|
11
|
+
if associated_records.present? && associated_records.all? { |r| r.marked_for_destruction? }
|
12
|
+
record.errors.add(attribute, :blank, options)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
# Validates that the specified attributes are not blank (as defined by
|
20
|
+
# Object#blank?), and, if the attribute is an association, that the
|
21
|
+
# associated object is not marked for destruction. Happens by default
|
22
|
+
# on save.
|
23
|
+
#
|
24
|
+
# class Person < ActiveRecord::Base
|
25
|
+
# has_one :face
|
26
|
+
# validates_presence_of :face
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# The face attribute must be in the object and it cannot be blank or marked
|
30
|
+
# for destruction.
|
31
|
+
#
|
32
|
+
# If you want to validate the presence of a boolean field (where the real values
|
33
|
+
# are true and false), you will want to use
|
34
|
+
# <tt>validates_inclusion_of :field_name, in: [true, false]</tt>.
|
35
|
+
#
|
36
|
+
# This is due to the way Object#blank? handles boolean values:
|
37
|
+
# <tt>false.blank? # => true</tt>.
|
38
|
+
#
|
39
|
+
# This validator defers to the ActiveModel validation for presence, adding the
|
40
|
+
# check to see that an associated object is not marked for destruction. This
|
41
|
+
# prevents the parent object from validating successfully and saving, which then
|
42
|
+
# deletes the associated object, thus putting the parent object into an invalid
|
43
|
+
# state.
|
44
|
+
#
|
45
|
+
# Configuration options:
|
46
|
+
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
|
47
|
+
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
|
48
|
+
# validation contexts by default (+nil+), other options are <tt>:create</tt>
|
49
|
+
# and <tt>:update</tt>.
|
50
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
|
51
|
+
# the validation should occur (e.g. <tt>if: :allow_validation</tt>, or
|
52
|
+
# <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
|
53
|
+
# or string should return or evaluate to a +true+ or +false+ value.
|
54
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
|
55
|
+
# if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
|
56
|
+
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
|
57
|
+
# proc or string should return or evaluate to a +true+ or +false+ value.
|
58
|
+
# * <tt>:strict</tt> - Specifies whether validation should be strict.
|
59
|
+
# See <tt>ActiveModel::Validation#validates!</tt> for more information.
|
60
|
+
def validates_presence_of(*attr_names)
|
61
|
+
validates_with PresenceValidator, _merge_attributes(attr_names)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -1,10 +1,12 @@
|
|
1
|
-
require 'active_support/core_ext/array/wrap'
|
2
|
-
|
3
1
|
module ActiveRecord
|
4
2
|
module Validations
|
5
|
-
class UniquenessValidator < ActiveModel::EachValidator
|
3
|
+
class UniquenessValidator < ActiveModel::EachValidator # :nodoc:
|
6
4
|
def initialize(options)
|
7
|
-
|
5
|
+
if options[:conditions] && !options[:conditions].respond_to?(:call)
|
6
|
+
raise ArgumentError, "#{options[:conditions]} was passed as :conditions but is not callable. " \
|
7
|
+
"Pass a callable instead: `conditions: -> { where(approved: true) }`"
|
8
|
+
end
|
9
|
+
super({ case_sensitive: true }.merge!(options))
|
8
10
|
end
|
9
11
|
|
10
12
|
# Unfortunately, we have to tie Uniqueness validators to a class.
|
@@ -14,25 +16,20 @@ module ActiveRecord
|
|
14
16
|
|
15
17
|
def validate_each(record, attribute, value)
|
16
18
|
finder_class = find_finder_class_for(record)
|
17
|
-
table = finder_class.
|
18
|
-
|
19
|
-
table_name = record.class.quoted_table_name
|
20
|
-
sql, params = mount_sql_and_params(finder_class, table_name, attribute, value)
|
21
|
-
|
22
|
-
relation = table.where(sql, *params)
|
19
|
+
table = finder_class.arel_table
|
20
|
+
value = deserialize_attribute(record, attribute, value)
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
unless record.new_record?
|
30
|
-
# TODO : This should be in Arel
|
31
|
-
relation = relation.where("#{record.class.quoted_table_name}.#{record.class.primary_key} <> ?", record.send(:id))
|
32
|
-
end
|
22
|
+
relation = build_relation(finder_class, table, attribute, value)
|
23
|
+
relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id)) if record.persisted?
|
24
|
+
relation = scope_relation(record, table, relation)
|
25
|
+
relation = finder_class.unscoped.where(relation)
|
26
|
+
relation = relation.merge(options[:conditions]) if options[:conditions]
|
33
27
|
|
34
28
|
if relation.exists?
|
35
|
-
|
29
|
+
error_options = options.except(:case_sensitive, :scope, :conditions)
|
30
|
+
error_options[:value] = value
|
31
|
+
|
32
|
+
record.errors.add(attribute, :taken, error_options)
|
36
33
|
end
|
37
34
|
end
|
38
35
|
|
@@ -47,70 +44,114 @@ module ActiveRecord
|
|
47
44
|
class_hierarchy = [record.class]
|
48
45
|
|
49
46
|
while class_hierarchy.first != @klass
|
50
|
-
class_hierarchy.
|
47
|
+
class_hierarchy.unshift(class_hierarchy.first.superclass)
|
51
48
|
end
|
52
49
|
|
53
50
|
class_hierarchy.detect { |klass| !klass.abstract_class? }
|
54
51
|
end
|
55
52
|
|
56
|
-
def
|
53
|
+
def build_relation(klass, table, attribute, value) #:nodoc:
|
54
|
+
if reflection = klass.reflect_on_association(attribute)
|
55
|
+
attribute = reflection.foreign_key
|
56
|
+
value = value.attributes[reflection.primary_key_column.name]
|
57
|
+
end
|
58
|
+
|
57
59
|
column = klass.columns_hash[attribute.to_s]
|
60
|
+
value = klass.connection.type_cast(value, column)
|
61
|
+
value = value.to_s[0, column.limit] if value && column.limit && column.text?
|
58
62
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s
|
63
|
-
"#{klass.connection.case_sensitive_equality_operator} ?"
|
63
|
+
if !options[:case_sensitive] && value && column.text?
|
64
|
+
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation
|
65
|
+
klass.connection.case_insensitive_comparison(table, attribute, column, value)
|
64
66
|
else
|
65
|
-
|
67
|
+
value = klass.connection.case_sensitive_modifier(value) unless value.nil?
|
68
|
+
table[attribute].eq(value)
|
66
69
|
end
|
70
|
+
end
|
67
71
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
72
|
+
def scope_relation(record, table, relation)
|
73
|
+
Array(options[:scope]).each do |scope_item|
|
74
|
+
if reflection = record.class.reflect_on_association(scope_item)
|
75
|
+
scope_value = record.send(reflection.foreign_key)
|
76
|
+
scope_item = reflection.foreign_key
|
77
|
+
else
|
78
|
+
scope_value = record.read_attribute(scope_item)
|
79
|
+
end
|
80
|
+
relation = relation.and(table[scope_item].eq(scope_value))
|
74
81
|
end
|
75
82
|
|
76
|
-
|
83
|
+
relation
|
84
|
+
end
|
85
|
+
|
86
|
+
def deserialize_attribute(record, attribute, value)
|
87
|
+
coder = record.class.serialized_attributes[attribute.to_s]
|
88
|
+
value = coder.dump value if value && coder
|
89
|
+
value
|
77
90
|
end
|
78
91
|
end
|
79
92
|
|
80
93
|
module ClassMethods
|
81
|
-
# Validates whether the value of the specified attributes are unique
|
82
|
-
# Useful for making sure that only one user
|
94
|
+
# Validates whether the value of the specified attributes are unique
|
95
|
+
# across the system. Useful for making sure that only one user
|
83
96
|
# can be named "davidhh".
|
84
97
|
#
|
85
98
|
# class Person < ActiveRecord::Base
|
86
|
-
# validates_uniqueness_of :user_name
|
99
|
+
# validates_uniqueness_of :user_name
|
87
100
|
# end
|
88
101
|
#
|
89
|
-
# It can also validate whether the value of the specified attributes are
|
90
|
-
#
|
91
|
-
#
|
102
|
+
# It can also validate whether the value of the specified attributes are
|
103
|
+
# unique based on a <tt>:scope</tt> parameter:
|
104
|
+
#
|
105
|
+
# class Person < ActiveRecord::Base
|
106
|
+
# validates_uniqueness_of :user_name, scope: :account_id
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# Or even multiple scope parameters. For example, making sure that a
|
110
|
+
# teacher can only be on the schedule once per semester for a particular
|
111
|
+
# class.
|
92
112
|
#
|
93
113
|
# class TeacherSchedule < ActiveRecord::Base
|
94
|
-
# validates_uniqueness_of :teacher_id, :
|
114
|
+
# validates_uniqueness_of :teacher_id, scope: [:semester_id, :class_id]
|
95
115
|
# end
|
96
116
|
#
|
97
|
-
#
|
98
|
-
#
|
117
|
+
# It is also possible to limit the uniqueness constraint to a set of
|
118
|
+
# records matching certain conditions. In this example archived articles
|
119
|
+
# are not being taken into consideration when validating uniqueness
|
120
|
+
# of the title attribute:
|
121
|
+
#
|
122
|
+
# class Article < ActiveRecord::Base
|
123
|
+
# validates_uniqueness_of :title, conditions: -> { where.not(status: 'archived') }
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# When the record is created, a check is performed to make sure that no
|
127
|
+
# record exists in the database with the given value for the specified
|
128
|
+
# attribute (that maps to a column). When the record is updated,
|
99
129
|
# the same check is made but disregarding the record itself.
|
100
130
|
#
|
101
131
|
# Configuration options:
|
102
|
-
#
|
103
|
-
# * <tt>:
|
104
|
-
#
|
105
|
-
# * <tt>:
|
106
|
-
#
|
107
|
-
# * <tt>:
|
108
|
-
#
|
109
|
-
#
|
110
|
-
# * <tt>:
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
132
|
+
#
|
133
|
+
# * <tt>:message</tt> - Specifies a custom error message (default is:
|
134
|
+
# "has already been taken").
|
135
|
+
# * <tt>:scope</tt> - One or more columns by which to limit the scope of
|
136
|
+
# the uniqueness constraint.
|
137
|
+
# * <tt>:conditions</tt> - Specify the conditions to be included as a
|
138
|
+
# <tt>WHERE</tt> SQL fragment to limit the uniqueness constraint lookup
|
139
|
+
# (e.g. <tt>conditions: -> { where(status: 'active') }</tt>).
|
140
|
+
# * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by
|
141
|
+
# non-text columns (+true+ by default).
|
142
|
+
# * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the
|
143
|
+
# attribute is +nil+ (default is +false+).
|
144
|
+
# * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the
|
145
|
+
# attribute is blank (default is +false+).
|
146
|
+
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
|
147
|
+
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
|
148
|
+
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
|
149
|
+
# proc or string should return or evaluate to a +true+ or +false+ value.
|
150
|
+
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
|
151
|
+
# determine if the validation should ot occur (e.g. <tt>unless: :skip_validation</tt>,
|
152
|
+
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
153
|
+
# method, proc or string should return or evaluate to a +true+ or +false+
|
154
|
+
# value.
|
114
155
|
#
|
115
156
|
# === Concurrency and integrity
|
116
157
|
#
|
@@ -149,34 +190,33 @@ module ActiveRecord
|
|
149
190
|
# | # title!
|
150
191
|
#
|
151
192
|
# This could even happen if you use transactions with the 'serializable'
|
152
|
-
# isolation level.
|
153
|
-
#
|
154
|
-
#
|
155
|
-
#
|
156
|
-
#
|
157
|
-
#
|
158
|
-
#
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
172
|
-
#
|
173
|
-
#
|
174
|
-
#
|
175
|
-
# Active Record currently provides no way to distinguish unique
|
176
|
-
# index constraint errors from other types of database errors, so you
|
177
|
-
# will have to parse the (database-specific) exception message to detect
|
178
|
-
# such a case.
|
193
|
+
# isolation level. The best way to work around this problem is to add a unique
|
194
|
+
# index to the database table using
|
195
|
+
# ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the
|
196
|
+
# rare case that a race condition occurs, the database will guarantee
|
197
|
+
# the field's uniqueness.
|
198
|
+
#
|
199
|
+
# When the database catches such a duplicate insertion,
|
200
|
+
# ActiveRecord::Base#save will raise an ActiveRecord::StatementInvalid
|
201
|
+
# exception. You can either choose to let this error propagate (which
|
202
|
+
# will result in the default Rails exception page being shown), or you
|
203
|
+
# can catch it and restart the transaction (e.g. by telling the user
|
204
|
+
# that the title already exists, and asking him to re-enter the title).
|
205
|
+
# This technique is also known as optimistic concurrency control:
|
206
|
+
# http://en.wikipedia.org/wiki/Optimistic_concurrency_control.
|
207
|
+
#
|
208
|
+
# The bundled ActiveRecord::ConnectionAdapters distinguish unique index
|
209
|
+
# constraint errors from other types of database errors by throwing an
|
210
|
+
# ActiveRecord::RecordNotUnique exception. For other adapters you will
|
211
|
+
# have to parse the (database-specific) exception message to detect such
|
212
|
+
# a case.
|
213
|
+
#
|
214
|
+
# The following bundled adapters throw the ActiveRecord::RecordNotUnique exception:
|
179
215
|
#
|
216
|
+
# * ActiveRecord::ConnectionAdapters::MysqlAdapter.
|
217
|
+
# * ActiveRecord::ConnectionAdapters::Mysql2Adapter.
|
218
|
+
# * ActiveRecord::ConnectionAdapters::SQLite3Adapter.
|
219
|
+
# * ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.
|
180
220
|
def validates_uniqueness_of(*attr_names)
|
181
221
|
validates_with UniquenessValidator, _merge_attributes(attr_names)
|
182
222
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
# = Active Record
|
2
|
+
# = Active Record RecordInvalid
|
3
3
|
#
|
4
|
-
# Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid.
|
4
|
+
# Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid. Use the
|
5
5
|
# +record+ method to retrieve the record which did not validate.
|
6
6
|
#
|
7
7
|
# begin
|
@@ -10,20 +10,27 @@ module ActiveRecord
|
|
10
10
|
# puts invalid.record.errors
|
11
11
|
# end
|
12
12
|
class RecordInvalid < ActiveRecordError
|
13
|
-
attr_reader :record
|
14
|
-
def initialize(record)
|
13
|
+
attr_reader :record # :nodoc:
|
14
|
+
def initialize(record) # :nodoc:
|
15
15
|
@record = record
|
16
16
|
errors = @record.errors.full_messages.join(", ")
|
17
|
-
super(I18n.t("
|
17
|
+
super(I18n.t(:"#{@record.class.i18n_scope}.errors.messages.record_invalid", :errors => errors, :default => :"errors.messages.record_invalid"))
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
+
# = Active Record Validations
|
22
|
+
#
|
23
|
+
# Active Record includes the majority of its validations from <tt>ActiveModel::Validations</tt>
|
24
|
+
# all of which accept the <tt>:on</tt> argument to define the context where the
|
25
|
+
# validations are active. Active Record will always supply either the context of
|
26
|
+
# <tt>:create</tt> or <tt>:update</tt> dependent on whether the model is a
|
27
|
+
# <tt>new_record?</tt>.
|
21
28
|
module Validations
|
22
29
|
extend ActiveSupport::Concern
|
23
30
|
include ActiveModel::Validations
|
24
31
|
|
25
32
|
module ClassMethods
|
26
|
-
# Creates an object just like Base.create but calls save
|
33
|
+
# Creates an object just like Base.create but calls <tt>save!</tt> instead of +save+
|
27
34
|
# so an exception is raised if the record is invalid.
|
28
35
|
def create!(attributes = nil, &block)
|
29
36
|
if attributes.is_a?(Array)
|
@@ -37,48 +44,41 @@ module ActiveRecord
|
|
37
44
|
end
|
38
45
|
end
|
39
46
|
|
40
|
-
# The validation process on save can be skipped by passing false
|
41
|
-
# replaced with this when the validations
|
47
|
+
# The validation process on save can be skipped by passing <tt>validate: false</tt>.
|
48
|
+
# The regular Base#save method is replaced with this when the validations
|
49
|
+
# module is mixed in, which it is by default.
|
42
50
|
def save(options={})
|
43
51
|
perform_validations(options) ? super : false
|
44
52
|
end
|
45
53
|
|
46
|
-
# Attempts to save the record just like Base#save but will raise a RecordInvalid
|
47
|
-
# if the record is not valid.
|
54
|
+
# Attempts to save the record just like Base#save but will raise a +RecordInvalid+
|
55
|
+
# exception instead of returning +false+ if the record is not valid.
|
48
56
|
def save!(options={})
|
49
57
|
perform_validations(options) ? super : raise(RecordInvalid.new(self))
|
50
58
|
end
|
51
59
|
|
52
|
-
# Runs all the
|
60
|
+
# Runs all the validations within the specified context. Returns +true+ if
|
61
|
+
# no errors are found, +false+ otherwise.
|
62
|
+
#
|
63
|
+
# If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
|
64
|
+
# <tt>new_record?</tt> is +true+, and to <tt>:update</tt> if it is not.
|
65
|
+
#
|
66
|
+
# Validations with no <tt>:on</tt> option will run no matter the context. Validations with
|
67
|
+
# some <tt>:on</tt> option will only run in the specified context.
|
53
68
|
def valid?(context = nil)
|
54
69
|
context ||= (new_record? ? :create : :update)
|
55
70
|
output = super(context)
|
56
|
-
|
57
|
-
deprecated_callback_method(:validate)
|
58
|
-
deprecated_callback_method(:"validate_on_#{context}")
|
59
|
-
|
60
71
|
errors.empty? && output
|
61
72
|
end
|
62
73
|
|
63
74
|
protected
|
64
75
|
|
65
|
-
def perform_validations(options={})
|
66
|
-
|
67
|
-
when Hash
|
68
|
-
options[:validate] != false
|
69
|
-
else
|
70
|
-
ActiveSupport::Deprecation.warn "save(#{options}) is deprecated, please give save(:validate => #{options}) instead", caller
|
71
|
-
options
|
72
|
-
end
|
73
|
-
|
74
|
-
if perform_validation
|
75
|
-
valid?(options.is_a?(Hash) ? options[:context] : nil)
|
76
|
-
else
|
77
|
-
true
|
78
|
-
end
|
76
|
+
def perform_validations(options={}) # :nodoc:
|
77
|
+
options[:validate] == false || valid?(options[:context])
|
79
78
|
end
|
80
79
|
end
|
81
80
|
end
|
82
81
|
|
83
82
|
require "active_record/validations/associated"
|
84
83
|
require "active_record/validations/uniqueness"
|
84
|
+
require "active_record/validations/presence"
|
@@ -1,9 +1,11 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
# Returns the version of the currently loaded ActiveRecord as a Gem::Version
|
3
|
+
def self.version
|
4
|
+
Gem::Version.new "4.0.0"
|
5
|
+
end
|
6
6
|
|
7
|
-
|
7
|
+
module VERSION #:nodoc:
|
8
|
+
MAJOR, MINOR, TINY, PRE = ActiveRecord.version.segments
|
9
|
+
STRING = ActiveRecord.version.to_s
|
8
10
|
end
|
9
11
|
end
|
data/lib/active_record.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2004-
|
2
|
+
# Copyright (c) 2004-2013 David Heinemeier Hansson
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -21,34 +21,62 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
#++
|
23
23
|
|
24
|
-
|
25
|
-
activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
|
26
|
-
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
|
27
|
-
|
28
|
-
activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__)
|
29
|
-
$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path)
|
30
|
-
|
31
24
|
require 'active_support'
|
32
|
-
require 'active_support/
|
25
|
+
require 'active_support/rails'
|
33
26
|
require 'active_model'
|
34
27
|
require 'arel'
|
28
|
+
require 'active_record/deprecated_finders'
|
29
|
+
|
30
|
+
require 'active_record/version'
|
35
31
|
|
36
32
|
module ActiveRecord
|
37
33
|
extend ActiveSupport::Autoload
|
38
34
|
|
39
|
-
|
40
|
-
|
35
|
+
autoload :Base
|
36
|
+
autoload :Callbacks
|
37
|
+
autoload :Core
|
38
|
+
autoload :ConnectionHandling
|
39
|
+
autoload :CounterCache
|
40
|
+
autoload :DynamicMatchers
|
41
|
+
autoload :Explain
|
42
|
+
autoload :Inheritance
|
43
|
+
autoload :Integration
|
44
|
+
autoload :Migration
|
45
|
+
autoload :Migrator, 'active_record/migration'
|
46
|
+
autoload :ModelSchema
|
47
|
+
autoload :NestedAttributes
|
48
|
+
autoload :Persistence
|
49
|
+
autoload :QueryCache
|
50
|
+
autoload :Querying
|
51
|
+
autoload :ReadonlyAttributes
|
52
|
+
autoload :Reflection
|
53
|
+
autoload :RuntimeRegistry
|
54
|
+
autoload :Sanitization
|
55
|
+
autoload :Schema
|
56
|
+
autoload :SchemaDumper
|
57
|
+
autoload :SchemaMigration
|
58
|
+
autoload :Scoping
|
59
|
+
autoload :Serialization
|
60
|
+
autoload :StatementCache
|
61
|
+
autoload :Store
|
62
|
+
autoload :Timestamp
|
63
|
+
autoload :Transactions
|
64
|
+
autoload :Translation
|
65
|
+
autoload :Validations
|
41
66
|
|
67
|
+
eager_autoload do
|
42
68
|
autoload :ActiveRecordError, 'active_record/errors'
|
43
69
|
autoload :ConnectionNotEstablished, 'active_record/errors'
|
70
|
+
autoload :ConnectionAdapters, 'active_record/connection_adapters/abstract_adapter'
|
44
71
|
|
45
72
|
autoload :Aggregations
|
46
|
-
autoload :AssociationPreload
|
47
73
|
autoload :Associations
|
74
|
+
autoload :AttributeAssignment
|
48
75
|
autoload :AttributeMethods
|
49
76
|
autoload :AutosaveAssociation
|
50
77
|
|
51
78
|
autoload :Relation
|
79
|
+
autoload :NullRelation
|
52
80
|
|
53
81
|
autoload_under 'relation' do
|
54
82
|
autoload :QueryMethods
|
@@ -57,28 +85,14 @@ module ActiveRecord
|
|
57
85
|
autoload :PredicateBuilder
|
58
86
|
autoload :SpawnMethods
|
59
87
|
autoload :Batches
|
88
|
+
autoload :Delegation
|
60
89
|
end
|
61
90
|
|
62
|
-
autoload :
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
autoload :
|
67
|
-
autoload :Migration
|
68
|
-
autoload :Migrator, 'active_record/migration'
|
69
|
-
autoload :NamedScope
|
70
|
-
autoload :NestedAttributes
|
71
|
-
autoload :Observer
|
72
|
-
autoload :Persistence
|
73
|
-
autoload :QueryCache
|
74
|
-
autoload :Reflection
|
75
|
-
autoload :Schema
|
76
|
-
autoload :SchemaDumper
|
77
|
-
autoload :Serialization
|
78
|
-
autoload :SessionStore
|
79
|
-
autoload :Timestamp
|
80
|
-
autoload :Transactions
|
81
|
-
autoload :Validations
|
91
|
+
autoload :Result
|
92
|
+
end
|
93
|
+
|
94
|
+
module Coders
|
95
|
+
autoload :YAMLColumn, 'active_record/coders/yaml_column'
|
82
96
|
end
|
83
97
|
|
84
98
|
module AttributeMethods
|
@@ -92,6 +106,7 @@ module ActiveRecord
|
|
92
106
|
autoload :Read
|
93
107
|
autoload :TimeZoneConversion
|
94
108
|
autoload :Write
|
109
|
+
autoload :Serialization
|
95
110
|
end
|
96
111
|
end
|
97
112
|
|
@@ -113,12 +128,46 @@ module ActiveRecord
|
|
113
128
|
end
|
114
129
|
end
|
115
130
|
|
131
|
+
module Scoping
|
132
|
+
extend ActiveSupport::Autoload
|
133
|
+
|
134
|
+
eager_autoload do
|
135
|
+
autoload :Named
|
136
|
+
autoload :Default
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
module Tasks
|
141
|
+
extend ActiveSupport::Autoload
|
142
|
+
|
143
|
+
autoload :DatabaseTasks
|
144
|
+
autoload :SQLiteDatabaseTasks, 'active_record/tasks/sqlite_database_tasks'
|
145
|
+
autoload :MySQLDatabaseTasks, 'active_record/tasks/mysql_database_tasks'
|
146
|
+
autoload :PostgreSQLDatabaseTasks,
|
147
|
+
'active_record/tasks/postgresql_database_tasks'
|
148
|
+
|
149
|
+
autoload :FirebirdDatabaseTasks, 'active_record/tasks/firebird_database_tasks'
|
150
|
+
autoload :SqlserverDatabaseTasks, 'active_record/tasks/sqlserver_database_tasks'
|
151
|
+
autoload :OracleDatabaseTasks, 'active_record/tasks/oracle_database_tasks'
|
152
|
+
end
|
153
|
+
|
116
154
|
autoload :TestCase
|
117
155
|
autoload :TestFixtures, 'active_record/fixtures'
|
156
|
+
|
157
|
+
def self.eager_load!
|
158
|
+
super
|
159
|
+
ActiveRecord::Locking.eager_load!
|
160
|
+
ActiveRecord::Scoping.eager_load!
|
161
|
+
ActiveRecord::Associations.eager_load!
|
162
|
+
ActiveRecord::AttributeMethods.eager_load!
|
163
|
+
ActiveRecord::ConnectionAdapters.eager_load!
|
164
|
+
end
|
118
165
|
end
|
119
166
|
|
120
167
|
ActiveSupport.on_load(:active_record) do
|
121
|
-
Arel::Table.engine =
|
168
|
+
Arel::Table.engine = self
|
122
169
|
end
|
123
170
|
|
124
|
-
|
171
|
+
ActiveSupport.on_load(:i18n) do
|
172
|
+
I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
|
173
|
+
end
|