activerecord 3.2.22.5 → 4.2.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (236) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1632 -609
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +37 -41
  5. data/examples/performance.rb +31 -19
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +56 -42
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -36
  10. data/lib/active_record/associations/association.rb +73 -55
  11. data/lib/active_record/associations/association_scope.rb +143 -82
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +125 -31
  15. data/lib/active_record/associations/builder/belongs_to.rb +89 -61
  16. data/lib/active_record/associations/builder/collection_association.rb +69 -49
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
  18. data/lib/active_record/associations/builder/has_many.rb +8 -64
  19. data/lib/active_record/associations/builder/has_one.rb +12 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +23 -17
  21. data/lib/active_record/associations/collection_association.rb +251 -177
  22. data/lib/active_record/associations/collection_proxy.rb +963 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +113 -22
  25. data/lib/active_record/associations/has_many_through_association.rb +99 -39
  26. data/lib/active_record/associations/has_one_association.rb +43 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +76 -107
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +230 -156
  32. data/lib/active_record/associations/preloader/association.rb +96 -55
  33. data/lib/active_record/associations/preloader/collection_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +62 -33
  38. data/lib/active_record/associations/preloader.rb +101 -79
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +30 -16
  41. data/lib/active_record/associations.rb +463 -345
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +142 -151
  44. data/lib/active_record/attribute_decorators.rb +66 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  46. data/lib/active_record/attribute_methods/dirty.rb +137 -57
  47. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +73 -106
  50. data/lib/active_record/attribute_methods/serialization.rb +44 -94
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -45
  52. data/lib/active_record/attribute_methods/write.rb +57 -44
  53. data/lib/active_record/attribute_methods.rb +301 -141
  54. data/lib/active_record/attribute_set/builder.rb +106 -0
  55. data/lib/active_record/attribute_set.rb +81 -0
  56. data/lib/active_record/attributes.rb +147 -0
  57. data/lib/active_record/autosave_association.rb +246 -217
  58. data/lib/active_record/base.rb +70 -474
  59. data/lib/active_record/callbacks.rb +66 -28
  60. data/lib/active_record/coders/json.rb +13 -0
  61. data/lib/active_record/coders/yaml_column.rb +18 -21
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +396 -219
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -164
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +29 -24
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -55
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +261 -169
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +707 -259
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +298 -89
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +466 -196
  75. data/lib/active_record/connection_adapters/column.rb +31 -245
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +45 -57
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +180 -123
  79. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  80. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  81. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
  112. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  113. data/lib/active_record/connection_adapters/postgresql_adapter.rb +430 -999
  114. data/lib/active_record/connection_adapters/schema_cache.rb +52 -27
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +579 -22
  116. data/lib/active_record/connection_handling.rb +132 -0
  117. data/lib/active_record/core.rb +579 -0
  118. data/lib/active_record/counter_cache.rb +157 -105
  119. data/lib/active_record/dynamic_matchers.rb +119 -63
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +94 -36
  122. data/lib/active_record/explain.rb +15 -63
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +9 -5
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +302 -215
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +143 -70
  129. data/lib/active_record/integration.rb +65 -12
  130. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  131. data/lib/active_record/locale/en.yml +8 -1
  132. data/lib/active_record/locking/optimistic.rb +73 -52
  133. data/lib/active_record/locking/pessimistic.rb +5 -5
  134. data/lib/active_record/log_subscriber.rb +24 -21
  135. data/lib/active_record/migration/command_recorder.rb +124 -32
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +511 -213
  138. data/lib/active_record/model_schema.rb +91 -117
  139. data/lib/active_record/nested_attributes.rb +184 -130
  140. data/lib/active_record/no_touching.rb +52 -0
  141. data/lib/active_record/null_relation.rb +81 -0
  142. data/lib/active_record/persistence.rb +276 -117
  143. data/lib/active_record/query_cache.rb +19 -37
  144. data/lib/active_record/querying.rb +28 -18
  145. data/lib/active_record/railtie.rb +73 -40
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +4 -3
  148. data/lib/active_record/railties/databases.rake +141 -416
  149. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  150. data/lib/active_record/readonly_attributes.rb +1 -4
  151. data/lib/active_record/reflection.rb +513 -154
  152. data/lib/active_record/relation/batches.rb +91 -43
  153. data/lib/active_record/relation/calculations.rb +199 -161
  154. data/lib/active_record/relation/delegation.rb +116 -25
  155. data/lib/active_record/relation/finder_methods.rb +362 -248
  156. data/lib/active_record/relation/merger.rb +193 -0
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  158. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  159. data/lib/active_record/relation/predicate_builder.rb +135 -43
  160. data/lib/active_record/relation/query_methods.rb +928 -167
  161. data/lib/active_record/relation/spawn_methods.rb +48 -149
  162. data/lib/active_record/relation.rb +352 -207
  163. data/lib/active_record/result.rb +101 -10
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +56 -59
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +106 -63
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +50 -57
  170. data/lib/active_record/scoping/named.rb +73 -109
  171. data/lib/active_record/scoping.rb +58 -123
  172. data/lib/active_record/serialization.rb +6 -2
  173. data/lib/active_record/serializers/xml_serializer.rb +12 -22
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +168 -15
  176. data/lib/active_record/tasks/database_tasks.rb +299 -0
  177. data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
  178. data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
  179. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  180. data/lib/active_record/timestamp.rb +23 -16
  181. data/lib/active_record/transactions.rb +125 -79
  182. data/lib/active_record/type/big_integer.rb +13 -0
  183. data/lib/active_record/type/binary.rb +50 -0
  184. data/lib/active_record/type/boolean.rb +31 -0
  185. data/lib/active_record/type/date.rb +50 -0
  186. data/lib/active_record/type/date_time.rb +54 -0
  187. data/lib/active_record/type/decimal.rb +64 -0
  188. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  189. data/lib/active_record/type/decorator.rb +14 -0
  190. data/lib/active_record/type/float.rb +19 -0
  191. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  192. data/lib/active_record/type/integer.rb +59 -0
  193. data/lib/active_record/type/mutable.rb +16 -0
  194. data/lib/active_record/type/numeric.rb +36 -0
  195. data/lib/active_record/type/serialized.rb +62 -0
  196. data/lib/active_record/type/string.rb +40 -0
  197. data/lib/active_record/type/text.rb +11 -0
  198. data/lib/active_record/type/time.rb +26 -0
  199. data/lib/active_record/type/time_value.rb +38 -0
  200. data/lib/active_record/type/type_map.rb +64 -0
  201. data/lib/active_record/type/unsigned_integer.rb +15 -0
  202. data/lib/active_record/type/value.rb +110 -0
  203. data/lib/active_record/type.rb +23 -0
  204. data/lib/active_record/validations/associated.rb +24 -16
  205. data/lib/active_record/validations/presence.rb +67 -0
  206. data/lib/active_record/validations/uniqueness.rb +123 -64
  207. data/lib/active_record/validations.rb +36 -29
  208. data/lib/active_record/version.rb +5 -7
  209. data/lib/active_record.rb +66 -46
  210. data/lib/rails/generators/active_record/migration/migration_generator.rb +53 -8
  211. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +5 -1
  212. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  213. data/lib/rails/generators/active_record/migration.rb +11 -8
  214. data/lib/rails/generators/active_record/model/model_generator.rb +9 -4
  215. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  216. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  217. data/lib/rails/generators/active_record.rb +3 -11
  218. metadata +101 -45
  219. data/examples/associations.png +0 -0
  220. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  221. data/lib/active_record/associations/join_helper.rb +0 -55
  222. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  223. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  226. data/lib/active_record/dynamic_finder_match.rb +0 -68
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/fixtures/file.rb +0 -65
  229. data/lib/active_record/identity_map.rb +0 -162
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -360
  232. data/lib/active_record/test_case.rb +0 -73
  233. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  234. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  235. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  236. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,42 +1,45 @@
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))
8
- end
9
-
10
- # Unfortunately, we have to tie Uniqueness validators to a class.
11
- def setup(klass)
12
- @klass = klass
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))
10
+ @klass = options[:class]
13
11
  end
