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.

Files changed (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +35 -44
  5. data/examples/performance.rb +110 -100
  6. data/lib/active_record/aggregations.rb +59 -75
  7. data/lib/active_record/associations/alias_tracker.rb +76 -0
  8. data/lib/active_record/associations/association.rb +248 -0
  9. data/lib/active_record/associations/association_scope.rb +135 -0
  10. data/lib/active_record/associations/belongs_to_association.rb +60 -59
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -59
  12. data/lib/active_record/associations/builder/association.rb +108 -0
  13. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  14. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  16. data/lib/active_record/associations/builder/has_many.rb +15 -0
  17. data/lib/active_record/associations/builder/has_one.rb +25 -0
  18. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  19. data/lib/active_record/associations/collection_association.rb +608 -0
  20. data/lib/active_record/associations/collection_proxy.rb +986 -0
  21. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +40 -112
  22. data/lib/active_record/associations/has_many_association.rb +83 -76
  23. data/lib/active_record/associations/has_many_through_association.rb +147 -66
  24. data/lib/active_record/associations/has_one_association.rb +67 -108
  25. data/lib/active_record/associations/has_one_through_association.rb +21 -25
  26. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  27. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  28. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  29. data/lib/active_record/associations/join_dependency.rb +235 -0
  30. data/lib/active_record/associations/join_helper.rb +45 -0
  31. data/lib/active_record/associations/preloader/association.rb +121 -0
  32. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  33. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  35. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  36. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  37. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  38. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  39. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  40. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  41. data/lib/active_record/associations/preloader.rb +178 -0
  42. data/lib/active_record/associations/singular_association.rb +64 -0
  43. data/lib/active_record/associations/through_association.rb +87 -0
  44. data/lib/active_record/associations.rb +512 -1224
  45. data/lib/active_record/attribute_assignment.rb +201 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +49 -12
  47. data/lib/active_record/attribute_methods/dirty.rb +51 -28
  48. data/lib/active_record/attribute_methods/primary_key.rb +94 -22
  49. data/lib/active_record/attribute_methods/query.rb +5 -4
  50. data/lib/active_record/attribute_methods/read.rb +63 -72
  51. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -41
  53. data/lib/active_record/attribute_methods/write.rb +39 -13
  54. data/lib/active_record/attribute_methods.rb +362 -29
  55. data/lib/active_record/autosave_association.rb +132 -75
  56. data/lib/active_record/base.rb +83 -1627
  57. data/lib/active_record/callbacks.rb +69 -47
  58. data/lib/active_record/coders/yaml_column.rb +38 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +411 -138
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +21 -11
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -173
  62. data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -22
  63. data/lib/active_record/connection_adapters/abstract/quoting.rb +82 -25
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +176 -414
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +562 -232
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +281 -53
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  70. data/lib/active_record/connection_adapters/column.rb +318 -0
  71. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  73. data/lib/active_record/connection_adapters/mysql_adapter.rb +365 -450
  74. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  75. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  81. data/lib/active_record/connection_adapters/postgresql_adapter.rb +672 -752
  82. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  83. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +588 -17
  84. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  85. data/lib/active_record/connection_handling.rb +98 -0
  86. data/lib/active_record/core.rb +463 -0
  87. data/lib/active_record/counter_cache.rb +108 -101
  88. data/lib/active_record/dynamic_matchers.rb +131 -0
  89. data/lib/active_record/errors.rb +54 -13
  90. data/lib/active_record/explain.rb +38 -0
  91. data/lib/active_record/explain_registry.rb +30 -0
  92. data/lib/active_record/explain_subscriber.rb +29 -0
  93. data/lib/active_record/fixture_set/file.rb +55 -0
  94. data/lib/active_record/fixtures.rb +703 -785
  95. data/lib/active_record/inheritance.rb +200 -0
  96. data/lib/active_record/integration.rb +60 -0
  97. data/lib/active_record/locale/en.yml +8 -1
  98. data/lib/active_record/locking/optimistic.rb +69 -60
  99. data/lib/active_record/locking/pessimistic.rb +34 -12
  100. data/lib/active_record/log_subscriber.rb +40 -6
  101. data/lib/active_record/migration/command_recorder.rb +164 -0
  102. data/lib/active_record/migration/join_table.rb +15 -0
  103. data/lib/active_record/migration.rb +614 -216
  104. data/lib/active_record/model_schema.rb +345 -0
  105. data/lib/active_record/nested_attributes.rb +248 -119
  106. data/lib/active_record/null_relation.rb +65 -0
  107. data/lib/active_record/persistence.rb +275 -57
  108. data/lib/active_record/query_cache.rb +29 -9
  109. data/lib/active_record/querying.rb +62 -0
  110. data/lib/active_record/railtie.rb +135 -21
  111. data/lib/active_record/railties/console_sandbox.rb +5 -0
  112. data/lib/active_record/railties/controller_runtime.rb +17 -5
  113. data/lib/active_record/railties/databases.rake +249 -359
  114. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  115. data/lib/active_record/readonly_attributes.rb +30 -0
  116. data/lib/active_record/reflection.rb +283 -103
  117. data/lib/active_record/relation/batches.rb +38 -34
  118. data/lib/active_record/relation/calculations.rb +252 -139
  119. data/lib/active_record/relation/delegation.rb +125 -0
  120. data/lib/active_record/relation/finder_methods.rb +182 -188
  121. data/lib/active_record/relation/merger.rb +161 -0
  122. data/lib/active_record/relation/predicate_builder.rb +86 -21
  123. data/lib/active_record/relation/query_methods.rb +917 -134
  124. data/lib/active_record/relation/spawn_methods.rb +53 -92
  125. data/lib/active_record/relation.rb +405 -143
  126. data/lib/active_record/result.rb +67 -0
  127. data/lib/active_record/runtime_registry.rb +17 -0
  128. data/lib/active_record/sanitization.rb +168 -0
  129. data/lib/active_record/schema.rb +20 -14
  130. data/lib/active_record/schema_dumper.rb +55 -46
  131. data/lib/active_record/schema_migration.rb +39 -0
  132. data/lib/active_record/scoping/default.rb +146 -0
  133. data/lib/active_record/scoping/named.rb +175 -0
  134. data/lib/active_record/scoping.rb +82 -0
  135. data/lib/active_record/serialization.rb +8 -46
  136. data/lib/active_record/serializers/xml_serializer.rb +21 -68
  137. data/lib/active_record/statement_cache.rb +26 -0
  138. data/lib/active_record/store.rb +156 -0
  139. data/lib/active_record/tasks/database_tasks.rb +203 -0
  140. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  141. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  142. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  143. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  144. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  145. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  146. data/lib/active_record/test_case.rb +57 -28
  147. data/lib/active_record/timestamp.rb +49 -18
  148. data/lib/active_record/transactions.rb +106 -63
  149. data/lib/active_record/translation.rb +22 -0
  150. data/lib/active_record/validations/associated.rb +25 -24
  151. data/lib/active_record/validations/presence.rb +65 -0
  152. data/lib/active_record/validations/uniqueness.rb +123 -83
  153. data/lib/active_record/validations.rb +29 -29
  154. data/lib/active_record/version.rb +7 -5
  155. data/lib/active_record.rb +83 -34
  156. data/lib/rails/generators/active_record/migration/migration_generator.rb +46 -9
  157. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  158. data/lib/rails/generators/active_record/migration/templates/migration.rb +30 -8
  159. data/lib/rails/generators/active_record/model/model_generator.rb +15 -5
  160. data/lib/rails/generators/active_record/model/templates/model.rb +7 -2
  161. data/lib/rails/generators/active_record/model/templates/module.rb +3 -1
  162. data/lib/rails/generators/active_record.rb +4 -8
  163. metadata +163 -121
  164. data/CHANGELOG +0 -6023
  165. data/examples/associations.png +0 -0
  166. data/lib/active_record/association_preload.rb +0 -403
  167. data/lib/active_record/associations/association_collection.rb +0 -562
  168. data/lib/active_record/associations/association_proxy.rb +0 -295
  169. data/lib/active_record/associations/through_association_scope.rb +0 -154
  170. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -113
  171. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -401
  172. data/lib/active_record/dynamic_finder_match.rb +0 -53
  173. data/lib/active_record/dynamic_scope_match.rb +0 -32
  174. data/lib/active_record/named_scope.rb +0 -138
  175. data/lib/active_record/observer.rb +0 -140
  176. data/lib/active_record/session_store.rb +0 -340
  177. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -16
  178. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  179. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -2
  180. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -24
  181. 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
- super(options.reverse_merge(:case_sensitive => true))
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.unscoped
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
- Array.wrap(options[:scope]).each do |scope_item|
25
- scope_value = record.send(scope_item)
26
- relation = relation.where(scope_item => scope_value)
27
- end
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
- record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
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.insert(0, class_hierarchy.first.superclass)
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 mount_sql_and_params(klass, table_name, attribute, value) #:nodoc:
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
- operator = if value.nil?
60
- "IS ?"
61
- elsif column.text?
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
- sql_attribute = "#{table_name}.#{klass.connection.quote_column_name(attribute)}"
69
-
70
- if value.nil? || (options[:case_sensitive] || !column.text?)
71
- sql = "#{sql_attribute} #{operator}"
72
- else
73
- sql = "LOWER(#{sql_attribute}) = LOWER(?)"
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
- [sql, [value]]
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 across the system.
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, :scope => :account_id
99
+ # validates_uniqueness_of :user_name
87
100
  # end
88
101
  #
89
- # It can also validate whether the value of the specified attributes are unique based on multiple
90
- # scope parameters. For example, making sure that a teacher can only be on the schedule once
91
- # per semester for a particular class.
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, :scope => [:semester_id, :class_id]
114
+ # validates_uniqueness_of :teacher_id, scope: [:semester_id, :class_id]
95
115
  # end
96
116
  #
97
- # When the record is created, a check is performed to make sure that no record exists in the database
98
- # with the given value for the specified attribute (that maps to a column). When the record is updated,
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
- # * <tt>:message</tt> - Specifies a custom error message (default is: "has already been taken").
103
- # * <tt>:scope</tt> - One or more columns by which to limit the scope of the uniqueness constraint.
104
- # * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (+true+ by default).
105
- # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
106
- # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
107
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
108
- # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).
109
- # The method, proc or string should return or evaluate to a true or false value.
110
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
111
- # not occur (e.g. <tt>:unless => :skip_validation</tt>, or
112
- # <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method, proc or string should
113
- # return or evaluate to a true or false value.
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. There are several ways to get around this problem:
153
- #
154
- # - By locking the database table before validating, and unlocking it after
155
- # saving. However, table locking is very expensive, and thus not
156
- # recommended.
157
- # - By locking a lock file before validating, and unlocking it after saving.
158
- # This does not work if you've scaled your Rails application across
159
- # multiple web servers (because they cannot share lock files, or cannot
160
- # do that efficiently), and thus not recommended.
161
- # - Creating a unique index on the field, by using
162
- # ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the
163
- # rare case that a race condition occurs, the database will guarantee
164
- # the field's uniqueness.
165
- #
166
- # When the database catches such a duplicate insertion,
167
- # ActiveRecord::Base#save will raise an ActiveRecord::StatementInvalid
168
- # exception. You can either choose to let this error propagate (which
169
- # will result in the default Rails exception page being shown), or you
170
- # can catch it and restart the transaction (e.g. by telling the user
171
- # that the title already exists, and asking him to re-enter the title).
172
- # This technique is also known as optimistic concurrency control:
173
- # http://en.wikipedia.org/wiki/Optimistic_concurrency_control
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 Validations
2
+ # = Active Record RecordInvalid
3
3
  #
4
- # Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid. Use the
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("activerecord.errors.messages.record_invalid", :errors => errors))
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! instead of 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. The regular Base#save method is
41
- # replaced with this when the validations module is mixed in, which it is by default.
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 exception instead of returning false
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 specified validations and returns true if no errors were added otherwise false.
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
- perform_validation = case options
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
- module VERSION #:nodoc:
3
- MAJOR = 3
4
- MINOR = 0
5
- TINY = 0
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
- STRING = [MAJOR, MINOR, TINY].join('.')
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-2010 David Heinemeier Hansson
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/i18n'
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
- eager_autoload do
40
- autoload :VERSION
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 :Base
63
- autoload :Callbacks
64
- autoload :CounterCache
65
- autoload :DynamicFinderMatch
66
- autoload :DynamicScopeMatch
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 = Arel::Sql::Engine.new(self)
168
+ Arel::Table.engine = self
122
169
  end
123
170
 
124
- I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
171
+ ActiveSupport.on_load(:i18n) do
172
+ I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
173
+ end