activerecord 3.2.22.5 → 5.2.8
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 +657 -621
- data/MIT-LICENSE +2 -2
- data/README.rdoc +41 -46
- data/examples/performance.rb +55 -42
- data/examples/simple.rb +6 -5
- data/lib/active_record/aggregations.rb +264 -236
- data/lib/active_record/association_relation.rb +40 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -42
- data/lib/active_record/associations/association.rb +127 -75
- data/lib/active_record/associations/association_scope.rb +126 -92
- data/lib/active_record/associations/belongs_to_association.rb +78 -27
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
- data/lib/active_record/associations/builder/association.rb +117 -32
- data/lib/active_record/associations/builder/belongs_to.rb +135 -60
- data/lib/active_record/associations/builder/collection_association.rb +61 -54
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
- data/lib/active_record/associations/builder/has_many.rb +10 -64
- data/lib/active_record/associations/builder/has_one.rb +19 -51
- data/lib/active_record/associations/builder/singular_association.rb +28 -18
- data/lib/active_record/associations/collection_association.rb +226 -293
- data/lib/active_record/associations/collection_proxy.rb +1067 -69
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +83 -47
- data/lib/active_record/associations/has_many_through_association.rb +98 -65
- data/lib/active_record/associations/has_one_association.rb +57 -20
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
- data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
- data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
- data/lib/active_record/associations/join_dependency.rb +212 -164
- data/lib/active_record/associations/preloader/association.rb +95 -89
- data/lib/active_record/associations/preloader/through_association.rb +84 -44
- data/lib/active_record/associations/preloader.rb +123 -111
- data/lib/active_record/associations/singular_association.rb +33 -24
- data/lib/active_record/associations/through_association.rb +60 -26
- data/lib/active_record/associations.rb +1759 -1506
- data/lib/active_record/attribute_assignment.rb +60 -193
- data/lib/active_record/attribute_decorators.rb +90 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
- data/lib/active_record/attribute_methods/dirty.rb +113 -74
- data/lib/active_record/attribute_methods/primary_key.rb +106 -77
- data/lib/active_record/attribute_methods/query.rb +8 -5
- data/lib/active_record/attribute_methods/read.rb +63 -114
- data/lib/active_record/attribute_methods/serialization.rb +60 -90
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
- data/lib/active_record/attribute_methods/write.rb +43 -45
- data/lib/active_record/attribute_methods.rb +366 -149
- data/lib/active_record/attributes.rb +266 -0
- data/lib/active_record/autosave_association.rb +312 -225
- data/lib/active_record/base.rb +114 -505
- data/lib/active_record/callbacks.rb +145 -67
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +32 -23
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
- data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
- data/lib/active_record/connection_adapters/column.rb +50 -255
- data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -210
- data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
- data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +545 -27
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +145 -0
- data/lib/active_record/core.rb +559 -0
- data/lib/active_record/counter_cache.rb +200 -105
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +107 -69
- data/lib/active_record/enum.rb +244 -0
- data/lib/active_record/errors.rb +245 -60
- data/lib/active_record/explain.rb +35 -71
- data/lib/active_record/explain_registry.rb +32 -0
- data/lib/active_record/explain_subscriber.rb +18 -9
- data/lib/active_record/fixture_set/file.rb +82 -0
- data/lib/active_record/fixtures.rb +418 -275
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +209 -100
- data/lib/active_record/integration.rb +116 -21
- data/lib/active_record/internal_metadata.rb +45 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +9 -1
- data/lib/active_record/locking/optimistic.rb +107 -94
- data/lib/active_record/locking/pessimistic.rb +20 -8
- data/lib/active_record/log_subscriber.rb +99 -34
- data/lib/active_record/migration/command_recorder.rb +199 -64
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +17 -0
- data/lib/active_record/migration.rb +893 -296
- data/lib/active_record/model_schema.rb +328 -175
- data/lib/active_record/nested_attributes.rb +338 -242
- data/lib/active_record/no_touching.rb +58 -0
- data/lib/active_record/null_relation.rb +68 -0
- data/lib/active_record/persistence.rb +557 -170
- data/lib/active_record/query_cache.rb +14 -43
- data/lib/active_record/querying.rb +36 -24
- data/lib/active_record/railtie.rb +147 -52
- data/lib/active_record/railties/console_sandbox.rb +5 -4
- data/lib/active_record/railties/controller_runtime.rb +13 -6
- data/lib/active_record/railties/databases.rake +206 -488
- data/lib/active_record/readonly_attributes.rb +4 -6
- data/lib/active_record/reflection.rb +734 -228
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +249 -52
- data/lib/active_record/relation/calculations.rb +330 -284
- data/lib/active_record/relation/delegation.rb +135 -37
- data/lib/active_record/relation/finder_methods.rb +450 -287
- data/lib/active_record/relation/from_clause.rb +26 -0
- 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/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder.rb +132 -43
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +1037 -221
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +48 -151
- data/lib/active_record/relation/where_clause.rb +186 -0
- data/lib/active_record/relation/where_clause_factory.rb +34 -0
- data/lib/active_record/relation.rb +451 -359
- data/lib/active_record/result.rb +129 -20
- data/lib/active_record/runtime_registry.rb +24 -0
- data/lib/active_record/sanitization.rb +164 -136
- data/lib/active_record/schema.rb +31 -19
- data/lib/active_record/schema_dumper.rb +154 -107
- data/lib/active_record/schema_migration.rb +56 -0
- data/lib/active_record/scoping/default.rb +108 -98
- data/lib/active_record/scoping/named.rb +125 -112
- data/lib/active_record/scoping.rb +77 -123
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +10 -6
- data/lib/active_record/statement_cache.rb +121 -0
- data/lib/active_record/store.rb +175 -16
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +82 -0
- data/lib/active_record/tasks/database_tasks.rb +337 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
- data/lib/active_record/timestamp.rb +80 -41
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +240 -119
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +136 -0
- data/lib/active_record/type/date.rb +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +71 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +21 -0
- data/lib/active_record/type/type_map.rb +62 -0
- data/lib/active_record/type/unsigned_integer.rb +17 -0
- data/lib/active_record/type.rb +79 -0
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +35 -18
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +68 -0
- data/lib/active_record/validations/uniqueness.rb +133 -75
- data/lib/active_record/validations.rb +53 -43
- data/lib/active_record/version.rb +7 -7
- data/lib/active_record.rb +89 -57
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +61 -8
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
- data/lib/rails/generators/active_record/migration.rb +28 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
- data/lib/rails/generators/active_record.rb +10 -16
- metadata +141 -62
- 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/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- 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/mysql_adapter.rb +0 -441
- 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/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -203
- data/lib/active_record/session_store.rb +0 -360
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
- 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
@@ -1,221 +1,88 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_model/forbidden_attributes_protection"
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
module AttributeAssignment
|
5
7
|
extend ActiveSupport::Concern
|
6
|
-
include ActiveModel::
|
7
|
-
|
8
|
-
module ClassMethods
|
9
|
-
private
|
10
|
-
|
11
|
-
# The primary key and inheritance column can never be set by mass-assignment for security reasons.
|
12
|
-
def attributes_protected_by_default
|
13
|
-
default = [ primary_key, inheritance_column ]
|
14
|
-
default << 'id' unless primary_key.eql? 'id'
|
15
|
-
default
|
16
|
-
end
|
17
|
-
end
|
8
|
+
include ActiveModel::AttributeAssignment
|
18
9
|
|
19
|
-
|
20
|
-
# matching the attribute names (which again matches the column names).
|
21
|
-
#
|
22
|
-
# If any attributes are protected by either +attr_protected+ or
|
23
|
-
# +attr_accessible+ then only settable attributes will be assigned.
|
24
|
-
#
|
25
|
-
# class User < ActiveRecord::Base
|
26
|
-
# attr_protected :is_admin
|
27
|
-
# end
|
28
|
-
#
|
29
|
-
# user = User.new
|
30
|
-
# user.attributes = { :username => 'Phusion', :is_admin => true }
|
31
|
-
# user.username # => "Phusion"
|
32
|
-
# user.is_admin? # => false
|
33
|
-
def attributes=(new_attributes)
|
34
|
-
return unless new_attributes.is_a?(Hash)
|
35
|
-
|
36
|
-
assign_attributes(new_attributes)
|
37
|
-
end
|
38
|
-
|
39
|
-
# Allows you to set all the attributes for a particular mass-assignment
|
40
|
-
# security role by passing in a hash of attributes with keys matching
|
41
|
-
# the attribute names (which again matches the column names) and the role
|
42
|
-
# name using the :as option.
|
43
|
-
#
|
44
|
-
# To bypass mass-assignment security you can use the :without_protection => true
|
45
|
-
# option.
|
46
|
-
#
|
47
|
-
# class User < ActiveRecord::Base
|
48
|
-
# attr_accessible :name
|
49
|
-
# attr_accessible :name, :is_admin, :as => :admin
|
50
|
-
# end
|
51
|
-
#
|
52
|
-
# user = User.new
|
53
|
-
# user.assign_attributes({ :name => 'Josh', :is_admin => true })
|
54
|
-
# user.name # => "Josh"
|
55
|
-
# user.is_admin? # => false
|
56
|
-
#
|
57
|
-
# user = User.new
|
58
|
-
# user.assign_attributes({ :name => 'Josh', :is_admin => true }, :as => :admin)
|
59
|
-
# user.name # => "Josh"
|
60
|
-
# user.is_admin? # => true
|
61
|
-
#
|
62
|
-
# user = User.new
|
63
|
-
# user.assign_attributes({ :name => 'Josh', :is_admin => true }, :without_protection => true)
|
64
|
-
# user.name # => "Josh"
|
65
|
-
# user.is_admin? # => true
|
66
|
-
def assign_attributes(new_attributes, options = {})
|
67
|
-
return if new_attributes.blank?
|
10
|
+
private
|
68
11
|
|
69
|
-
attributes
|
70
|
-
|
71
|
-
|
72
|
-
@mass_assignment_options = options
|
12
|
+
def _assign_attributes(attributes)
|
13
|
+
multi_parameter_attributes = {}
|
14
|
+
nested_parameter_attributes = {}
|
73
15
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
if k.include?("(")
|
80
|
-
multi_parameter_attributes << [ k, v ]
|
81
|
-
elsif respond_to?("#{k}=")
|
82
|
-
if v.is_a?(Hash)
|
83
|
-
nested_parameter_attributes << [ k, v ]
|
84
|
-
else
|
85
|
-
send("#{k}=", v)
|
16
|
+
attributes.each do |k, v|
|
17
|
+
if k.include?("(")
|
18
|
+
multi_parameter_attributes[k] = attributes.delete(k)
|
19
|
+
elsif v.is_a?(Hash)
|
20
|
+
nested_parameter_attributes[k] = attributes.delete(k)
|
86
21
|
end
|
87
|
-
else
|
88
|
-
raise(UnknownAttributeError, "unknown attribute: #{k}")
|
89
22
|
end
|
90
|
-
|
23
|
+
super(attributes)
|
91
24
|
|
92
|
-
|
93
|
-
|
94
|
-
send("#{k}=", v)
|
25
|
+
assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
|
26
|
+
assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
|
95
27
|
end
|
96
28
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
protected
|
102
|
-
|
103
|
-
def mass_assignment_options
|
104
|
-
@mass_assignment_options ||= {}
|
105
|
-
end
|
106
|
-
|
107
|
-
def mass_assignment_role
|
108
|
-
mass_assignment_options[:as] || :default
|
109
|
-
end
|
110
|
-
|
111
|
-
private
|
112
|
-
|
113
|
-
# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
|
114
|
-
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
|
115
|
-
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
|
116
|
-
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
|
117
|
-
# parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum,
|
118
|
-
# f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the
|
119
|
-
# attribute will be set to nil.
|
120
|
-
def assign_multiparameter_attributes(pairs)
|
121
|
-
execute_callstack_for_multiparameter_attributes(
|
122
|
-
extract_callstack_for_multiparameter_attributes(pairs)
|
123
|
-
)
|
124
|
-
end
|
29
|
+
# Assign any deferred nested attributes after the base attributes have been set.
|
30
|
+
def assign_nested_parameter_attributes(pairs)
|
31
|
+
pairs.each { |k, v| _assign_attribute(k, v) }
|
32
|
+
end
|
125
33
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
34
|
+
# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
|
35
|
+
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
|
36
|
+
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
|
37
|
+
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
|
38
|
+
# parentheses to have the parameters typecasted before they're used in the constructor. Use i for Integer and
|
39
|
+
# f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
|
40
|
+
def assign_multiparameter_attributes(pairs)
|
41
|
+
execute_callstack_for_multiparameter_attributes(
|
42
|
+
extract_callstack_for_multiparameter_attributes(pairs)
|
43
|
+
)
|
131
44
|
end
|
132
|
-
end
|
133
45
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
46
|
+
def execute_callstack_for_multiparameter_attributes(callstack)
|
47
|
+
errors = []
|
48
|
+
callstack.each do |name, values_with_empty_parameters|
|
49
|
+
begin
|
50
|
+
if values_with_empty_parameters.each_value.all?(&:nil?)
|
51
|
+
values = nil
|
52
|
+
else
|
53
|
+
values = values_with_empty_parameters
|
54
|
+
end
|
55
|
+
send("#{name}=", values)
|
56
|
+
rescue => ex
|
57
|
+
errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
unless errors.empty?
|
61
|
+
error_descriptions = errors.map(&:message).join(",")
|
62
|
+
raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
|
141
63
|
end
|
142
64
|
end
|
143
|
-
unless errors.empty?
|
144
|
-
raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
|
145
|
-
end
|
146
|
-
end
|
147
65
|
|
148
|
-
|
149
|
-
|
150
|
-
if values_hash_from_param.values.all?{|v|v.nil?}
|
151
|
-
nil
|
152
|
-
elsif klass == Time
|
153
|
-
read_time_parameter_value(name, values_hash_from_param)
|
154
|
-
elsif klass == Date
|
155
|
-
read_date_parameter_value(name, values_hash_from_param)
|
156
|
-
else
|
157
|
-
read_other_parameter_value(klass, name, values_hash_from_param)
|
158
|
-
end
|
159
|
-
end
|
66
|
+
def extract_callstack_for_multiparameter_attributes(pairs)
|
67
|
+
attributes = {}
|
160
68
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6)
|
165
|
-
# If Date bits were provided but blank, then return nil
|
166
|
-
return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
|
69
|
+
pairs.each do |(multiparameter_name, value)|
|
70
|
+
attribute_name = multiparameter_name.split("(").first
|
71
|
+
attributes[attribute_name] ||= {}
|
167
72
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
instantiate_time_object(name, set_values)
|
172
|
-
end
|
73
|
+
parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
|
74
|
+
attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
|
75
|
+
end
|
173
76
|
|
174
|
-
|
175
|
-
return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
|
176
|
-
set_values = [values_hash_from_param[1], values_hash_from_param[2], values_hash_from_param[3]]
|
177
|
-
begin
|
178
|
-
Date.new(*set_values)
|
179
|
-
rescue ArgumentError # if Date.new raises an exception on an invalid date
|
180
|
-
instantiate_time_object(name, set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
|
77
|
+
attributes
|
181
78
|
end
|
182
|
-
end
|
183
79
|
|
184
|
-
|
185
|
-
|
186
|
-
values = (1..max_position).collect do |position|
|
187
|
-
raise "Missing Parameter" if !values_hash_from_param.has_key?(position)
|
188
|
-
values_hash_from_param[position]
|
80
|
+
def type_cast_attribute_value(multiparameter_name, value)
|
81
|
+
multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
|
189
82
|
end
|
190
|
-
klass.new(*values)
|
191
|
-
end
|
192
83
|
|
193
|
-
|
194
|
-
|
195
|
-
end
|
196
|
-
|
197
|
-
def extract_callstack_for_multiparameter_attributes(pairs)
|
198
|
-
attributes = { }
|
199
|
-
|
200
|
-
pairs.each do |pair|
|
201
|
-
multiparameter_name, value = pair
|
202
|
-
attribute_name = multiparameter_name.split("(").first
|
203
|
-
attributes[attribute_name] = {} unless attributes.include?(attribute_name)
|
204
|
-
|
205
|
-
parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
|
206
|
-
attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
|
84
|
+
def find_parameter_position(multiparameter_name)
|
85
|
+
multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
|
207
86
|
end
|
208
|
-
|
209
|
-
attributes
|
210
|
-
end
|
211
|
-
|
212
|
-
def type_cast_attribute_value(multiparameter_name, value)
|
213
|
-
multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
|
214
|
-
end
|
215
|
-
|
216
|
-
def find_parameter_position(multiparameter_name)
|
217
|
-
multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
|
218
|
-
end
|
219
|
-
|
220
87
|
end
|
221
88
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module AttributeDecorators # :nodoc:
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
class_attribute :attribute_type_decorations, instance_accessor: false, default: TypeDecorator.new # :internal:
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods # :nodoc:
|
12
|
+
# This method is an internal API used to create class macros such as
|
13
|
+
# +serialize+, and features like time zone aware attributes.
|
14
|
+
#
|
15
|
+
# Used to wrap the type of an attribute in a new type.
|
16
|
+
# When the schema for a model is loaded, attributes with the same name as
|
17
|
+
# +column_name+ will have their type yielded to the given block. The
|
18
|
+
# return value of that block will be used instead.
|
19
|
+
#
|
20
|
+
# Subsequent calls where +column_name+ and +decorator_name+ are the same
|
21
|
+
# will override the previous decorator, not decorate twice. This can be
|
22
|
+
# used to create idempotent class macros like +serialize+
|
23
|
+
def decorate_attribute_type(column_name, decorator_name, &block)
|
24
|
+
matcher = ->(name, _) { name == column_name.to_s }
|
25
|
+
key = "_#{column_name}_#{decorator_name}"
|
26
|
+
decorate_matching_attribute_types(matcher, key, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
# This method is an internal API used to create higher level features like
|
30
|
+
# time zone aware attributes.
|
31
|
+
#
|
32
|
+
# When the schema for a model is loaded, +matcher+ will be called for each
|
33
|
+
# attribute with its name and type. If the matcher returns a truthy value,
|
34
|
+
# the type will then be yielded to the given block, and the return value
|
35
|
+
# of that block will replace the type.
|
36
|
+
#
|
37
|
+
# Subsequent calls to this method with the same value for +decorator_name+
|
38
|
+
# will replace the previous decorator, not decorate twice. This can be
|
39
|
+
# used to ensure that class macros are idempotent.
|
40
|
+
def decorate_matching_attribute_types(matcher, decorator_name, &block)
|
41
|
+
reload_schema_from_cache
|
42
|
+
decorator_name = decorator_name.to_s
|
43
|
+
|
44
|
+
# Create new hashes so we don't modify parent classes
|
45
|
+
self.attribute_type_decorations = attribute_type_decorations.merge(decorator_name => [matcher, block])
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def load_schema!
|
51
|
+
super
|
52
|
+
attribute_types.each do |name, type|
|
53
|
+
decorated_type = attribute_type_decorations.apply(name, type)
|
54
|
+
define_attribute(name, decorated_type)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class TypeDecorator # :nodoc:
|
60
|
+
delegate :clear, to: :@decorations
|
61
|
+
|
62
|
+
def initialize(decorations = {})
|
63
|
+
@decorations = decorations
|
64
|
+
end
|
65
|
+
|
66
|
+
def merge(*args)
|
67
|
+
TypeDecorator.new(@decorations.merge(*args))
|
68
|
+
end
|
69
|
+
|
70
|
+
def apply(name, type)
|
71
|
+
decorations = decorators_for(name, type)
|
72
|
+
decorations.inject(type) do |new_type, block|
|
73
|
+
block.call(new_type)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def decorators_for(name, type)
|
80
|
+
matching(name, type).map(&:last)
|
81
|
+
end
|
82
|
+
|
83
|
+
def matching(name, type)
|
84
|
+
@decorations.values.select do |(matcher, _)|
|
85
|
+
matcher.call(name, type)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -1,31 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module AttributeMethods
|
5
|
+
# = Active Record Attribute Methods Before Type Cast
|
6
|
+
#
|
7
|
+
# ActiveRecord::AttributeMethods::BeforeTypeCast provides a way to
|
8
|
+
# read the value of the attributes before typecasting and deserialization.
|
9
|
+
#
|
10
|
+
# class Task < ActiveRecord::Base
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# task = Task.new(id: '1', completed_on: '2012-10-21')
|
14
|
+
# task.id # => 1
|
15
|
+
# task.completed_on # => Sun, 21 Oct 2012
|
16
|
+
#
|
17
|
+
# task.attributes_before_type_cast
|
18
|
+
# # => {"id"=>"1", "completed_on"=>"2012-10-21", ... }
|
19
|
+
# task.read_attribute_before_type_cast('id') # => "1"
|
20
|
+
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
|
21
|
+
#
|
22
|
+
# In addition to #read_attribute_before_type_cast and #attributes_before_type_cast,
|
23
|
+
# it declares a method for all attributes with the <tt>*_before_type_cast</tt>
|
24
|
+
# suffix.
|
25
|
+
#
|
26
|
+
# task.id_before_type_cast # => "1"
|
27
|
+
# task.completed_on_before_type_cast # => "2012-10-21"
|
3
28
|
module BeforeTypeCast
|
4
29
|
extend ActiveSupport::Concern
|
5
30
|
|
6
31
|
included do
|
7
32
|
attribute_method_suffix "_before_type_cast"
|
33
|
+
attribute_method_suffix "_came_from_user?"
|
8
34
|
end
|
9
35
|
|
36
|
+
# Returns the value of the attribute identified by +attr_name+ before
|
37
|
+
# typecasting and deserialization.
|
38
|
+
#
|
39
|
+
# class Task < ActiveRecord::Base
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# task = Task.new(id: '1', completed_on: '2012-10-21')
|
43
|
+
# task.read_attribute('id') # => 1
|
44
|
+
# task.read_attribute_before_type_cast('id') # => '1'
|
45
|
+
# task.read_attribute('completed_on') # => Sun, 21 Oct 2012
|
46
|
+
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
|
47
|
+
# task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
|
10
48
|
def read_attribute_before_type_cast(attr_name)
|
11
|
-
@attributes[attr_name]
|
49
|
+
@attributes[attr_name.to_s].value_before_type_cast
|
12
50
|
end
|
13
51
|
|
14
52
|
# Returns a hash of attributes before typecasting and deserialization.
|
53
|
+
#
|
54
|
+
# class Task < ActiveRecord::Base
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# task = Task.new(title: nil, is_done: true, completed_on: '2012-10-21')
|
58
|
+
# task.attributes
|
59
|
+
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>Sun, 21 Oct 2012, "created_at"=>nil, "updated_at"=>nil}
|
60
|
+
# task.attributes_before_type_cast
|
61
|
+
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
|
15
62
|
def attributes_before_type_cast
|
16
|
-
@attributes
|
63
|
+
@attributes.values_before_type_cast
|
17
64
|
end
|
18
65
|
|
19
66
|
private
|
20
67
|
|
21
|
-
|
22
|
-
|
23
|
-
if attribute_name == 'id'
|
24
|
-
read_attribute_before_type_cast(self.class.primary_key)
|
25
|
-
else
|
68
|
+
# Handle *_before_type_cast for method_missing.
|
69
|
+
def attribute_before_type_cast(attribute_name)
|
26
70
|
read_attribute_before_type_cast(attribute_name)
|
27
71
|
end
|
28
|
-
|
72
|
+
|
73
|
+
def attribute_came_from_user?(attribute_name)
|
74
|
+
@attributes[attribute_name].came_from_user?
|
75
|
+
end
|
29
76
|
end
|
30
77
|
end
|
31
78
|
end
|