activerecord 3.1.10 → 4.2.11

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 (237) hide show
  1. checksums.yaml +6 -6
  2. data/CHANGELOG.md +1837 -338
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +39 -43
  5. data/examples/performance.rb +51 -20
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +57 -43
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -39
  10. data/lib/active_record/associations/association.rb +71 -85
  11. data/lib/active_record/associations/association_scope.rb +138 -89
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -3
  14. data/lib/active_record/associations/builder/association.rb +125 -29
  15. data/lib/active_record/associations/builder/belongs_to.rb +91 -60
  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 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +22 -29
  21. data/lib/active_record/associations/collection_association.rb +294 -187
  22. data/lib/active_record/associations/collection_proxy.rb +961 -94
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +118 -23
  25. data/lib/active_record/associations/has_many_through_association.rb +115 -45
  26. data/lib/active_record/associations/has_one_association.rb +57 -24
  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 -102
  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 +61 -32
  38. data/lib/active_record/associations/preloader.rb +113 -87
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +37 -19
  41. data/lib/active_record/associations.rb +505 -371
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +212 -0
  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 +141 -51
  47. data/lib/active_record/attribute_methods/primary_key.rb +87 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +74 -117
  50. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -47
  52. data/lib/active_record/attribute_methods/write.rb +60 -21
  53. data/lib/active_record/attribute_methods.rb +409 -48
  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 +279 -232
  58. data/lib/active_record/base.rb +84 -1969
  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 +422 -243
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +170 -194
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -19
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -57
  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 +273 -170
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +731 -254
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +339 -95
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +946 -0
  75. data/lib/active_record/connection_adapters/column.rb +33 -221
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +140 -602
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +254 -756
  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 +445 -902
  114. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +578 -25
  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 +159 -102
  119. data/lib/active_record/dynamic_matchers.rb +140 -0
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +102 -34
  122. data/lib/active_record/explain.rb +38 -0
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +29 -0
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +318 -260
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +247 -0
  129. data/lib/active_record/integration.rb +113 -0
  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 +80 -52
  133. data/lib/active_record/locking/pessimistic.rb +27 -5
  134. data/lib/active_record/log_subscriber.rb +25 -18
  135. data/lib/active_record/migration/command_recorder.rb +130 -38
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +532 -201
  138. data/lib/active_record/model_schema.rb +342 -0
  139. data/lib/active_record/nested_attributes.rb +229 -139
  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 +304 -99
  143. data/lib/active_record/query_cache.rb +25 -43
  144. data/lib/active_record/querying.rb +68 -0
  145. data/lib/active_record/railtie.rb +86 -45
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +7 -4
  148. data/lib/active_record/railties/databases.rake +198 -377
  149. data/lib/active_record/railties/jdbcmysql_error.rb +2 -2
  150. data/lib/active_record/readonly_attributes.rb +23 -0
  151. data/lib/active_record/reflection.rb +516 -165
  152. data/lib/active_record/relation/batches.rb +96 -45
  153. data/lib/active_record/relation/calculations.rb +221 -144
  154. data/lib/active_record/relation/delegation.rb +140 -0
  155. data/lib/active_record/relation/finder_methods.rb +362 -243
  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 -41
  160. data/lib/active_record/relation/query_methods.rb +982 -155
  161. data/lib/active_record/relation/spawn_methods.rb +50 -110
  162. data/lib/active_record/relation.rb +371 -180
  163. data/lib/active_record/result.rb +109 -12
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +191 -0
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +111 -61
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +135 -0
  170. data/lib/active_record/scoping/named.rb +164 -0
  171. data/lib/active_record/scoping.rb +87 -0
  172. data/lib/active_record/serialization.rb +7 -45
  173. data/lib/active_record/serializers/xml_serializer.rb +14 -65
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +205 -0
  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 +35 -14
  181. data/lib/active_record/transactions.rb +141 -74
  182. data/lib/active_record/translation.rb +22 -0
  183. data/lib/active_record/type/big_integer.rb +13 -0
  184. data/lib/active_record/type/binary.rb +50 -0
  185. data/lib/active_record/type/boolean.rb +31 -0
  186. data/lib/active_record/type/date.rb +50 -0
  187. data/lib/active_record/type/date_time.rb +54 -0
  188. data/lib/active_record/type/decimal.rb +64 -0
  189. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  190. data/lib/active_record/type/decorator.rb +14 -0
  191. data/lib/active_record/type/float.rb +19 -0
  192. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  193. data/lib/active_record/type/integer.rb +59 -0
  194. data/lib/active_record/type/mutable.rb +16 -0
  195. data/lib/active_record/type/numeric.rb +36 -0
  196. data/lib/active_record/type/serialized.rb +62 -0
  197. data/lib/active_record/type/string.rb +40 -0
  198. data/lib/active_record/type/text.rb +11 -0
  199. data/lib/active_record/type/time.rb +26 -0
  200. data/lib/active_record/type/time_value.rb +38 -0
  201. data/lib/active_record/type/type_map.rb +64 -0
  202. data/lib/active_record/type/unsigned_integer.rb +15 -0
  203. data/lib/active_record/type/value.rb +110 -0
  204. data/lib/active_record/type.rb +23 -0
  205. data/lib/active_record/validations/associated.rb +27 -18
  206. data/lib/active_record/validations/presence.rb +67 -0
  207. data/lib/active_record/validations/uniqueness.rb +125 -66
  208. data/lib/active_record/validations.rb +37 -30
  209. data/lib/active_record/version.rb +5 -7
  210. data/lib/active_record.rb +80 -25
  211. data/lib/rails/generators/active_record/migration/migration_generator.rb +54 -9
  212. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  213. data/lib/rails/generators/active_record/migration/templates/migration.rb +25 -11
  214. data/lib/rails/generators/active_record/migration.rb +11 -8
  215. data/lib/rails/generators/active_record/model/model_generator.rb +17 -4
  216. data/lib/rails/generators/active_record/model/templates/model.rb +5 -2
  217. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  218. data/lib/rails/generators/active_record.rb +3 -11
  219. metadata +132 -53
  220. data/examples/associations.png +0 -0
  221. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -62
  222. data/lib/active_record/associations/join_helper.rb +0 -55
  223. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -135
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -556
  226. data/lib/active_record/dynamic_finder_match.rb +0 -56
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/identity_map.rb +0 -163
  229. data/lib/active_record/named_scope.rb +0 -200
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -358
  232. data/lib/active_record/test_case.rb +0 -69
  233. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -17
  234. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  235. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  236. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  237. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -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.send(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 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
- # will use SQL LOWER function before comparison
61
- relation = table[attribute].lower.eq(table.lower(value))
79
+ # will use SQL LOWER function before comparison, unless it detects a case insensitive collation
80
+ klass.connection.case_insensitive_comparison(table, attribute, column, value)
62
81
  else
63
- value = klass.connection.case_sensitive_modifier(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
84
- # end
120
+ # validates_uniqueness_of :user_name, scope: :account_id
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]
91
129
  # end
92
130
  #
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,
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') }
138
+ # end
139
+ #
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
@@ -1,20 +1,21 @@
1
1
  module ActiveRecord
2
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
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 = 1
5
- TINY = 10
6
- PRE = nil
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,25 +22,68 @@
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
 
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
70
+
34
71
  eager_autoload do
35
72
  autoload :ActiveRecordError, 'active_record/errors'
36
73
  autoload :ConnectionNotEstablished, 'active_record/errors'
74
+ autoload :ConnectionAdapters, 'active_record/connection_adapters/abstract_adapter'
37
75
 
38
76
  autoload :Aggregations
39
77
  autoload :Associations
78
+ autoload :AttributeAssignment
40
79
  autoload :AttributeMethods
41
80
  autoload :AutosaveAssociation
42
81
 
82
+ autoload :LegacyYamlAdapter
83
+
43
84
  autoload :Relation
85
+ autoload :AssociationRelation
86
+ autoload :NullRelation
44
87
 
45
88
  autoload_under 'relation' do
46
89
  autoload :QueryMethods
@@ -49,33 +92,15 @@ module ActiveRecord
49
92
  autoload :PredicateBuilder
50
93
  autoload :SpawnMethods
51
94
  autoload :Batches
95
+ autoload :Delegation
52
96
  end
53
97
 
54
- autoload :Base
55
- autoload :Callbacks
56
- autoload :CounterCache
57
- autoload :DynamicFinderMatch
58
- autoload :DynamicScopeMatch
59
- autoload :Migration
60
- autoload :Migrator, 'active_record/migration'
61
- autoload :NamedScope
62
- autoload :NestedAttributes
63
- autoload :Observer
64
- autoload :Persistence
65
- autoload :QueryCache
66
- autoload :Reflection
67
- autoload :Schema
68
- autoload :SchemaDumper
69
- autoload :Serialization
70
- autoload :SessionStore
71
- autoload :Timestamp
72
- autoload :Transactions
73
- autoload :Validations
74
- autoload :IdentityMap
98
+ autoload :Result
75
99
  end
76
100
 
77
101
  module Coders
78
102
  autoload :YAMLColumn, 'active_record/coders/yaml_column'
103
+ autoload :JSON, 'active_record/coders/json'
79
104
  end
80
105
 
81
106
  module AttributeMethods
@@ -89,6 +114,7 @@ module ActiveRecord
89
114
  autoload :Read
90
115
  autoload :TimeZoneConversion
91
116
  autoload :Write
117
+ autoload :Serialization
92
118
  end
93
119
  end
94
120
 
@@ -110,12 +136,41 @@ module ActiveRecord
110
136
  end
111
137
  end
112
138
 
113
- autoload :TestCase
139
+ module Scoping
140
+ extend ActiveSupport::Autoload
141
+
142
+ eager_autoload do
143
+ autoload :Named
144
+ autoload :Default
145
+ end
146
+ end
147
+
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
+
114
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
115
168
  end
116
169
 
117
170
  ActiveSupport.on_load(:active_record) do
118
171
  Arel::Table.engine = self
119
172
  end
120
173
 
121
- 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
6
- argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
4
+ module Generators # :nodoc:
5
+ class MigrationGenerator < Base # :nodoc:
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
@@ -0,0 +1,19 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ def change
3
+ create_table :<%= table_name %> do |t|
4
+ <% attributes.each do |attribute| -%>
5
+ <% if attribute.password_digest? -%>
6
+ t.string :password_digest<%= attribute.inject_options %>
7
+ <% else -%>
8
+ t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
9
+ <% end -%>
10
+ <% end -%>
11
+ <% if options[:timestamps] %>
12
+ t.timestamps null: false
13
+ <% end -%>
14
+ end
15
+ <% attributes_with_index.each do |attribute| -%>
16
+ add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
17
+ <% end -%>
18
+ end
19
+ end