activerecord 3.2.22.5 → 4.2.11.3
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1632 -609
- data/MIT-LICENSE +1 -1
- data/README.rdoc +37 -41
- data/examples/performance.rb +31 -19
- data/examples/simple.rb +4 -4
- data/lib/active_record/aggregations.rb +56 -42
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -36
- data/lib/active_record/associations/association.rb +73 -55
- data/lib/active_record/associations/association_scope.rb +143 -82
- data/lib/active_record/associations/belongs_to_association.rb +65 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
- data/lib/active_record/associations/builder/association.rb +125 -31
- data/lib/active_record/associations/builder/belongs_to.rb +89 -61
- data/lib/active_record/associations/builder/collection_association.rb +69 -49
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +12 -51
- data/lib/active_record/associations/builder/singular_association.rb +23 -17
- data/lib/active_record/associations/collection_association.rb +251 -177
- data/lib/active_record/associations/collection_proxy.rb +963 -63
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +113 -22
- data/lib/active_record/associations/has_many_through_association.rb +99 -39
- data/lib/active_record/associations/has_one_association.rb +43 -20
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +76 -107
- data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
- data/lib/active_record/associations/join_dependency.rb +230 -156
- data/lib/active_record/associations/preloader/association.rb +96 -55
- data/lib/active_record/associations/preloader/collection_association.rb +3 -3
- data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +62 -33
- data/lib/active_record/associations/preloader.rb +101 -79
- data/lib/active_record/associations/singular_association.rb +29 -13
- data/lib/active_record/associations/through_association.rb +30 -16
- data/lib/active_record/associations.rb +463 -345
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +142 -151
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
- data/lib/active_record/attribute_methods/dirty.rb +137 -57
- data/lib/active_record/attribute_methods/primary_key.rb +50 -36
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +73 -106
- data/lib/active_record/attribute_methods/serialization.rb +44 -94
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -45
- data/lib/active_record/attribute_methods/write.rb +57 -44
- data/lib/active_record/attribute_methods.rb +301 -141
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +246 -217
- data/lib/active_record/base.rb +70 -474
- data/lib/active_record/callbacks.rb +66 -28
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +396 -219
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -164
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +29 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -55
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +261 -169
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +707 -259
- data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +298 -89
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +466 -196
- data/lib/active_record/connection_adapters/column.rb +31 -245
- data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +45 -57
- data/lib/active_record/connection_adapters/mysql_adapter.rb +180 -123
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +430 -999
- data/lib/active_record/connection_adapters/schema_cache.rb +52 -27
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +579 -22
- data/lib/active_record/connection_handling.rb +132 -0
- data/lib/active_record/core.rb +579 -0
- data/lib/active_record/counter_cache.rb +157 -105
- data/lib/active_record/dynamic_matchers.rb +119 -63
- data/lib/active_record/enum.rb +197 -0
- data/lib/active_record/errors.rb +94 -36
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +9 -5
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +302 -215
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +143 -70
- data/lib/active_record/integration.rb +65 -12
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +73 -52
- data/lib/active_record/locking/pessimistic.rb +5 -5
- data/lib/active_record/log_subscriber.rb +24 -21
- data/lib/active_record/migration/command_recorder.rb +124 -32
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +511 -213
- data/lib/active_record/model_schema.rb +91 -117
- data/lib/active_record/nested_attributes.rb +184 -130
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +81 -0
- data/lib/active_record/persistence.rb +276 -117
- data/lib/active_record/query_cache.rb +19 -37
- data/lib/active_record/querying.rb +28 -18
- data/lib/active_record/railtie.rb +73 -40
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +4 -3
- data/lib/active_record/railties/databases.rake +141 -416
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +1 -4
- data/lib/active_record/reflection.rb +513 -154
- data/lib/active_record/relation/batches.rb +91 -43
- data/lib/active_record/relation/calculations.rb +199 -161
- data/lib/active_record/relation/delegation.rb +116 -25
- data/lib/active_record/relation/finder_methods.rb +362 -248
- data/lib/active_record/relation/merger.rb +193 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/predicate_builder.rb +135 -43
- data/lib/active_record/relation/query_methods.rb +928 -167
- data/lib/active_record/relation/spawn_methods.rb +48 -149
- data/lib/active_record/relation.rb +352 -207
- data/lib/active_record/result.rb +101 -10
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +56 -59
- data/lib/active_record/schema.rb +19 -13
- data/lib/active_record/schema_dumper.rb +106 -63
- data/lib/active_record/schema_migration.rb +53 -0
- data/lib/active_record/scoping/default.rb +50 -57
- data/lib/active_record/scoping/named.rb +73 -109
- data/lib/active_record/scoping.rb +58 -123
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +12 -22
- data/lib/active_record/statement_cache.rb +111 -0
- data/lib/active_record/store.rb +168 -15
- data/lib/active_record/tasks/database_tasks.rb +299 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
- data/lib/active_record/timestamp.rb +23 -16
- data/lib/active_record/transactions.rb +125 -79
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +24 -16
- data/lib/active_record/validations/presence.rb +67 -0
- data/lib/active_record/validations/uniqueness.rb +123 -64
- data/lib/active_record/validations.rb +36 -29
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +66 -46
- data/lib/rails/generators/active_record/migration/migration_generator.rb +53 -8
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +5 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/migration.rb +11 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +101 -45
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- data/lib/active_record/associations/join_helper.rb +0 -55
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -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,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,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,6 +1,6 @@
|
|
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
5
|
if Array.wrap(value).reject {|r| r.marked_for_destruction? || r.valid?}.any?
|
6
6
|
record.errors.add(attribute, :invalid, options.merge(:value => value))
|
@@ -9,7 +9,8 @@ module ActiveRecord
|
|
9
9
|
end
|
10
10
|
|
11
11
|
module ClassMethods
|
12
|
-
# Validates whether the associated object or objects are all valid
|
12
|
+
# Validates whether the associated object or objects are all valid.
|
13
|
+
# Works with any kind of association.
|
13
14
|
#
|
14
15
|
# class Book < ActiveRecord::Base
|
15
16
|
# has_many :pages
|
@@ -18,23 +19,30 @@ module ActiveRecord
|
|
18
19
|
# validates_associated :pages, :library
|
19
20
|
# end
|
20
21
|
#
|
21
|
-
# WARNING: This validation must not be used on both ends of an association.
|
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.
|
22
24
|
#
|
23
|
-
# NOTE: This validation will not fail if the association hasn't been
|
24
|
-
# ensure that the association is both present and
|
25
|
-
# 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+.
|
26
28
|
#
|
27
29
|
# Configuration options:
|
28
|
-
#
|
29
|
-
# * <tt>:
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# * <tt>:
|
36
|
-
#
|
37
|
-
#
|
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.
|
38
46
|
def validates_associated(*attr_names)
|
39
47
|
validates_with AssociatedValidator, _merge_attributes(attr_names)
|
40
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
|