14
12
 
15
13
  def validate_each(record, attribute, value)
16
14
  finder_class = find_finder_class_for(record)
17
15
  table = finder_class.arel_table
18
-
19
- coder = record.class.serialized_attributes[attribute.to_s]
20
-
21
- if value && coder
22
- value = coder.dump value
16
+ value = map_enum_attribute(finder_class, attribute, value)
17
+
18
+ begin
19
+ relation = build_relation(finder_class, table, attribute, value)
20
+ if record.persisted?
21
+ if finder_class.primary_key
22
+ relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id))
23
+ else
24
+ raise UnknownPrimaryKey.new(finder_class, "Can not validate uniqueness for persisted record without primary key.")
25
+ end
26
+ end
27
+ relation = scope_relation(record, table, relation)
28
+ relation = finder_class.unscoped.where(relation)
29
+ relation = relation.merge(options[:conditions]) if options[:conditions]
30
+ rescue RangeError
31
+ relation = finder_class.none
23
32
  end
24
33
 
25
- relation = build_relation(finder_class, table, attribute, value)
26
- relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted?
27
-
28
- Array.wrap(options[:scope]).each do |scope_item|
29
- scope_value = record.read_attribute(scope_item)
30
- relation = relation.and(table[scope_item].eq(scope_value))
31
- end
34
+ if relation.exists?
35
+ error_options = options.except(:case_sensitive, :scope, :conditions)
36
+ error_options[:value] = value
32
37
 
