activerecord 4.1.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +776 -1330
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +14 -13
- data/lib/active_record/associations/association.rb +2 -2
- data/lib/active_record/associations/association_scope.rb +83 -43
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +66 -29
- data/lib/active_record/associations/collection_proxy.rb +22 -26
- data/lib/active_record/associations/has_many_association.rb +65 -18
- data/lib/active_record/associations/has_many_through_association.rb +55 -27
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +20 -12
- data/lib/active_record/associations/preloader/association.rb +34 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +49 -59
- data/lib/active_record/associations/singular_association.rb +25 -4
- data/lib/active_record/associations/through_association.rb +23 -14
- data/lib/active_record/associations.rb +171 -42
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +18 -10
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +98 -44
- data/lib/active_record/attribute_methods/primary_key.rb +14 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +37 -147
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
- data/lib/active_record/attribute_methods/write.rb +14 -21
- data/lib/active_record/attribute_methods.rb +67 -94
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +45 -38
- data/lib/active_record/base.rb +10 -20
- data/lib/active_record/callbacks.rb +7 -7
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
- data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
- data/lib/active_record/connection_adapters/column.rb +28 -239
- data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
- data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -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 +14 -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 +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -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 +15 -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 +97 -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 +29 -374
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
- data/lib/active_record/connection_handling.rb +3 -3
- data/lib/active_record/core.rb +143 -32
- data/lib/active_record/counter_cache.rb +60 -7
- data/lib/active_record/enum.rb +10 -11
- data/lib/active_record/errors.rb +49 -27
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +56 -70
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +35 -17
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +52 -49
- data/lib/active_record/model_schema.rb +49 -57
- data/lib/active_record/nested_attributes.rb +7 -7
- data/lib/active_record/null_relation.rb +19 -5
- data/lib/active_record/persistence.rb +50 -31
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +14 -11
- data/lib/active_record/railties/databases.rake +56 -54
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +286 -102
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +39 -31
- data/lib/active_record/relation/delegation.rb +2 -2
- data/lib/active_record/relation/finder_methods.rb +80 -36
- data/lib/active_record/relation/merger.rb +25 -30
- data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +11 -10
- data/lib/active_record/relation/query_methods.rb +141 -55
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +69 -30
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +58 -26
- data/lib/active_record/schema_migration.rb +11 -0
- data/lib/active_record/scoping/default.rb +8 -7
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +19 -10
- data/lib/active_record/tasks/database_tasks.rb +73 -7
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +11 -9
- data/lib/active_record/transactions.rb +37 -21
- 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 +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -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 +17 -0
- data/lib/active_record/type/integer.rb +55 -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 +56 -0
- data/lib/active_record/type/string.rb +36 -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 +101 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +6 -4
- data/lib/active_record/validations/uniqueness.rb +11 -17
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +3 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +65 -10
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -0,0 +1,56 @@
|
|
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 changed_in_place?(raw_old_value, value)
|
31
|
+
return false if value.nil?
|
32
|
+
subtype.changed_in_place?(raw_old_value, coder.dump(value))
|
33
|
+
end
|
34
|
+
|
35
|
+
def accessor
|
36
|
+
ActiveRecord::Store::IndifferentHashAccessor
|
37
|
+
end
|
38
|
+
|
39
|
+
def init_with(coder)
|
40
|
+
@coder = coder['coder']
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
def encode_with(coder)
|
45
|
+
coder['coder'] = @coder
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def default_value?(value)
|
52
|
+
value == coder.load(nil)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,36 @@
|
|
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
|
+
private
|
25
|
+
|
26
|
+
def cast_value(value)
|
27
|
+
case value
|
28
|
+
when true then "t"
|
29
|
+
when false then "f"
|
30
|
+
# String.new is slightly faster than dup
|
31
|
+
else ::String.new(value.to_s)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
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,101 @@
|
|
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 number? # :nodoc:
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
def binary? # :nodoc:
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
61
|
+
def klass # :nodoc:
|
62
|
+
end
|
63
|
+
|
64
|
+
# Determines whether a value has changed for dirty checking. +old_value+
|
65
|
+
# and +new_value+ will always be type-cast. Types should not need to
|
66
|
+
# override this method.
|
67
|
+
def changed?(old_value, new_value, _new_value_before_type_cast)
|
68
|
+
old_value != new_value
|
69
|
+
end
|
70
|
+
|
71
|
+
# Determines whether the mutable value has been modified since it was
|
72
|
+
# read. Returns +false+ by default. This method should not be overridden
|
73
|
+
# directly. Types which return a mutable value should include
|
74
|
+
# +Type::Mutable+, which will define this method.
|
75
|
+
def changed_in_place?(*)
|
76
|
+
false
|
77
|
+
end
|
78
|
+
|
79
|
+
def ==(other)
|
80
|
+
self.class == other.class &&
|
81
|
+
precision == other.precision &&
|
82
|
+
scale == other.scale &&
|
83
|
+
limit == other.limit
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def type_cast(value)
|
89
|
+
cast_value(value) unless value.nil?
|
90
|
+
end
|
91
|
+
|
92
|
+
# Convenience method for types which do not need separate type casting
|
93
|
+
# behavior for user and database inputs. Called by
|
94
|
+
# `type_cast_from_database` and `type_cast_from_user` for all values
|
95
|
+
# except `nil`.
|
96
|
+
def cast_value(value) # :doc:
|
97
|
+
value
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
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'
|
@@ -29,9 +29,11 @@ module ActiveRecord
|
|
29
29
|
# Configuration options:
|
30
30
|
#
|
31
31
|
# * <tt>:message</tt> - A custom error message (default is: "is invalid").
|
32
|
-
# * <tt>:on</tt> - Specifies
|
33
|
-
# validation contexts by default (
|
34
|
-
#
|
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>)
|
35
37
|
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
|
36
38
|
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
|
37
39
|
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
|
@@ -4,7 +4,7 @@ module ActiveRecord
|
|
4
4
|
def validate(record)
|
5
5
|
super
|
6
6
|
attributes.each do |attribute|
|
7
|
-
next unless record.class.
|
7
|
+
next unless record.class._reflect_on_association(attribute)
|
8
8
|
associated_records = Array.wrap(record.send(attribute))
|
9
9
|
|
10
10
|
# Superclass validates presence. Ensure present records aren't about to be destroyed.
|
@@ -44,9 +44,11 @@ module ActiveRecord
|
|
44
44
|
#
|
45
45
|
# Configuration options:
|
46
46
|
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
|
47
|
-
# * <tt>:on</tt> - Specifies
|
48
|
-
# validation contexts by default (
|
49
|
-
#
|
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>)
|
50
52
|
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
|
51
53
|
# the validation should occur (e.g. <tt>if: :allow_validation</tt>, or
|
52
54
|
# <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
|
@@ -13,8 +13,7 @@ module ActiveRecord
|
|
13
13
|
def validate_each(record, attribute, value)
|
14
14
|
finder_class = find_finder_class_for(record)
|
15
15
|
table = finder_class.arel_table
|
16
|
-
value = map_enum_attribute(finder_class,attribute,value)
|
17
|
-
value = deserialize_attribute(record, attribute, value)
|
16
|
+
value = map_enum_attribute(finder_class, attribute, value)
|
18
17
|
|
19
18
|
relation = build_relation(finder_class, table, attribute, value)
|
20
19
|
relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id)) if record.persisted?
|
@@ -47,9 +46,9 @@ module ActiveRecord
|
|
47
46
|
end
|
48
47
|
|
49
48
|
def build_relation(klass, table, attribute, value) #:nodoc:
|
50
|
-
if reflection = klass.
|
49
|
+
if reflection = klass._reflect_on_association(attribute)
|
51
50
|
attribute = reflection.foreign_key
|
52
|
-
value = value.attributes[reflection.
|
51
|
+
value = value.attributes[reflection.klass.primary_key] unless value.nil?
|
53
52
|
end
|
54
53
|
|
55
54
|
attribute_name = attribute.to_s
|
@@ -62,24 +61,25 @@ module ActiveRecord
|
|
62
61
|
|
63
62
|
column = klass.columns_hash[attribute_name]
|
64
63
|
value = klass.connection.type_cast(value, column)
|
65
|
-
|
64
|
+
if value.is_a?(String) && column.limit
|
65
|
+
value = value.to_s[0, column.limit]
|
66
|
+
end
|
66
67
|
|
67
|
-
if !options[:case_sensitive] && value
|
68
|
+
if !options[:case_sensitive] && value.is_a?(String)
|
68
69
|
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation
|
69
70
|
klass.connection.case_insensitive_comparison(table, attribute, column, value)
|
70
71
|
else
|
71
|
-
|
72
|
-
table[attribute].eq(value)
|
72
|
+
klass.connection.case_sensitive_comparison(table, attribute, column, value)
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
76
|
def scope_relation(record, table, relation)
|
77
77
|
Array(options[:scope]).each do |scope_item|
|
78
|
-
if reflection = record.class.
|
78
|
+
if reflection = record.class._reflect_on_association(scope_item)
|
79
79
|
scope_value = record.send(reflection.foreign_key)
|
80
80
|
scope_item = reflection.foreign_key
|
81
81
|
else
|
82
|
-
scope_value = record.
|
82
|
+
scope_value = record._read_attribute(scope_item)
|
83
83
|
end
|
84
84
|
relation = relation.and(table[scope_item].eq(scope_value))
|
85
85
|
end
|
@@ -87,12 +87,6 @@ module ActiveRecord
|
|
87
87
|
relation
|
88
88
|
end
|
89
89
|
|
90
|
-
def deserialize_attribute(record, attribute, value)
|
91
|
-
coder = record.class.serialized_attributes[attribute.to_s]
|
92
|
-
value = coder.dump value if value && coder
|
93
|
-
value
|
94
|
-
end
|
95
|
-
|
96
90
|
def map_enum_attribute(klass, attribute, value)
|
97
91
|
mapping = klass.defined_enums[attribute.to_s]
|
98
92
|
value = mapping[value] if value && mapping
|
@@ -158,7 +152,7 @@ module ActiveRecord
|
|
158
152
|
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
|
159
153
|
# proc or string should return or evaluate to a +true+ or +false+ value.
|
160
154
|
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
|
161
|
-
# determine if the validation should
|
155
|
+
# determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
|
162
156
|
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
|
163
157
|
# method, proc or string should return or evaluate to a +true+ or +false+
|
164
158
|
# value.
|
@@ -5,13 +5,14 @@ module ActiveRecord
|
|
5
5
|
# +record+ method to retrieve the record which did not validate.
|
6
6
|
#
|
7
7
|
# begin
|
8
|
-
#
|
8
|
+
# complex_operation_that_internally_calls_save!
|
9
9
|
# rescue ActiveRecord::RecordInvalid => invalid
|
10
10
|
# puts invalid.record.errors
|
11
11
|
# end
|
12
12
|
class RecordInvalid < ActiveRecordError
|
13
|
-
attr_reader :record
|
14
|
-
|
13
|
+
attr_reader :record
|
14
|
+
|
15
|
+
def initialize(record)
|
15
16
|
@record = record
|
16
17
|
errors = @record.errors.full_messages.join(", ")
|
17
18
|
super(I18n.t(:"#{@record.class.i18n_scope}.errors.messages.record_invalid", :errors => errors, :default => :"errors.messages.record_invalid"))
|
@@ -29,21 +30,6 @@ module ActiveRecord
|
|
29
30
|
extend ActiveSupport::Concern
|
30
31
|
include ActiveModel::Validations
|
31
32
|
|
32
|
-
module ClassMethods
|
33
|
-
# Creates an object just like Base.create but calls <tt>save!</tt> instead of +save+
|
34
|
-
# so an exception is raised if the record is invalid.
|
35
|
-
def create!(attributes = nil, &block)
|
36
|
-
if attributes.is_a?(Array)
|
37
|
-
attributes.collect { |attr| create!(attr, &block) }
|
38
|
-
else
|
39
|
-
object = new(attributes)
|
40
|
-
yield(object) if block_given?
|
41
|
-
object.save!
|
42
|
-
object
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
33
|
# The validation process on save can be skipped by passing <tt>validate: false</tt>.
|
48
34
|
# The regular Base#save method is replaced with this when the validations
|
49
35
|
# module is mixed in, which it is by default.
|
@@ -54,12 +40,14 @@ module ActiveRecord
|
|
54
40
|
# Attempts to save the record just like Base#save but will raise a +RecordInvalid+
|
55
41
|
# exception instead of returning +false+ if the record is not valid.
|
56
42
|
def save!(options={})
|
57
|
-
perform_validations(options) ? super :
|
43
|
+
perform_validations(options) ? super : raise_record_invalid
|
58
44
|
end
|
59
45
|
|
60
46
|
# Runs all the validations within the specified context. Returns +true+ if
|
61
47
|
# no errors are found, +false+ otherwise.
|
62
48
|
#
|
49
|
+
# Aliased as validate.
|
50
|
+
#
|
63
51
|
# If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
|
64
52
|
# <tt>new_record?</tt> is +true+, and to <tt>:update</tt> if it is not.
|
65
53
|
#
|
@@ -71,8 +59,26 @@ module ActiveRecord
|
|
71
59
|
errors.empty? && output
|
72
60
|
end
|
73
61
|
|
62
|
+
alias_method :validate, :valid?
|
63
|
+
|
64
|
+
# Runs all the validations within the specified context. Returns +true+ if
|
65
|
+
# no errors are found, raises +RecordInvalid+ otherwise.
|
66
|
+
#
|
67
|
+
# If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
|
68
|
+
# <tt>new_record?</tt> is +true+, and to <tt>:update</tt> if it is not.
|
69
|
+
#
|
70
|
+
# Validations with no <tt>:on</tt> option will run no matter the context. Validations with
|
71
|
+
# some <tt>:on</tt> option will only run in the specified context.
|
72
|
+
def validate!(context = nil)
|
73
|
+
valid?(context) || raise_record_invalid
|
74
|
+
end
|
75
|
+
|
74
76
|
protected
|
75
77
|
|
78
|
+
def raise_record_invalid
|
79
|
+
raise(RecordInvalid.new(self))
|
80
|
+
end
|
81
|
+
|
76
82
|
def perform_validations(options={}) # :nodoc:
|
77
83
|
options[:validate] == false || valid?(options[:context])
|
78
84
|
end
|
data/lib/active_record.rb
CHANGED
@@ -27,10 +27,12 @@ require 'active_model'
|
|
27
27
|
require 'arel'
|
28
28
|
|
29
29
|
require 'active_record/version'
|
30
|
+
require 'active_record/attribute_set'
|
30
31
|
|
31
32
|
module ActiveRecord
|
32
33
|
extend ActiveSupport::Autoload
|
33
34
|
|
35
|
+
autoload :Attribute
|
34
36
|
autoload :Base
|
35
37
|
autoload :Callbacks
|
36
38
|
autoload :Core
|
@@ -95,6 +97,7 @@ module ActiveRecord
|
|
95
97
|
|
96
98
|
module Coders
|
97
99
|
autoload :YAMLColumn, 'active_record/coders/yaml_column'
|
100
|
+
autoload :JSON, 'active_record/coders/json'
|
98
101
|
end
|
99
102
|
|
100
103
|
module AttributeMethods
|
@@ -23,16 +23,16 @@ module ActiveRecord
|
|
23
23
|
case file_name
|
24
24
|
when /^(add|remove)_.*_(?:to|from)_(.*)/
|
25
25
|
@migration_action = $1
|
26
|
-
@table_name = $2
|
26
|
+
@table_name = normalize_table_name($2)
|
27
27
|
when /join_table/
|
28
28
|
if attributes.length == 2
|
29
29
|
@migration_action = 'join'
|
30
|
-
@join_tables = attributes.map(&:plural_name)
|
30
|
+
@join_tables = pluralize_table_names? ? attributes.map(&:plural_name) : attributes.map(&:singular_name)
|
31
31
|
|
32
32
|
set_index_names
|
33
33
|
end
|
34
34
|
when /^create_(.+)/
|
35
|
-
@table_name = $1
|
35
|
+
@table_name = normalize_table_name($1)
|
36
36
|
@migration_template = "create_table_migration.rb"
|
37
37
|
end
|
38
38
|
end
|
@@ -55,12 +55,16 @@ module ActiveRecord
|
|
55
55
|
def attributes_with_index
|
56
56
|
attributes.select { |a| !a.reference? && a.has_index? }
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
def validate_file_name!
|
60
60
|
unless file_name =~ /^[_a-z0-9]+$/
|
61
61
|
raise IllegalMigrationNameError.new(file_name)
|
62
62
|
end
|
63
63
|
end
|
64
|
+
|
65
|
+
def normalize_table_name(_table_name)
|
66
|
+
pluralize_table_names? ? _table_name.pluralize : _table_name.singularize
|
67
|
+
end
|
64
68
|
end
|
65
69
|
end
|
66
70
|
end
|
@@ -9,11 +9,14 @@ class <%= migration_class_name %> < ActiveRecord::Migration
|
|
9
9
|
<% end -%>
|
10
10
|
<% end -%>
|
11
11
|
<% if options[:timestamps] %>
|
12
|
-
t.timestamps
|
12
|
+
t.timestamps null: false
|
13
13
|
<% end -%>
|
14
14
|
end
|
15
15
|
<% attributes_with_index.each do |attribute| -%>
|
16
16
|
add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
|
17
|
+
<% end -%>
|
18
|
+
<% attributes.select(&:reference?).reject(&:polymorphic?).each do |attribute| -%>
|
19
|
+
add_foreign_key :<%= table_name %>, :<%= attribute.name.pluralize %>
|
17
20
|
<% end -%>
|
18
21
|
end
|
19
22
|
end
|
@@ -4,6 +4,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration
|
|
4
4
|
<% attributes.each do |attribute| -%>
|
5
5
|
<%- if attribute.reference? -%>
|
6
6
|
add_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %>
|
7
|
+
<%- unless attribute.polymorphic? -%>
|
8
|
+
add_foreign_key :<%= table_name %>, :<%= attribute.name.pluralize %>
|
9
|
+
<%- end -%>
|
7
10
|
<%- else -%>
|
8
11
|
add_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %>
|
9
12
|
<%- if attribute.has_index? -%>
|
@@ -26,6 +29,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration
|
|
26
29
|
<%- if migration_action -%>
|
27
30
|
<%- if attribute.reference? -%>
|
28
31
|
remove_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %>
|
32
|
+
<%- unless attribute.polymorphic? -%>
|
33
|
+
remove_foreign_key :<%= table_name %>, :<%= attribute.name.pluralize %>
|
34
|
+
<%- end -%>
|
29
35
|
<%- else -%>
|
30
36
|
<%- if attribute.has_index? -%>
|
31
37
|
remove_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
|