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
@@ -0,0 +1,23 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class HashLookupTypeMap < TypeMap # :nodoc:
4
+ def alias_type(type, alias_type)
5
+ register_type(type) { |_, *args| lookup(alias_type, *args) }
6
+ end
7
+
8
+ def key?(key)
9
+ @mapping.key?(key)
10
+ end
11
+
12
+ def keys
13
+ @mapping.keys
14
+ end
15
+
16
+ private
17
+
18
+ def perform_fetch(type, *args, &block)
19
+ @mapping.fetch(type, block).call(type, *args)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,59 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class Integer < Value # :nodoc:
4
+ include Numeric
5
+
6
+ def initialize(*)
7
+ super
8
+ @range = min_value...max_value
9
+ end
10
+
11
+ def type
12
+ :integer
13
+ end
14
+
15
+ def type_cast_from_database(value)
16
+ return if value.nil?
17
+ value.to_i
18
+ end
19
+
20
+ def type_cast_for_database(value)
21
+ result = type_cast(value)
22
+ if result
23
+ ensure_in_range(result)
24
+ end
25
+ result
26
+ end
27
+
28
+ protected
29
+
30
+ attr_reader :range
31
+
32
+ private
33
+
34
+ def cast_value(value)
35
+ case value
36
+ when true then 1
37
+ when false then 0
38
+ else
39
+ value.to_i rescue nil
40
+ end
41
+ end
42
+
43
+ def ensure_in_range(value)
44
+ unless range.cover?(value)
45
+ raise RangeError, "#{value} is out of range for #{self.class} with limit #{limit || 4}"
46
+ end
47
+ end
48
+
49
+ def max_value
50
+ limit = self.limit || 4
51
+ 1 << (limit * 8 - 1) # 8 bits per byte with one bit for sign
52
+ end
53
+
54
+ def min_value
55
+ -max_value
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,16 @@
1
+ module ActiveRecord
2
+ module Type
3
+ module Mutable # :nodoc:
4
+ def type_cast_from_user(value)
5
+ type_cast_from_database(type_cast_for_database(value))
6
+ end
7
+
8
+ # +raw_old_value+ will be the `_before_type_cast` version of the
9
+ # value (likely a string). +new_value+ will be the current, type
10
+ # cast value.
11
+ def changed_in_place?(raw_old_value, new_value)
12
+ raw_old_value != type_cast_for_database(new_value)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ module ActiveRecord
2
+ module Type
3
+ module Numeric # :nodoc:
4
+ def number?
5
+ true
6
+ end
7
+
8
+ def type_cast(value)
9
+ value = case value
10
+ when true then 1
11
+ when false then 0
12
+ when ::String then value.presence
13
+ else value
14
+ end
15
+ super(value)
16
+ end
17
+
18
+ def changed?(old_value, _new_value, new_value_before_type_cast) # :nodoc:
19
+ super || number_to_non_number?(old_value, new_value_before_type_cast)
20
+ end
21
+
22
+ private
23
+
24
+ def number_to_non_number?(old_value, new_value_before_type_cast)
25
+ old_value != nil && non_numeric_string?(new_value_before_type_cast)
26
+ end
27
+
28
+ def non_numeric_string?(value)
29
+ # 'wibble'.to_i will give zero, we want to make sure
30
+ # that we aren't marking int zero to string zero as
31
+ # changed.
32
+ value.to_s !~ /\A-?\d+\.?\d*\z/
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,62 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class Serialized < DelegateClass(Type::Value) # :nodoc:
4
+ include Mutable
5
+ include Decorator
6
+
7
+ attr_reader :subtype, :coder
8
+
9
+ def initialize(subtype, coder)
10
+ @subtype = subtype
11
+ @coder = coder
12
+ super(subtype)
13
+ end
14
+
15
+ def type_cast_from_database(value)
16
+ if default_value?(value)
17
+ value
18
+ else
19
+ coder.load(super)
20
+ end
21
+ end
22
+
23
+ def type_cast_for_database(value)
24
+ return if value.nil?
25
+ unless default_value?(value)
26
+ super coder.dump(value)
27
+ end
28
+ end
29
+
30
+ def inspect
31
+ Kernel.instance_method(:inspect).bind(self).call
32
+ end
33
+
34
+ def changed_in_place?(raw_old_value, value)
35
+ return false if value.nil?
36
+ raw_new_value = type_cast_for_database(value)
37
+ raw_old_value.nil? != raw_new_value.nil? ||
38
+ subtype.changed_in_place?(raw_old_value, raw_new_value)
39
+ end
40
+
41
+ def accessor
42
+ ActiveRecord::Store::IndifferentHashAccessor
43
+ end
44
+
45
+ def init_with(coder)
46
+ @coder = coder['coder']
47
+ super
48
+ end
49
+
50
+ def encode_with(coder)
51
+ coder['coder'] = @coder
52
+ super
53
+ end
54
+
55
+ private
56
+
57
+ def default_value?(value)
58
+ value == coder.load(nil)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,40 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class String < Value # :nodoc:
4
+ def type
5
+ :string
6
+ end
7
+
8
+ def changed_in_place?(raw_old_value, new_value)
9
+ if new_value.is_a?(::String)
10
+ raw_old_value != new_value
11
+ end
12
+ end
13
+
14
+ def type_cast_for_database(value)
15
+ case value
16
+ when ::Numeric, ActiveSupport::Duration then value.to_s
17
+ when ::String then ::String.new(value)
18
+ when true then "t"
19
+ when false then "f"
20
+ else super
21
+ end
22
+ end
23
+
24
+ def text?
25
+ true
26
+ end
27
+
28
+ private
29
+
30
+ def cast_value(value)
31
+ case value
32
+ when true then "t"
33
+ when false then "f"
34
+ # String.new is slightly faster than dup
35
+ else ::String.new(value.to_s)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,11 @@
1
+ require 'active_record/type/string'
2
+
3
+ module ActiveRecord
4
+ module Type
5
+ class Text < String # :nodoc:
6
+ def type
7
+ :text
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class Time < Value # :nodoc:
4
+ include TimeValue
5
+
6
+ def type
7
+ :time
8
+ end
9
+
10
+ private
11
+
12
+ def cast_value(value)
13
+ return value unless value.is_a?(::String)
14
+ return if value.empty?
15
+
16
+ dummy_time_value = "2000-01-01 #{value}"
17
+
18
+ fast_string_to_time(dummy_time_value) || begin
19
+ time_hash = ::Date._parse(dummy_time_value)
20
+ return if time_hash[:hour].nil?
21
+ new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,38 @@
1
+ module ActiveRecord
2
+ module Type
3
+ module TimeValue # :nodoc:
4
+ def klass
5
+ ::Time
6
+ end
7
+
8
+ def type_cast_for_schema(value)
9
+ "'#{value.to_s(:db)}'"
10
+ end
11
+
12
+ private
13
+
14
+ def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil)
15
+ # Treat 0000-00-00 00:00:00 as nil.
16
+ return if year.nil? || (year == 0 && mon == 0 && mday == 0)
17
+
18
+ if offset
19
+ time = ::Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
20
+ return unless time
21
+
22
+ time -= offset
23
+ Base.default_timezone == :utc ? time : time.getlocal
24
+ else
25
+ ::Time.public_send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
26
+ end
27
+ end
28
+
29
+ # Doesn't handle time zones.
30
+ def fast_string_to_time(string)
31
+ if string =~ ConnectionAdapters::Column::Format::ISO_DATETIME
32
+ microsec = ($7.to_r * 1_000_000).to_i
33
+ new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,64 @@
1
+ require 'thread_safe'
2
+
3
+ module ActiveRecord
4
+ module Type
5
+ class TypeMap # :nodoc:
6
+ def initialize
7
+ @mapping = {}
8
+ @cache = ThreadSafe::Cache.new do |h, key|
9
+ h.fetch_or_store(key, ThreadSafe::Cache.new)
10
+ end
11
+ end
12
+
13
+ def lookup(lookup_key, *args)
14
+ fetch(lookup_key, *args) { default_value }
15
+ end
16
+
17
+ def fetch(lookup_key, *args, &block)
18
+ @cache[lookup_key].fetch_or_store(args) do
19
+ perform_fetch(lookup_key, *args, &block)
20
+ end
21
+ end
22
+
23
+ def register_type(key, value = nil, &block)
24
+ raise ::ArgumentError unless value || block
25
+ @cache.clear
26
+
27
+ if block
28
+ @mapping[key] = block
29
+ else
30
+ @mapping[key] = proc { value }
31
+ end
32
+ end
33
+
34
+ def alias_type(key, target_key)
35
+ register_type(key) do |sql_type, *args|
36
+ metadata = sql_type[/\(.*\)/, 0]
37
+ lookup("#{target_key}#{metadata}", *args)
38
+ end
39
+ end
40
+
41
+ def clear
42
+ @mapping.clear
43
+ end
44
+
45
+ private
46
+
47
+ def perform_fetch(lookup_key, *args)
48
+ matching_pair = @mapping.reverse_each.detect do |key, _|
49
+ key === lookup_key
50
+ end
51
+
52
+ if matching_pair
53
+ matching_pair.last.call(lookup_key, *args)
54
+ else
55
+ yield lookup_key, *args
56
+ end
57
+ end
58
+
59
+ def default_value
60
+ @default_value ||= Value.new
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class UnsignedInteger < Integer # :nodoc:
4
+ private
5
+
6
+ def max_value
7
+ super * 2
8
+ end
9
+
10
+ def min_value
11
+ 0
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,110 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class Value # :nodoc:
4
+ attr_reader :precision, :scale, :limit
5
+
6
+ # Valid options are +precision+, +scale+, and +limit+. They are only
7
+ # used when dumping schema.
8
+ def initialize(options = {})
9
+ options.assert_valid_keys(:precision, :scale, :limit)
10
+ @precision = options[:precision]
11
+ @scale = options[:scale]
12
+ @limit = options[:limit]
13
+ end
14
+
15
+ # The simplified type that this object represents. Returns a symbol such
16
+ # as +:string+ or +:integer+
17
+ def type; end
18
+
19
+ # Type casts a string from the database into the appropriate ruby type.
20
+ # Classes which do not need separate type casting behavior for database
21
+ # and user provided values should override +cast_value+ instead.
22
+ def type_cast_from_database(value)
23
+ type_cast(value)
24
+ end
25
+
26
+ # Type casts a value from user input (e.g. from a setter). This value may
27
+ # be a string from the form builder, or an already type cast value
28
+ # provided manually to a setter.
29
+ #
30
+ # Classes which do not need separate type casting behavior for database
31
+ # and user provided values should override +type_cast+ or +cast_value+
32
+ # instead.
33
+ def type_cast_from_user(value)
34
+ type_cast(value)
35
+ end
36
+
37
+ # Cast a value from the ruby type to a type that the database knows how
38
+ # to understand. The returned value from this method should be a
39
+ # +String+, +Numeric+, +Date+, +Time+, +Symbol+, +true+, +false+, or
40
+ # +nil+
41
+ def type_cast_for_database(value)
42
+ value
43
+ end
44
+
45
+ # Type cast a value for schema dumping. This method is private, as we are
46
+ # hoping to remove it entirely.
47
+ def type_cast_for_schema(value) # :nodoc:
48
+ value.inspect
49
+ end
50
+
51
+ # These predicates are not documented, as I need to look further into
52
+ # their use, and see if they can be removed entirely.
53
+ def text? # :nodoc:
54
+ false
55
+ end
56
+
57
+ def number? # :nodoc:
58
+ false
59
+ end
60
+
61
+ def binary? # :nodoc:
62
+ false
63
+ end
64
+
65
+ def klass # :nodoc:
66
+ end
67
+
68
+ # Determines whether a value has changed for dirty checking. +old_value+
69
+ # and +new_value+ will always be type-cast. Types should not need to
70
+ # override this method.
71
+ def changed?(old_value, new_value, _new_value_before_type_cast)
72
+ old_value != new_value
73
+ end
74
+
75
+ # Determines whether the mutable value has been modified since it was
76
+ # read. Returns +false+ by default. This method should not be overridden
77
+ # directly. Types which return a mutable value should include
78
+ # +Type::Mutable+, which will define this method.
79
+ def changed_in_place?(*)
80
+ false
81
+ end
82
+
83
+ def ==(other)
84
+ self.class == other.class &&
85
+ precision == other.precision &&
86
+ scale == other.scale &&
87
+ limit == other.limit
88
+ end
89
+ alias eql? ==
90
+
91
+ def hash
92
+ [self.class, precision, scale, limit].hash
93
+ end
94
+
95
+ private
96
+
97
+ def type_cast(value)
98
+ cast_value(value) unless value.nil?
99
+ end
100
+
101
+ # Convenience method for types which do not need separate type casting
102
+ # behavior for user and database inputs. Called by
103
+ # `type_cast_from_database` and `type_cast_from_user` for all values
104
+ # except `nil`.
105
+ def cast_value(value) # :doc:
106
+ value
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,23 @@
1
+ require 'active_record/type/decorator'
2
+ require 'active_record/type/mutable'
3
+ require 'active_record/type/numeric'
4
+ require 'active_record/type/time_value'
5
+ require 'active_record/type/value'
6
+
7
+ require 'active_record/type/big_integer'
8
+ require 'active_record/type/binary'
9
+ require 'active_record/type/boolean'
10
+ require 'active_record/type/date'
11
+ require 'active_record/type/date_time'
12
+ require 'active_record/type/decimal'
13
+ require 'active_record/type/decimal_without_scale'
14
+ require 'active_record/type/float'
15
+ require 'active_record/type/integer'
16
+ require 'active_record/type/serialized'
17
+ require 'active_record/type/string'
18
+ require 'active_record/type/text'
19
+ require 'active_record/type/time'
20
+ require 'active_record/type/unsigned_integer'
21
+
22
+ require 'active_record/type/type_map'
23
+ require 'active_record/type/hash_lookup_type_map'
@@ -1,14 +1,16 @@
1
1
  module ActiveRecord