33
- if finder_class.unscoped.where(relation).exists?
34
- record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
38
+ record.errors.add(attribute, :taken, error_options)
35
39
  end
36
40
  end
37
41
 
38
42
  protected
39
-
40
43
  # The check for an existing value should be run from a class that
41
44
  # isn't abstract. This means working down from the current class
42
45
  # (self), to the first non-abstract class. Since classes don't know
@@ -46,67 +49,123 @@ module ActiveRecord
46
49
  class_hierarchy = [record.class]
47
50
 
48
51
  while class_hierarchy.first != @klass
49
- class_hierarchy.insert(0, class_hierarchy.first.superclass)
52
+ class_hierarchy.unshift(class_hierarchy.first.superclass)
50
53
  end
51
54
 
52
55
  class_hierarchy.detect { |klass| !klass.abstract_class? }
53
56
  end
54
57
 
55
58
  def build_relation(klass, table, attribute, value) #:nodoc:
56
- column = klass.columns_hash[attribute.to_s]
57
- value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if value && column.text?
59
+ if reflection = klass._reflect_on_association(attribute)
60
+ attribute = reflection.foreign_key
61
+ value = value.attributes[reflection.klass.primary_key] unless value.nil?
62
+ end
63
+
64
+ attribute_name = attribute.to_s
65
+
66
+ # the attribute may be an aliased attribute
67
+ if klass.attribute_aliases[attribute_name]
68
+ attribute = klass.attribute_aliases[attribute_name]
69
+ attribute_name = attribute.to_s
70
+ end
71
+
72
+ column = klass.columns_hash[attribute_name]
73
+ value = klass.connection.type_cast(value, column)
74
+ if value.is_a?(String) && column.limit
75
+ value = value.to_s[0, column.limit]
76
+ end
58
77
 
59
78
  if !options[:case_sensitive] && value && column.text?
60
79
  # will use SQL LOWER function before comparison, unless it detects a case insensitive collation
61
- relation = klass.connection.case_insensitive_comparison(table, attribute, column, value)
80
+ klass.connection.case_insensitive_comparison(table, attribute, column, value)
62
81
  else
63
- value = klass.connection.case_sensitive_modifier(value) if value
64
- relation = table[attribute].eq(value)
82
+ klass.connection.case_sensitive_comparison(table, attribute, column, value)
83
+ end
84
+ end
85
+
86
+ def scope_relation(record, table, relation)
87
+ Array(options[:scope]).each do |scope_item|
88
+ if reflection = record.class._reflect_on_association(scope_item)
89
+ scope_value = record.send(reflection.foreign_key)
90
+ scope_item = reflection.foreign_key
91
+ else
92
+ scope_value = record._read_attribute(scope_item)
93
+ end
94
+ relation = relation.and(table[scope_item].eq(scope_value))
65
95
  end
66
96
 
67
97
  relation
68
98
  end
99
+
100
+ def map_enum_attribute(klass, attribute, value)
101
+ mapping = klass.defined_enums[attribute.to_s]
102
+ value = mapping[value] if value && mapping
103
+ value
104
+ end
69
105
  end
70
106
 
71
107
  module ClassMethods
