activerecord 4.2.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 (221) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1372 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +218 -0
  5. data/examples/performance.rb +184 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +173 -0
  8. data/lib/active_record/aggregations.rb +266 -0
  9. data/lib/active_record/association_relation.rb +22 -0
  10. data/lib/active_record/associations.rb +1724 -0
  11. data/lib/active_record/associations/alias_tracker.rb +87 -0
  12. data/lib/active_record/associations/association.rb +253 -0
  13. data/lib/active_record/associations/association_scope.rb +194 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +111 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +149 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +116 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +91 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
  20. data/lib/active_record/associations/builder/has_many.rb +15 -0
  21. data/lib/active_record/associations/builder/has_one.rb +23 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +38 -0
  23. data/lib/active_record/associations/collection_association.rb +634 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1027 -0
  25. data/lib/active_record/associations/has_many_association.rb +184 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +238 -0
  27. data/lib/active_record/associations/has_one_association.rb +105 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +282 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/preloader.rb +203 -0
  34. data/lib/active_record/associations/preloader/association.rb +162 -0
  35. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  36. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  37. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  38. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  39. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  40. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  41. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  42. data/lib/active_record/associations/preloader/through_association.rb +96 -0
  43. data/lib/active_record/associations/singular_association.rb +86 -0
  44. data/lib/active_record/associations/through_association.rb +96 -0
  45. data/lib/active_record/attribute.rb +149 -0
  46. data/lib/active_record/attribute_assignment.rb +212 -0
  47. data/lib/active_record/attribute_decorators.rb +66 -0
  48. data/lib/active_record/attribute_methods.rb +439 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
  50. data/lib/active_record/attribute_methods/dirty.rb +181 -0
  51. data/lib/active_record/attribute_methods/primary_key.rb +128 -0
  52. data/lib/active_record/attribute_methods/query.rb +40 -0
  53. data/lib/active_record/attribute_methods/read.rb +103 -0
  54. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  55. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  56. data/lib/active_record/attribute_methods/write.rb +83 -0
  57. data/lib/active_record/attribute_set.rb +77 -0
  58. data/lib/active_record/attribute_set/builder.rb +86 -0
  59. data/lib/active_record/attributes.rb +139 -0
  60. data/lib/active_record/autosave_association.rb +439 -0
  61. data/lib/active_record/base.rb +317 -0
  62. data/lib/active_record/callbacks.rb +313 -0
  63. data/lib/active_record/coders/json.rb +13 -0
  64. data/lib/active_record/coders/yaml_column.rb +38 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
  78. data/lib/active_record/connection_adapters/column.rb +82 -0
  79. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
  81. data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
  82. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  111. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  112. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
  119. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  120. data/lib/active_record/connection_handling.rb +132 -0
  121. data/lib/active_record/core.rb +566 -0
  122. data/lib/active_record/counter_cache.rb +175 -0
  123. data/lib/active_record/dynamic_matchers.rb +140 -0
  124. data/lib/active_record/enum.rb +198 -0
  125. data/lib/active_record/errors.rb +252 -0
  126. data/lib/active_record/explain.rb +38 -0
  127. data/lib/active_record/explain_registry.rb +30 -0
  128. data/lib/active_record/explain_subscriber.rb +29 -0
  129. data/lib/active_record/fixture_set/file.rb +56 -0
  130. data/lib/active_record/fixtures.rb +1007 -0
  131. data/lib/active_record/gem_version.rb +15 -0
  132. data/lib/active_record/inheritance.rb +247 -0
  133. data/lib/active_record/integration.rb +113 -0
  134. data/lib/active_record/locale/en.yml +47 -0
  135. data/lib/active_record/locking/optimistic.rb +204 -0
  136. data/lib/active_record/locking/pessimistic.rb +77 -0
  137. data/lib/active_record/log_subscriber.rb +75 -0
  138. data/lib/active_record/migration.rb +1051 -0
  139. data/lib/active_record/migration/command_recorder.rb +197 -0
  140. data/lib/active_record/migration/join_table.rb +15 -0
  141. data/lib/active_record/model_schema.rb +340 -0
  142. data/lib/active_record/nested_attributes.rb +548 -0
  143. data/lib/active_record/no_touching.rb +52 -0
  144. data/lib/active_record/null_relation.rb +81 -0
  145. data/lib/active_record/persistence.rb +532 -0
  146. data/lib/active_record/query_cache.rb +56 -0
  147. data/lib/active_record/querying.rb +68 -0
  148. data/lib/active_record/railtie.rb +162 -0
  149. data/lib/active_record/railties/console_sandbox.rb +5 -0
  150. data/lib/active_record/railties/controller_runtime.rb +50 -0
  151. data/lib/active_record/railties/databases.rake +391 -0
  152. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  153. data/lib/active_record/readonly_attributes.rb +23 -0
  154. data/lib/active_record/reflection.rb +881 -0
  155. data/lib/active_record/relation.rb +681 -0
  156. data/lib/active_record/relation/batches.rb +138 -0
  157. data/lib/active_record/relation/calculations.rb +403 -0
  158. data/lib/active_record/relation/delegation.rb +140 -0
  159. data/lib/active_record/relation/finder_methods.rb +528 -0
  160. data/lib/active_record/relation/merger.rb +170 -0
  161. data/lib/active_record/relation/predicate_builder.rb +126 -0
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  164. data/lib/active_record/relation/query_methods.rb +1176 -0
  165. data/lib/active_record/relation/spawn_methods.rb +75 -0
  166. data/lib/active_record/result.rb +131 -0
  167. data/lib/active_record/runtime_registry.rb +22 -0
  168. data/lib/active_record/sanitization.rb +191 -0
  169. data/lib/active_record/schema.rb +64 -0
  170. data/lib/active_record/schema_dumper.rb +251 -0
  171. data/lib/active_record/schema_migration.rb +56 -0
  172. data/lib/active_record/scoping.rb +87 -0
  173. data/lib/active_record/scoping/default.rb +134 -0
  174. data/lib/active_record/scoping/named.rb +164 -0
  175. data/lib/active_record/serialization.rb +22 -0
  176. data/lib/active_record/serializers/xml_serializer.rb +193 -0
  177. data/lib/active_record/statement_cache.rb +111 -0
  178. data/lib/active_record/store.rb +205 -0
  179. data/lib/active_record/tasks/database_tasks.rb +296 -0
  180. data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
  181. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  182. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  183. data/lib/active_record/timestamp.rb +121 -0
  184. data/lib/active_record/transactions.rb +417 -0
  185. data/lib/active_record/translation.rb +22 -0
  186. data/lib/active_record/type.rb +23 -0
  187. data/lib/active_record/type/big_integer.rb +13 -0
  188. data/lib/active_record/type/binary.rb +50 -0
  189. data/lib/active_record/type/boolean.rb +30 -0
  190. data/lib/active_record/type/date.rb +46 -0
  191. data/lib/active_record/type/date_time.rb +43 -0
  192. data/lib/active_record/type/decimal.rb +40 -0
  193. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  194. data/lib/active_record/type/decorator.rb +14 -0
  195. data/lib/active_record/type/float.rb +19 -0
  196. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  197. data/lib/active_record/type/integer.rb +55 -0
  198. data/lib/active_record/type/mutable.rb +16 -0
  199. data/lib/active_record/type/numeric.rb +36 -0
  200. data/lib/active_record/type/serialized.rb +56 -0
  201. data/lib/active_record/type/string.rb +36 -0
  202. data/lib/active_record/type/text.rb +11 -0
  203. data/lib/active_record/type/time.rb +26 -0
  204. data/lib/active_record/type/time_value.rb +38 -0
  205. data/lib/active_record/type/type_map.rb +64 -0
  206. data/lib/active_record/type/unsigned_integer.rb +15 -0
  207. data/lib/active_record/type/value.rb +101 -0
  208. data/lib/active_record/validations.rb +90 -0
  209. data/lib/active_record/validations/associated.rb +51 -0
  210. data/lib/active_record/validations/presence.rb +67 -0
  211. data/lib/active_record/validations/uniqueness.rb +229 -0
  212. data/lib/active_record/version.rb +8 -0
  213. data/lib/rails/generators/active_record.rb +17 -0
  214. data/lib/rails/generators/active_record/migration.rb +18 -0
  215. data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
  216. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
  217. data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
  218. data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
  219. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  220. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  221. metadata +309 -0