2
2
  module Validations
3
- class AssociatedValidator < ActiveModel::EachValidator
3
+ class AssociatedValidator < ActiveModel::EachValidator #:nodoc:
4
4
  def validate_each(record, attribute, value)
5
- return if (value.is_a?(Array) ? value : [value]).collect{ |r| r.nil? || r.valid? }.all?
6
- record.errors.add(attribute, :invalid, options.merge(:value => 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
7
8
  end
8
9
  end
9
10
 
10
11
  module ClassMethods
11
- # Validates whether the associated object or objects are all valid themselves. Works with any kind of association.
12
+ # Validates whether the associated object or objects are all valid.
13
+ # Works with any kind of association.
12
14
  #
13
15
  # class Book < ActiveRecord::Base
14
16
  # has_many :pages
@@ -17,23 +19,30 @@ module ActiveRecord
17
19
  # validates_associated :pages, :library
18
20
  # end
19
21
  #
20
- # WARNING: This validation must not be used on both ends of an association. Doing so will lead to a circular dependency and cause infinite recursion.
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.
21
24
  #
22
- # NOTE: This validation will not fail if the association hasn't been assigned. If you want to
23
- # ensure that the association is both present and guaranteed to be valid, you also need to
24
- # use +validates_presence_of+.
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+.
25
28
  #
26
29
  # Configuration options:
27
- # * <tt>:message</tt> - A custom error message (default is: "is invalid")
28
- # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
29
- # validation contexts by default (+nil+), other options are <tt>:create</tt>
30
- # and <tt>:update</tt>.
31
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
32
- # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
33
- # method, proc or string should return or evaluate to a true or false value.
34
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
35
- # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
36
- # method, proc or string should return or evaluate to a true or false value.
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.
37
46
  def validates_associated(*attr_names)
38
47
  validates_with AssociatedValidator, _merge_attributes(attr_names)
39
48
  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