72
- # Validates whether the value of the specified attributes are unique across the system.
73
- # Useful for making sure that only one user
108
+ # Validates whether the value of the specified attributes are unique
109
+ # across the system. Useful for making sure that only one user
74
110
  # can be named "davidhh".
75
111
  #
76
112
  # class Person < ActiveRecord::Base
77
113
  # validates_uniqueness_of :user_name
78
114
  # end
79
115
  #
80
- # It can also validate whether the value of the specified attributes are unique based on a scope parameter:
116
+ # It can also validate whether the value of the specified attributes are
117
+ # unique based on a <tt>:scope</tt> parameter:
81
118
  #
82
119
  # class Person < ActiveRecord::Base
83
- # validates_uniqueness_of :user_name, :scope => :account_id
120
+ # validates_uniqueness_of :user_name, scope: :account_id
84
121
  # end
85
122
  #
86
- # Or even multiple scope parameters. For example, making sure that a teacher can only be on the schedule once
87
- # per semester for a particular class.
123
+ # Or even multiple scope parameters. For example, making sure that a
124
+ # teacher can only be on the schedule once per semester for a particular
125
+ # class.
88
126
  #
89
127
  # class TeacherSchedule < ActiveRecord::Base
90
- # validates_uniqueness_of :teacher_id, :scope => [:semester_id, :class_id]
128
+ # validates_uniqueness_of :teacher_id, scope: [:semester_id, :class_id]
129
+ # end
130
+ #
131
+ # It is also possible to limit the uniqueness constraint to a set of
132
+ # records matching certain conditions. In this example archived articles
133
+ # are not being taken into consideration when validating uniqueness
134
+ # of the title attribute:
135
+ #
136
+ # class Article < ActiveRecord::Base
137
+ # validates_uniqueness_of :title, conditions: -> { where.not(status: 'archived') }
91
138
  # end
92
139
  #
93
- # When the record is created, a check is performed to make sure that no record exists in the database
94
- # with the given value for the specified attribute (that maps to a column). When the record is updated,
140
+ # When the record is created, a check is performed to make sure that no
141
+ # record exists in the database with the given value for the specified
142
+ # attribute (that maps to a column). When the record is updated,
95
143
  # the same check is made but disregarding the record itself.
96
144
  #
97
145
  # Configuration options:
98
- # * <tt>:message</tt> - Specifies a custom error message (default is: "has already been taken").
99
- # * <tt>:scope</tt> - One or more columns by which to limit the scope of the uniqueness constraint.
100
- # * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (+true+ by default).
101
- # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
102
- # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
103
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
104
- # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).
105
- # The method, proc or string should return or evaluate to a true or false value.
106
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
107
- # not occur (e.g. <tt>:unless => :skip_validation</tt>, or
108
- # <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method, proc or string should
109
- # return or evaluate to a true or false value.
146
+ #
147
+ # * <tt>:message</tt> - Specifies a custom error message (default is:
148
+ # "has already been taken").
149
+ # * <tt>:scope</tt> - One or more columns by which to limit the scope of
150
+ # the uniqueness constraint.
151
+ # * <tt>:conditions</tt> - Specify the conditions to be included as a
152
+ # <tt>WHERE</tt> SQL fragment to limit the uniqueness constraint lookup
153
+ # (e.g. <tt>conditions: -> { where(status: 'active') }</tt>).
154
+ # * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by
155
+ # non-text columns (+true+ by default).
156
+ # * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the
157
+ # attribute is +nil+ (default is +false+).
158
+ # * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the
159
+ # attribute is blank (default is +false+).
160
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
161
+ # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
162
+ # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
163
+ # proc or string should return or evaluate to a +true+ or +false+ value.
164
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to
165
+ # determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
166
+ # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
167
+ # method, proc or string should return or evaluate to a +true+ or +false+
168
+ # value.
110
169
  #
111
170
  # === Concurrency and integrity
112
171
  #
@@ -126,11 +185,11 @@ module ActiveRecord
126
185
  # WHERE title = 'My Post' |
127
186
  # |
128
187
  # | # User 2 does the same thing and also
129
- # | # infers that his title is unique.
188
+ # | # infers that their title is unique.
130
189
  # | SELECT * FROM comments
131
190
  # | WHERE title = 'My Post'
132
191
  # |
133
- # # User 1 inserts his comment. |
192
+ # # User 1 inserts their comment. |
134
193
  # INSERT INTO comments |