@@ -0,0 +1,51 @@
1
+ module ActiveRecord
2
+ module Validations
3
+ class AssociatedValidator < ActiveModel::EachValidator #:nodoc:
4
+ def validate_each(record, attribute, value)
5
+ if Array.wrap(value).reject {|r| r.marked_for_destruction? || r.valid?}.any?
6
+ record.errors.add(attribute, :invalid, options.merge(:value => value))
7
+ end
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ # Validates whether the associated object or objects are all valid.
13
+ # Works with any kind of association.
14
+ #
15
+ # class Book < ActiveRecord::Base
16
+ # has_many :pages
17
+ # belongs_to :library
18
+ #
19
+ # validates_associated :pages, :library
20
+ # end
21
+ #
22
+ # WARNING: This validation must not be used on both ends of an association.
23
+ # Doing so will lead to a circular dependency and cause infinite recursion.
24
+ #
25
+ # NOTE: This validation will not fail if the association hasn't been
26
+ # assigned. If you want to ensure that the association is both present and
27
+ # guaranteed to be valid, you also need to use +validates_presence_of+.
28
+ #
29
+ # Configuration options:
30
+ #
31
+ # * <tt>:message</tt> - A custom error message (default is: "is invalid").
32
+ # * <tt>:on</tt> - Specifies the contexts where this validation is active.
33
+ # Runs in all validation contexts by default (nil). You can pass a symbol
34
+ # or an array of symbols. (e.g. <tt>on: :create</tt> or
35
+ # <tt>on: :custom_validation_context</tt> or
36
+ # <tt>on: [:create, :custom_validation_context]</tt>)
37
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
38
+ # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
39
+ # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
40
+ # proc or string should return or evaluate to a +true+ or +false+ value.
41
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to
42
+ # determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
43
+ # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
44
+ # method, proc or string should return or evaluate to a +true+ or +false+
45
+ # value.
46
+ def validates_associated(*attr_names)
47
+ validates_with AssociatedValidator, _merge_attributes(attr_names)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,67 @@
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.wrap(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 the contexts where this validation is active.
48
+ # Runs in all validation contexts by default (nil). You can pass a symbol
49
+ # or an array of symbols. (e.g. <tt>on: :create</tt> or
50
+ # <tt>on: :custom_validation_context</tt> or
51
+ # <tt>on: [:create, :custom_validation_context]</tt>)
52
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
53
+ # the validation should occur (e.g. <tt>if: :allow_validation</tt>, or
54
+ # <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
55
+ # or string should return or evaluate to a +true+ or +false+ value.
56
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
57
+ # if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
58
+ # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
59
+ # proc or string should return or evaluate to a +true+ or +false+ value.
60
+ # * <tt>:strict</tt> - Specifies whether validation should be strict.
61
+ # See <tt>ActiveModel::Validation#validates!</tt> for more information.
62
+ def validates_presence_of(*attr_names)
63
+ validates_with PresenceValidator, _merge_attributes(attr_names)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,229 @@
1
+ module ActiveRecord
2
+ module Validations
3
+ class UniquenessValidator < ActiveModel::EachValidator # :nodoc:
4
+ def initialize(options)
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]
11
+ end
12
+
13
+ def validate_each(record, attribute, value)
14
+ finder_class = find_finder_class_for(record)
15
+ table = finder_class.arel_table
16
+ value = map_enum_attribute(finder_class, attribute, value)
17
+
18
+ relation = build_relation(finder_class, table, attribute, value)
19
+ relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id)) if record.persisted?
20
+ relation = scope_relation(record, table, relation)
21
+ relation = finder_class.unscoped.where(relation)
22
+ relation = relation.merge(options[:conditions]) if options[:conditions]
23
+
24
+ if relation.exists?
25
+ error_options = options.except(:case_sensitive, :scope, :conditions)
26
+ error_options[:value] = value
27
+
28
+ record.errors.add(attribute, :taken, error_options)
29
+ end
30
+ end
31
+
32
+ protected
33
+ # The check for an existing value should be run from a class that
34
+ # isn't abstract. This means working down from the current class
35
+ # (self), to the first non-abstract class. Since classes don't know
36
+ # their subclasses, we have to build the hierarchy between self and
37
+ # the record's class.
38
+ def find_finder_class_for(record) #:nodoc:
39
+ class_hierarchy = [record.class]
40
+
41
+ while class_hierarchy.first != @klass
42
+ class_hierarchy.unshift(class_hierarchy.first.superclass)
43
+ end
44
+
45
+ class_hierarchy.detect { |klass| !klass.abstract_class? }
46
+ end
47
+
48
+ def build_relation(klass, table, attribute, value) #:nodoc:
49
+ if reflection = klass._reflect_on_association(attribute)
50
+ attribute = reflection.foreign_key
51
+ value = value.attributes[reflection.klass.primary_key] unless value.nil?
52
+ end
53
+
54
+ attribute_name = attribute.to_s
55
+
56
+ # the attribute may be an aliased attribute
57
+ if klass.attribute_aliases[attribute_name]
58
+ attribute = klass.attribute_aliases[attribute_name]
59
+ attribute_name = attribute.to_s
60
+ end
61
+
62
+ column = klass.columns_hash[attribute_name]
63
+ value = klass.connection.type_cast(value, column)
64
+ if value.is_a?(String) && column.limit
65
+ value = value.to_s[0, column.limit]
66
+ end
67
+
68
+ if !options[:case_sensitive] && value.is_a?(String)
69
+ # will use SQL LOWER function before comparison, unless it detects a case insensitive collation
70
+ klass.connection.case_insensitive_comparison(table, attribute, column, value)
71
+ else
72
+ klass.connection.case_sensitive_comparison(table, attribute, column, value)
73
+ end
74
+ end
75
+
76
+ def scope_relation(record, table, relation)
77
+ Array(options[:scope]).each do |scope_item|
78
+ if reflection = record.class._reflect_on_association(scope_item)
79
+ scope_value = record.send(reflection.foreign_key)
80
+ scope_item = reflection.foreign_key
81
+ else
82
+ scope_value = record._read_attribute(scope_item)
83
+ end
84
+ relation = relation.and(table[scope_item].eq(scope_value))
85
+ end
86
+
87
+ relation
88
+ end
89
+
90
+ def map_enum_attribute(klass, attribute, value)
91
+ mapping = klass.defined_enums[attribute.to_s]
92
+ value = mapping[value] if value && mapping
93
+ value
94
+ end
95
+ end
96
+
97
+ module ClassMethods
98
+ # Validates whether the value of the specified attributes are unique
99
+ # across the system. Useful for making sure that only one user
100
+ # can be named "davidhh".
101
+ #
102
+ # class Person < ActiveRecord::Base
103
+ # validates_uniqueness_of :user_name
104
+ # end
105
+ #
106
+ # It can also validate whether the value of the specified attributes are
107
+ # unique based on a <tt>:scope</tt> parameter:
108
+ #
109
+ # class Person < ActiveRecord::Base
110
+ # validates_uniqueness_of :user_name, scope: :account_id
111
+ # end
112
+ #
113
+ # Or even multiple scope parameters. For example, making sure that a
114
+ # teacher can only be on the schedule once per semester for a particular
115
+ # class.
116
+ #
117
+ # class TeacherSchedule < ActiveRecord::Base
118
+ # validates_uniqueness_of :teacher_id, scope: [:semester_id, :class_id]
119
+ # end
120
+ #
121
+ # It is also possible to limit the uniqueness constraint to a set of
122
+ # records matching certain conditions. In this example archived articles
123
+ # are not being taken into consideration when validating uniqueness
124
+ # of the title attribute:
125
+ #
126
+ # class Article < ActiveRecord::Base
127
+ # validates_uniqueness_of :title, conditions: -> { where.not(status: 'archived') }
128
+ # end
129
+ #
130
+ # When the record is created, a check is performed to make sure that no
131
+ # record exists in the database with the given value for the specified
132
+ # attribute (that maps to a column). When the record is updated,
133
+ # the same check is made but disregarding the record itself.
134
+ #
135
+ # Configuration options:
136
+ #
137
+ # * <tt>:message</tt> - Specifies a custom error message (default is:
138
+ # "has already been taken").
139
+ # * <tt>:scope</tt> - One or more columns by which to limit the scope of
140
+ # the uniqueness constraint.
141
+ # * <tt>:conditions</tt> - Specify the conditions to be included as a
142
+ # <tt>WHERE</tt> SQL fragment to limit the uniqueness constraint lookup
143
+ # (e.g. <tt>conditions: -> { where(status: 'active') }</tt>).
144
+ # * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by
145
+ # non-text columns (+true+ by default).
146
+ # * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the
147
+ # attribute is +nil+ (default is +false+).
148
+ # * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the
149
+ # attribute is blank (default is +false+).
150
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
151
+ # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
152
+ # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
153
+ # proc or string should return or evaluate to a +true+ or +false+ value.
154
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to
155
+ # determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
156
+ # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
157
+ # method, proc or string should return or evaluate to a +true+ or +false+
158
+ # value.
159
+ #
160
+ # === Concurrency and integrity
161
+ #
162
+ # Using this validation method in conjunction with ActiveRecord::Base#save
163
+ # does not guarantee the absence of duplicate record insertions, because
164
+ # uniqueness checks on the application level are inherently prone to race
165
+ # conditions. For example, suppose that two users try to post a Comment at
166
+ # the same time, and a Comment's title must be unique. At the database-level,
167
+ # the actions performed by these users could be interleaved in the following manner:
168
+ #
169
+ # User 1 | User 2
170
+ # ------------------------------------+--------------------------------------
171
+ # # User 1 checks whether there's |
172
+ # # already a comment with the title |
173
+ # # 'My Post'. This is not the case. |
174
+ # SELECT * FROM comments |
175
+ # WHERE title = 'My Post' |
176
+ # |
177
+ # | # User 2 does the same thing and also
178
+ # | # infers that their title is unique.
179
+ # | SELECT * FROM comments
180
+ # | WHERE title = 'My Post'
181
+ # |
182
+ # # User 1 inserts their comment. |
183
+ # INSERT INTO comments |
184
+ # (title, content) VALUES |
185
+ # ('My Post', 'hi!') |
186
+ # |
187
+ # | # User 2 does the same thing.
188
+ # | INSERT INTO comments
189
+ # | (title, content) VALUES
190
+ # | ('My Post', 'hello!')
191
+ # |
192
+ # | # ^^^^^^
193
+ # | # Boom! We now have a duplicate
194
+ # | # title!
195
+ #
196
+ # This could even happen if you use transactions with the 'serializable'
197
+ # isolation level. The best way to work around this problem is to add a unique
198
+ # index to the database table using
199
+ # ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the
200
+ # rare case that a race condition occurs, the database will guarantee
201
+ # the field's uniqueness.
202
+ #
203
+ # When the database catches such a duplicate insertion,
204
+ # ActiveRecord::Base#save will raise an ActiveRecord::StatementInvalid
205
+ # exception. You can either choose to let this error propagate (which
206
+ # will result in the default Rails exception page being shown), or you
207
+ # can catch it and restart the transaction (e.g. by telling the user
208
+ # that the title already exists, and asking them to re-enter the title).
209
+ # This technique is also known as
210
+ # {optimistic concurrency control}[http://en.wikipedia.org/wiki/Optimistic_concurrency_control].
211
+ #
212
+ # The bundled ActiveRecord::ConnectionAdapters distinguish unique index
213
+ # constraint errors from other types of database errors by throwing an
214
+ # ActiveRecord::RecordNotUnique exception. For other adapters you will
215
+ # have to parse the (database-specific) exception message to detect such
216
+ # a case.
217
+ #
218
+ # The following bundled adapters throw the ActiveRecord::RecordNotUnique exception:
219
+ #
220
+ # * ActiveRecord::ConnectionAdapters::MysqlAdapter.
221
+ # * ActiveRecord::ConnectionAdapters::Mysql2Adapter.
222
+ # * ActiveRecord::ConnectionAdapters::SQLite3Adapter.
223
+ # * ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.
224
+ def validates_uniqueness_of(*attr_names)
225
+ validates_with UniquenessValidator, _merge_attributes(attr_names)
226
+ end
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'gem_version'
2
+
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
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ require 'rails/generators/named_base'
2
+ require 'rails/generators/active_model'
3
+ require 'rails/generators/active_record/migration'
4
+ require 'active_record'
5
+
6
+ module ActiveRecord
7
+ module Generators # :nodoc:
8
+ class Base < Rails::Generators::NamedBase # :nodoc:
9
+ include ActiveRecord::Generators::Migration
10
+
11
+ # Set the current directory as base for the inherited generators.
12
+ def self.base_root
13
+ File.dirname(__FILE__)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ require 'rails/generators/migration'
2
+
3
+ module ActiveRecord
4
+ module Generators # :nodoc:
5
+ module Migration
6
+ extend ActiveSupport::Concern
7
+ include Rails::Generators::Migration
8
+
9
+ module ClassMethods
10
+ # Implement the required interface for Rails::Generators::Migration.
11
+ def next_migration_number(dirname)
12
+ next_migration_number = current_migration_number(dirname) + 1
13
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,70 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ module ActiveRecord
4
+ module Generators # :nodoc:
5
+ class MigrationGenerator < Base # :nodoc:
6
+ argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]"
7
+
8
+ def create_migration_file
9
+ set_local_assigns!
10
+ validate_file_name!
11
+ migration_template @migration_template, "db/migrate/#{file_name}.rb"
12
+ end
13
+
14
+ protected
15
+ attr_reader :migration_action, :join_tables
16
+
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)
62
+ end
63
+ end
64
+
65
+ def normalize_table_name(_table_name)
66
+ pluralize_table_names? ? _table_name.pluralize : _table_name.singularize
67
+ end
68
+ end
69
+ end
70
+ end