135
194
  # (title, content) VALUES |
136
195
  # ('My Post', 'hi!') |
@@ -156,22 +215,22 @@ module ActiveRecord
156
215
  # exception. You can either choose to let this error propagate (which
157
216
  # will result in the default Rails exception page being shown), or you
158
217
  # can catch it and restart the transaction (e.g. by telling the user
159
- # that the title already exists, and asking him to re-enter the title).
160
- # This technique is also known as optimistic concurrency control:
161
- # http://en.wikipedia.org/wiki/Optimistic_concurrency_control
218
+ # that the title already exists, and asking them to re-enter the title).
219
+ # This technique is also known as
220
+ # {optimistic concurrency control}[http://en.wikipedia.org/wiki/Optimistic_concurrency_control].
162
221
  #
163
222
  # The bundled ActiveRecord::ConnectionAdapters distinguish unique index
164
223
  # constraint errors from other types of database errors by throwing an
165
- # ActiveRecord::RecordNotUnique exception.
166
- # For other adapters you will have to parse the (database-specific) exception
167
- # message to detect such a case.
224
+ # ActiveRecord::RecordNotUnique exception. For other adapters you will
225
+ # have to parse the (database-specific) exception message to detect such
226
+ # a case.
227
+ #
168
228
  # The following bundled adapters throw the ActiveRecord::RecordNotUnique exception:
169
- # * ActiveRecord::ConnectionAdapters::MysqlAdapter
170
- # * ActiveRecord::ConnectionAdapters::Mysql2Adapter
171
- # * ActiveRecord::ConnectionAdapters::SQLiteAdapter
172
- # * ActiveRecord::ConnectionAdapters::SQLite3Adapter
173
- # * ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
174
229
  #
230
+ # * ActiveRecord::ConnectionAdapters::MysqlAdapter.
231
+ # * ActiveRecord::ConnectionAdapters::Mysql2Adapter.
232
+ # * ActiveRecord::ConnectionAdapters::SQLite3Adapter.
233
+ # * ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.
175
234
  def validates_uniqueness_of(*attr_names)
176
235
  validates_with UniquenessValidator, _merge_attributes(attr_names)
177
236
  end
@@ -5,16 +5,17 @@ module ActiveRecord
5
5
  # +record+ method to retrieve the record which did not validate.
6
6
  #
7
7
  # begin
8
- # complex_operation_that_calls_save!_internally
8
+ # complex_operation_that_internally_calls_save!
9
9
  # rescue ActiveRecord::RecordInvalid => invalid
10
10
  # puts invalid.record.errors
11
11
  # end
12
12
  class RecordInvalid < ActiveRecordError
13
13
  attr_reader :record
14
+
14
15
  def initialize(record)
15
16
  @record = record
16
17
  errors = @record.errors.full_messages.join(", ")
17
- super(I18n.t("activerecord.errors.messages.record_invalid", :errors => errors))
18
+ super(I18n.t(:"#{@record.class.i18n_scope}.errors.messages.record_invalid", :errors => errors, :default => :"errors.messages.record_invalid"))
18
19
  end
19
20
  end
20
21
 
@@ -29,38 +30,26 @@ module ActiveRecord
29
30
  extend ActiveSupport::Concern
30
31
  include ActiveModel::Validations
31
32
 
32
- module ClassMethods
33
- # Creates an object just like Base.create but calls <tt>save!</tt> instead of +save+
34
- # so an exception is raised if the record is invalid.
35
- def create!(attributes = nil, options = {}, &block)
36
- if attributes.is_a?(Array)
37
- attributes.collect { |attr| create!(attr, options, &block) }
38
- else
39
- object = new(attributes, options)
40
- yield(object) if block_given?
41
- object.save!
42
- object
43
- end
44
- end
45
- end
46
-
47
- # The validation process on save can be skipped by passing <tt>:validate => false</tt>. The regular Base#save method is
48
- # replaced with this when the validations module is mixed in, which it is by default.
33
+ # The validation process on save can be skipped by passing <tt>validate: false</tt>.
34
+ # The regular Base#save method is replaced with this when the validations
35
+ # module is mixed in, which it is by default.
49
36
  def save(options={})
50
37
  perform_validations(options) ? super : false
51
38
  end
52
39
 
53
- # Attempts to save the record just like Base#save but will raise a +RecordInvalid+ exception instead of returning false
54
- # if the record is not valid.
40
+ # Attempts to save the record just like Base#save but will raise a +RecordInvalid+
41
+ # exception instead of returning +false+ if the record is not valid.
55
42
  def save!(options={})
56
- perform_validations(options) ? super : raise(RecordInvalid.new(self))
43
+ perform_validations(options) ? super : raise_record_invalid
57
44
  end
58
45
 
59
- # Runs all the validations within the specified context. Returns true if no errors are found,
60
- # false otherwise.
46
+ # Runs all the validations within the specified context. Returns +true+ if
47
+ # no errors are found, +false+ otherwise.
48
+ #
49
+ # Aliased as validate.
61
50
  #
62
- # If the argument is false (default is +nil+), the context is set to <tt>:create</tt> if
63
- # <tt>new_record?</tt> is true, and to <tt>:update</tt> if it is not.
51
+ # If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
52
+ # <tt>new_record?</tt> is +true+, and to <tt>:update</tt> if it is not.
64
53
  #
65
54
  # Validations with no <tt>:on</tt> option will run no matter the context. Validations with
66
55
  # some <tt>:on</tt> option will only run in the specified context.
@@ -70,14 +59,32 @@ module ActiveRecord
70
59
  errors.empty? && output
71
60
  end
72
61
 
62
+ alias_method :validate, :valid?
63
+
64
+ # Runs all the validations within the specified context. Returns +true+ if
65
+ # no errors are found, raises +RecordInvalid+ otherwise.
66
+ #
67
+ # If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
68
+ # <tt>new_record?</tt> is +true+, and to <tt>:update</tt> if it is not.
69
+ #
70
+ # Validations with no <tt>:on</tt> option will run no matter the context. Validations with
71
+ # some <tt>:on</tt> option will only run in the specified context.
72
+ def validate!(context = nil)
73
+ valid?(context) || raise_record_invalid
74
+ end
75
+
73
76
  protected
74
77
 
75
- def perform_validations(options={})
76
- perform_validation = options[:validate] != false
77
- perform_validation ? valid?(options[:context]) : true
78
+ def raise_record_invalid
79
+ raise(RecordInvalid.new(self))
80
+ end
81
+
82
+ def perform_validations(options={}) # :nodoc:
83
+ options[:validate] == false || valid?(options[:context])
78
84
  end
79
85
  end
80
86
  end
81
87
 
82
88
  require "active_record/validations/associated"
83
89
  require "active_record/validations/uniqueness"
90
+ require "active_record/validations/presence"
@@ -1,10 +1,8 @@
1
- module ActiveRecord
2
- module VERSION #:nodoc:
3
- MAJOR = 3
4
- MINOR = 2
5
- TINY = 22
6
- PRE = "5"
1
+ require_relative 'gem_version'
7
2
 
8
- STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
3
+ module ActiveRecord
4
+ # Returns the version of the currently loaded ActiveRecord as a <tt>Gem::Version</tt>
5
+ def self.version
6
+ gem_version
9
7
  end
10
8
  end
data/lib/active_record.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2004-2011 David Heinemeier Hansson
2
+ # Copyright (c) 2004-2014 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
@@ -22,24 +22,51 @@
22
22
  #++
23
23
 
24
24
  require 'active_support'
25
- require 'active_support/i18n'
25
+ require 'active_support/rails'
26
26
  require 'active_model'
27
27
  require 'arel'
28
28
 
29
29
  require 'active_record/version'
30
+ require 'active_record/attribute_set'
30
31
 
31
32
  module ActiveRecord
32
33
  extend ActiveSupport::Autoload
33
34
 
34
- # ActiveRecord::SessionStore depends on the abstract store in Action Pack.
35
- # Eager loading this class would break client code that eager loads Active
36
- # Record standalone.
37
- #
38
- # Note that the Rails application generator creates an initializer specific
39
- # for setting the session store. Thus, albeit in theory this autoload would
40
- # not be thread-safe, in practice it is because if the application uses this
41
- # session store its autoload happens at boot time.
42
- autoload :SessionStore
35
+ autoload :Attribute
36
+ autoload :Base
37
+ autoload :Callbacks
38
+ autoload :Core
39
+ autoload :ConnectionHandling
40
+ autoload :CounterCache
41
+ autoload :DynamicMatchers
42
+ autoload :Enum
43
+ autoload :Explain
44
+ autoload :Inheritance
45
+ autoload :Integration
46
+ autoload :Migration
47
+ autoload :Migrator, 'active_record/migration'
48
+ autoload :ModelSchema
49
+ autoload :NestedAttributes
50
+ autoload :NoTouching
51
+ autoload :Persistence
52
+ autoload :QueryCache
53
+ autoload :Querying
54
+ autoload :ReadonlyAttributes
55
+ autoload :RecordInvalid, 'active_record/validations'
56
+ autoload :Reflection
57
+ autoload :RuntimeRegistry
58
+ autoload :Sanitization
59
+ autoload :Schema
60
+ autoload :SchemaDumper
61
+ autoload :SchemaMigration
62
+ autoload :Scoping
63
+ autoload :Serialization
64
+ autoload :StatementCache
65
+ autoload :Store
66
+ autoload :Timestamp
67
+ autoload :Transactions
68
+ autoload :Translation
69
+ autoload :Validations
43
70
 
44
71
  eager_autoload do
45
72
  autoload :ActiveRecordError, 'active_record/errors'
@@ -48,11 +75,15 @@ module ActiveRecord
48
75
 
49
76
  autoload :Aggregations
50
77
  autoload :Associations
51
- autoload :AttributeMethods
52
78
  autoload :AttributeAssignment
79
+ autoload :AttributeMethods
53
80
  autoload :AutosaveAssociation
54
81
 
82
+ autoload :LegacyYamlAdapter
83
+
55
84
  autoload :Relation
85
+ autoload :AssociationRelation
86
+ autoload :NullRelation
56
87
 
57
88
  autoload_under 'relation' do
58
89
  autoload :QueryMethods
@@ -61,45 +92,15 @@ module ActiveRecord
61
92
  autoload :PredicateBuilder
62
93
  autoload :SpawnMethods
63
94
  autoload :Batches
64
- autoload :Explain
65
95
  autoload :Delegation
66
96
  end
67
97
 
68
- autoload :Base
69
- autoload :Callbacks
70
- autoload :CounterCache
71
- autoload :DynamicMatchers
72
- autoload :DynamicFinderMatch
73
- autoload :DynamicScopeMatch
74
- autoload :Explain
75
- autoload :IdentityMap
76
- autoload :Inheritance
77
- autoload :Integration
78
- autoload :Migration
79
- autoload :Migrator, 'active_record/migration'
80
- autoload :ModelSchema
81
- autoload :NestedAttributes
82
- autoload :Observer
83
- autoload :Persistence
84
- autoload :QueryCache
85
- autoload :Querying
86
- autoload :ReadonlyAttributes
87
- autoload :Reflection
88
98
  autoload :Result
89
- autoload :Sanitization
90
- autoload :Schema
91
- autoload :SchemaDumper
92
- autoload :Scoping
93
- autoload :Serialization
94
- autoload :Store
95
- autoload :Timestamp
96
- autoload :Transactions
97
- autoload :Translation
98
- autoload :Validations
99
99
  end
100
100
 
101
101
  module Coders
102
102
  autoload :YAMLColumn, 'active_record/coders/yaml_column'
103
+ autoload :JSON, 'active_record/coders/json'
103
104
  end
104
105
 
105
106
  module AttributeMethods
@@ -114,7 +115,6 @@ module ActiveRecord
114
115
  autoload :TimeZoneConversion
115
116
  autoload :Write
116
117
  autoload :Serialization
117
- autoload :DeprecatedUnderscoreRead
118
118
  end
119
119
  end
120
120
 
@@ -145,12 +145,32 @@ module ActiveRecord
145
145
  end
146
146
  end
147
147
 
148
- autoload :TestCase
148
+ module Tasks
149
+ extend ActiveSupport::Autoload
150
+
151
+ autoload :DatabaseTasks
152
+ autoload :SQLiteDatabaseTasks, 'active_record/tasks/sqlite_database_tasks'
153
+ autoload :MySQLDatabaseTasks, 'active_record/tasks/mysql_database_tasks'
154
+ autoload :PostgreSQLDatabaseTasks,
155
+ 'active_record/tasks/postgresql_database_tasks'
156
+ end
157
+
149
158
  autoload :TestFixtures, 'active_record/fixtures'
159
+
160
+ def self.eager_load!
161
+ super
162
+ ActiveRecord::Locking.eager_load!
163
+ ActiveRecord::Scoping.eager_load!
164
+ ActiveRecord::Associations.eager_load!
165
+ ActiveRecord::AttributeMethods.eager_load!
166
+ ActiveRecord::ConnectionAdapters.eager_load!
167
+ end
150
168
  end
151
169
 
152
170
  ActiveSupport.on_load(:active_record) do
153
171
  Arel::Table.engine = self
154
172
  end
155
173
 
156
- I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
174
+ ActiveSupport.on_load(:i18n) do
175
+ I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
176
+ end
@@ -1,25 +1,70 @@
1
1
  require 'rails/generators/active_record'
2
2
 
3
3
  module ActiveRecord
4
- module Generators
5
- class MigrationGenerator < Base
4
+ module Generators # :nodoc:
5
+ class MigrationGenerator < Base # :nodoc:
6
6
  argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]"
7
7
 
8
8
  def create_migration_file
9
9
  set_local_assigns!
10
- migration_template "migration.rb", "db/migrate/#{file_name}.rb"
10
+ validate_file_name!
11
+ migration_template @migration_template, "db/migrate/#{file_name}.rb"
11
12
  end
12
13
 
13
14
  protected
14
- attr_reader :migration_action
15
+ attr_reader :migration_action, :join_tables
15
16
 
16
- def set_local_assigns!
17
- if file_name =~ /^(add|remove)_.*_(?:to|from)_(.*)/
18
- @migration_action = $1
19
- @table_name = $2.pluralize
17
+ # sets the default migration template that is being used for the generation of the migration
18
+ # depending on the arguments which would be sent out in the command line, the migration template
19
+ # and the table name instance variables are setup.
20
+
21
+ def set_local_assigns!
22
+ @migration_template = "migration.rb"
23
+ case file_name
24
+ when /^(add|remove)_.*_(?:to|from)_(.*)/
25
+ @migration_action = $1
26
+ @table_name = normalize_table_name($2)
27
+ when /join_table/
28
+ if attributes.length == 2
29
+ @migration_action = 'join'
30
+ @join_tables = pluralize_table_names? ? attributes.map(&:plural_name) : attributes.map(&:singular_name)
31
+
32
+ set_index_names
33
+ end
34
+ when /^create_(.+)/
35
+ @table_name = normalize_table_name($1)
36
+ @migration_template = "create_table_migration.rb"
37
+ end
38
+ end
39
+
40
+ def set_index_names
41
+ attributes.each_with_index do |attr, i|
42
+ attr.index_name = [attr, attributes[i - 1]].map{ |a| index_name_for(a) }
43
+ end
44
+ end
45
+
46
+ def index_name_for(attribute)
47
+ if attribute.foreign_key?
48
+ attribute.name
49
+ else
50
+ attribute.name.singularize.foreign_key
51
+ end.to_sym
52
+ end
53
+
54
+ private
55
+ def attributes_with_index
56
+ attributes.select { |a| !a.reference? && a.has_index? }
57
+ end
58
+
59
+ def validate_file_name!
60
+ unless file_name =~ /^[_a-z0-9]+$/
61
+ raise IllegalMigrationNameError.new(file_name)
20
62
  end
21
63
  end
22
64
 
65
+ def normalize_table_name(_table_name)
66
+ pluralize_table_names? ? _table_name.pluralize : _table_name.singularize
67
+ end
23
68
  end
24
69
  end
25
70
  end
@@ -2,10 +2,14 @@ class <%= migration_class_name %> < ActiveRecord::Migration
2
2
  def change
3
3
  create_table :<%= table_name %> do |t|
4
4
  <% attributes.each do |attribute| -%>
5
+ <% if attribute.password_digest? -%>
6
+ t.string :password_digest<%= attribute.inject_options %>
7
+ <% else -%>
5
8
  t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
6
9
  <% end -%>
10
+ <% end -%>
7
11
  <% if options[:timestamps] %>
8
- t.timestamps
12
+ t.timestamps null: false
9
13
  <% end -%>
10
14
  end
11
15
  <% attributes_with_index.each do |attribute| -%>