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
@@ -1,13 +1,16 @@
|
|
1
1
|
require 'active_support/core_ext/enumerable'
|
2
|
-
require 'active_support/
|
2
|
+
require 'active_support/core_ext/string/filters'
|
3
|
+
require 'mutex_m'
|
4
|
+
require 'thread_safe'
|
3
5
|
|
4
6
|
module ActiveRecord
|
5
7
|
# = Active Record Attribute Methods
|
6
|
-
module AttributeMethods
|
8
|
+
module AttributeMethods
|
7
9
|
extend ActiveSupport::Concern
|
8
10
|
include ActiveModel::AttributeMethods
|
9
11
|
|
10
12
|
included do
|
13
|
+
initialize_generated_modules
|
11
14
|
include Read
|
12
15
|
include Write
|
13
16
|
include BeforeTypeCast
|
@@ -16,100 +19,141 @@ module ActiveRecord
|
|
16
19
|
include TimeZoneConversion
|
17
20
|
include Dirty
|
18
21
|
include Serialization
|
19
|
-
include DeprecatedUnderscoreRead
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
delegate :column_for_attribute, to: :class
|
24
|
+
end
|
25
|
+
|
26
|
+
AttrNames = Module.new {
|
27
|
+
def self.set_name_cache(name, value)
|
28
|
+
const_name = "ATTR_#{name}"
|
29
|
+
unless const_defined? const_name
|
30
|
+
const_set const_name, value.dup.freeze
|
31
|
+
end
|
32
|
+
end
|
33
|
+
}
|
34
|
+
|
35
|
+
BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
|
36
|
+
|
37
|
+
class AttributeMethodCache
|
38
|
+
def initialize
|
39
|
+
@module = Module.new
|
40
|
+
@method_cache = ThreadSafe::Cache.new
|
26
41
|
end
|
27
42
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
43
|
+
def [](name)
|
44
|
+
@method_cache.compute_if_absent(name) do
|
45
|
+
safe_name = name.unpack('h*').first
|
46
|
+
temp_method = "__temp__#{safe_name}"
|
47
|
+
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
|
48
|
+
@module.module_eval method_body(temp_method, safe_name), __FILE__, __LINE__
|
49
|
+
@module.instance_method temp_method
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Override this method in the subclasses for method body.
|
56
|
+
def method_body(method_name, const_name)
|
57
|
+
raise NotImplementedError, "Subclasses must implement a method_body(method_name, const_name) method."
|
32
58
|
end
|
33
59
|
end
|
34
60
|
|
61
|
+
class GeneratedAttributeMethods < Module; end # :nodoc:
|
62
|
+
|
35
63
|
module ClassMethods
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
msg = "It looks like something (probably a gem/plugin) is overriding the " \
|
41
|
-
"ActiveRecord::Base.inherited method. It is important that this hook executes so " \
|
42
|
-
"that your models are set up correctly. A workaround has been added to stop this " \
|
43
|
-
"causing an error in 3.2, but future versions will simply not work if the hook is " \
|
44
|
-
"overridden. If you are using Kaminari, please upgrade as it is known to have had " \
|
45
|
-
"this problem.\n\n"
|
46
|
-
msg << "The following may help track down the problem:"
|
47
|
-
|
48
|
-
meth = method(:inherited)
|
49
|
-
if meth.respond_to?(:source_location)
|
50
|
-
msg << " #{meth.source_location.inspect}"
|
51
|
-
else
|
52
|
-
msg << " #{meth.inspect}"
|
53
|
-
end
|
54
|
-
msg << "\n\n"
|
64
|
+
def inherited(child_class) #:nodoc:
|
65
|
+
child_class.initialize_generated_modules
|
66
|
+
super
|
67
|
+
end
|
55
68
|
|
56
|
-
|
69
|
+
def initialize_generated_modules # :nodoc:
|
70
|
+
@generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m }
|
71
|
+
@attribute_methods_generated = false
|
72
|
+
include @generated_attribute_methods
|
57
73
|
|
58
|
-
|
59
|
-
|
74
|
+
super
|
75
|
+
end
|
60
76
|
|
61
|
-
|
77
|
+
# Generates all the attribute related methods for columns in the database
|
78
|
+
# accessors, mutators and query methods.
|
79
|
+
def define_attribute_methods # :nodoc:
|
80
|
+
return false if @attribute_methods_generated
|
81
|
+
# Use a mutex; we don't want two threads simultaneously trying to define
|
62
82
|
# attribute methods.
|
63
|
-
|
64
|
-
return if attribute_methods_generated
|
83
|
+
generated_attribute_methods.synchronize do
|
84
|
+
return false if @attribute_methods_generated
|
65
85
|
superclass.define_attribute_methods unless self == base_class
|
66
86
|
super(column_names)
|
67
|
-
column_names.each { |name| define_external_attribute_method(name) }
|
68
87
|
@attribute_methods_generated = true
|
69
88
|
end
|
89
|
+
true
|
70
90
|
end
|
71
91
|
|
72
|
-
def
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
# methods. This allows us to use method_defined? to check if the method exists,
|
78
|
-
# which is fast and won't give any false positives from the ancestors (because
|
79
|
-
# there are no ancestors).
|
80
|
-
def generated_external_attribute_methods
|
81
|
-
@generated_external_attribute_methods ||= Module.new { extend self }
|
82
|
-
end
|
83
|
-
|
84
|
-
def undefine_attribute_methods
|
85
|
-
super
|
86
|
-
@attribute_methods_generated = false
|
92
|
+
def undefine_attribute_methods # :nodoc:
|
93
|
+
generated_attribute_methods.synchronize do
|
94
|
+
super if defined?(@attribute_methods_generated) && @attribute_methods_generated
|
95
|
+
@attribute_methods_generated = false
|
96
|
+
end
|
87
97
|
end
|
88
98
|
|
99
|
+
# Raises a <tt>ActiveRecord::DangerousAttributeError</tt> exception when an
|
100
|
+
# \Active \Record method is defined in the model, otherwise +false+.
|
101
|
+
#
|
102
|
+
# class Person < ActiveRecord::Base
|
103
|
+
# def save
|
104
|
+
# 'already defined by Active Record'
|
105
|
+
# end
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# Person.instance_method_already_implemented?(:save)
|
109
|
+
# # => ActiveRecord::DangerousAttributeError: save is defined by ActiveRecord
|
110
|
+
#
|
111
|
+
# Person.instance_method_already_implemented?(:name)
|
112
|
+
# # => false
|
89
113
|
def instance_method_already_implemented?(method_name)
|
90
114
|
if dangerous_attribute_method?(method_name)
|
91
|
-
raise DangerousAttributeError, "#{method_name} is defined by
|
115
|
+
raise DangerousAttributeError, "#{method_name} is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name."
|
92
116
|
end
|
93
117
|
|
94
118
|
if superclass == Base
|
95
119
|
super
|
96
120
|
else
|
97
|
-
# If
|
98
|
-
|
99
|
-
defined
|
121
|
+
# If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass
|
122
|
+
# defines its own attribute method, then we don't want to overwrite that.
|
123
|
+
defined = method_defined_within?(method_name, superclass, Base) &&
|
124
|
+
! superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods)
|
125
|
+
defined || super
|
100
126
|
end
|
101
127
|
end
|
102
128
|
|
103
|
-
# A method name is 'dangerous' if it is already defined by Active Record, but
|
129
|
+
# A method name is 'dangerous' if it is already (re)defined by Active Record, but
|
104
130
|
# not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
|
105
|
-
def dangerous_attribute_method?(name)
|
131
|
+
def dangerous_attribute_method?(name) # :nodoc:
|
106
132
|
method_defined_within?(name, Base)
|
107
133
|
end
|
108
134
|
|
109
|
-
def method_defined_within?(name, klass,
|
135
|
+
def method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
|
110
136
|
if klass.method_defined?(name) || klass.private_method_defined?(name)
|
111
|
-
if
|
112
|
-
klass.instance_method(name).owner !=
|
137
|
+
if superklass.method_defined?(name) || superklass.private_method_defined?(name)
|
138
|
+
klass.instance_method(name).owner != superklass.instance_method(name).owner
|
139
|
+
else
|
140
|
+
true
|
141
|
+
end
|
142
|
+
else
|
143
|
+
false
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# A class method is 'dangerous' if it is already (re)defined by Active Record, but
|
148
|
+
# not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
|
149
|
+
def dangerous_class_method?(method_name)
|
150
|
+
BLACKLISTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
|
151
|
+
end
|
152
|
+
|
153
|
+
def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc
|
154
|
+
if klass.respond_to?(name, true)
|
155
|
+
if superklass.respond_to?(name, true)
|
156
|
+
klass.method(name).owner != superklass.method(name).owner
|
113
157
|
else
|
114
158
|
true
|
115
159
|
end
|
@@ -118,13 +162,27 @@ module ActiveRecord
|
|
118
162
|
end
|
119
163
|
end
|
120
164
|
|
165
|
+
# Returns +true+ if +attribute+ is an attribute method and table exists,
|
166
|
+
# +false+ otherwise.
|
167
|
+
#
|
168
|
+
# class Person < ActiveRecord::Base
|
169
|
+
# end
|
170
|
+
#
|
171
|
+
# Person.attribute_method?('name') # => true
|
172
|
+
# Person.attribute_method?(:age=) # => true
|
173
|
+
# Person.attribute_method?(:nothing) # => false
|
121
174
|
def attribute_method?(attribute)
|
122
175
|
super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
|
123
176
|
end
|
124
177
|
|
125
|
-
# Returns an array of column names as strings if it's not
|
126
|
-
#
|
127
|
-
#
|
178
|
+
# Returns an array of column names as strings if it's not an abstract class and
|
179
|
+
# table exists. Otherwise it returns an empty array.
|
180
|
+
#
|
181
|
+
# class Person < ActiveRecord::Base
|
182
|
+
# end
|
183
|
+
#
|
184
|
+
# Person.attribute_names
|
185
|
+
# # => ["id", "created_at", "updated_at", "name", "age"]
|
128
186
|
def attribute_names
|
129
187
|
@attribute_names ||= if !abstract_class? && table_exists?
|
130
188
|
column_names
|
@@ -132,78 +190,121 @@ module ActiveRecord
|
|
132
190
|
[]
|
133
191
|
end
|
134
192
|
end
|
135
|
-
end
|
136
|
-
|
137
|
-
# If we haven't generated any methods yet, generate them, then
|
138
|
-
# see if we've created the method we're looking for.
|
139
|
-
def method_missing(method, *args, &block)
|
140
|
-
unless self.class.attribute_methods_generated?
|
141
|
-
self.class.define_attribute_methods
|
142
193
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
194
|
+
# Returns the column object for the named attribute.
|
195
|
+
# Returns nil if the named attribute does not exist.
|
196
|
+
#
|
197
|
+
# class Person < ActiveRecord::Base
|
198
|
+
# end
|
199
|
+
#
|
200
|
+
# person = Person.new
|
201
|
+
# person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
|
202
|
+
# # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
|
203
|
+
#
|
204
|
+
# person.column_for_attribute(:nothing)
|
205
|
+
# # => nil
|
206
|
+
def column_for_attribute(name)
|
207
|
+
column = columns_hash[name.to_s]
|
208
|
+
if column.nil?
|
209
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
210
|
+
`#column_for_attribute` will return a null object for non-existent
|
211
|
+
columns in Rails 5. Use `#has_attribute?` if you need to check for
|
212
|
+
an attribute's existence.
|
213
|
+
MSG
|
147
214
|
end
|
148
|
-
|
149
|
-
super
|
215
|
+
column
|
150
216
|
end
|
151
217
|
end
|
152
218
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
219
|
+
# A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
|
220
|
+
# <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
|
221
|
+
# which will all return +true+. It also define the attribute methods if they have
|
222
|
+
# not been generated.
|
223
|
+
#
|
224
|
+
# class Person < ActiveRecord::Base
|
225
|
+
# end
|
226
|
+
#
|
227
|
+
# person = Person.new
|
228
|
+
# person.respond_to(:name) # => true
|
229
|
+
# person.respond_to(:name=) # => true
|
230
|
+
# person.respond_to(:name?) # => true
|
231
|
+
# person.respond_to('age') # => true
|
232
|
+
# person.respond_to('age=') # => true
|
233
|
+
# person.respond_to('age?') # => true
|
234
|
+
# person.respond_to(:nothing) # => false
|
235
|
+
def respond_to?(name, include_private = false)
|
236
|
+
return false unless super
|
237
|
+
name = name.to_s
|
238
|
+
|
239
|
+
# If the result is true then check for the select case.
|
240
|
+
# For queries selecting a subset of columns, return false for unselected columns.
|
241
|
+
# We check defined?(@attributes) not to issue warnings if called on objects that
|
242
|
+
# have been allocated but not yet initialized.
|
243
|
+
if defined?(@attributes) && self.class.column_names.include?(name)
|
244
|
+
return has_attribute?(name)
|
162
245
|
end
|
163
246
|
|
164
|
-
|
165
|
-
end
|
166
|
-
|
167
|
-
def respond_to?(name, include_private = false)
|
168
|
-
self.class.define_attribute_methods unless self.class.attribute_methods_generated?
|
169
|
-
super
|
247
|
+
return true
|
170
248
|
end
|
171
249
|
|
172
|
-
# Returns true if the given attribute is in the attributes hash
|
250
|
+
# Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
|
251
|
+
#
|
252
|
+
# class Person < ActiveRecord::Base
|
253
|
+
# end
|
254
|
+
#
|
255
|
+
# person = Person.new
|
256
|
+
# person.has_attribute?(:name) # => true
|
257
|
+
# person.has_attribute?('age') # => true
|
258
|
+
# person.has_attribute?(:nothing) # => false
|
173
259
|
def has_attribute?(attr_name)
|
174
|
-
@attributes.
|
260
|
+
@attributes.key?(attr_name.to_s)
|
175
261
|
end
|
176
262
|
|
177
263
|
# Returns an array of names for the attributes available on this object.
|
264
|
+
#
|
265
|
+
# class Person < ActiveRecord::Base
|
266
|
+
# end
|
267
|
+
#
|
268
|
+
# person = Person.new
|
269
|
+
# person.attribute_names
|
270
|
+
# # => ["id", "created_at", "updated_at", "name", "age"]
|
178
271
|
def attribute_names
|
179
272
|
@attributes.keys
|
180
273
|
end
|
181
274
|
|
182
275
|
# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
|
276
|
+
#
|
277
|
+
# class Person < ActiveRecord::Base
|
278
|
+
# end
|
279
|
+
#
|
280
|
+
# person = Person.create(name: 'Francesco', age: 22)
|
281
|
+
# person.attributes
|
282
|
+
# # => {"id"=>3, "created_at"=>Sun, 21 Oct 2012 04:53:04, "updated_at"=>Sun, 21 Oct 2012 04:53:04, "name"=>"Francesco", "age"=>22}
|
183
283
|
def attributes
|
184
|
-
|
185
|
-
attribute_names.each { |name| attrs[name] = read_attribute(name) }
|
186
|
-
attrs
|
284
|
+
@attributes.to_hash
|
187
285
|
end
|
188
286
|
|
189
287
|
# Returns an <tt>#inspect</tt>-like string for the value of the
|
190
|
-
# attribute +attr_name+. String attributes are truncated
|
191
|
-
# characters,
|
288
|
+
# attribute +attr_name+. String attributes are truncated up to 50
|
289
|
+
# characters, Date and Time attributes are returned in the
|
192
290
|
# <tt>:db</tt> format. Other attributes return the value of
|
193
291
|
# <tt>#inspect</tt> without modification.
|
194
292
|
#
|
195
|
-
# person = Person.create!(:
|
293
|
+
# person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
|
196
294
|
#
|
197
295
|
# person.attribute_for_inspect(:name)
|
198
|
-
# # =>
|
296
|
+
# # => "\"David Heinemeier Hansson David Heinemeier Hansson ...\""
|
199
297
|
#
|
200
298
|
# person.attribute_for_inspect(:created_at)
|
201
|
-
# # =>
|
299
|
+
# # => "\"2012-10-22 00:15:07\""
|
300
|
+
#
|
301
|
+
# person.attribute_for_inspect(:tag_ids)
|
302
|
+
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
|
202
303
|
def attribute_for_inspect(attr_name)
|
203
304
|
value = read_attribute(attr_name)
|
204
305
|
|
205
306
|
if value.is_a?(String) && value.length > 50
|
206
|
-
"#{value[0
|
307
|
+
"#{value[0, 50]}...".inspect
|
207
308
|
elsif value.is_a?(Date) || value.is_a?(Time)
|
208
309
|
%("#{value.to_s(:db)}")
|
209
310
|
else
|
@@ -211,65 +312,124 @@ module ActiveRecord
|
|
211
312
|
end
|
212
313
|
end
|
213
314
|
|
214
|
-
# Returns true if the specified +attribute+ has been set by the user or by a
|
215
|
-
# nil nor empty
|
315
|
+
# Returns +true+ if the specified +attribute+ has been set by the user or by a
|
316
|
+
# database load and is neither +nil+ nor <tt>empty?</tt> (the latter only applies
|
317
|
+
# to objects that respond to <tt>empty?</tt>, most notably Strings). Otherwise, +false+.
|
318
|
+
# Note that it always returns +true+ with boolean attributes.
|
319
|
+
#
|
320
|
+
# class Task < ActiveRecord::Base
|
321
|
+
# end
|
322
|
+
#
|
323
|
+
# task = Task.new(title: '', is_done: false)
|
324
|
+
# task.attribute_present?(:title) # => false
|
325
|
+
# task.attribute_present?(:is_done) # => true
|
326
|
+
# task.title = 'Buy milk'
|
327
|
+
# task.is_done = true
|
328
|
+
# task.attribute_present?(:title) # => true
|
329
|
+
# task.attribute_present?(:is_done) # => true
|
216
330
|
def attribute_present?(attribute)
|
217
|
-
value =
|
331
|
+
value = _read_attribute(attribute)
|
218
332
|
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
219
333
|
end
|
220
334
|
|
221
|
-
# Returns the
|
222
|
-
|
223
|
-
|
335
|
+
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
|
336
|
+
# "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
|
337
|
+
# <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
|
338
|
+
#
|
339
|
+
# Note: +:id+ is always present.
|
340
|
+
#
|
341
|
+
# Alias for the <tt>read_attribute</tt> method.
|
342
|
+
#
|
343
|
+
# class Person < ActiveRecord::Base
|
344
|
+
# belongs_to :organization
|
345
|
+
# end
|
346
|
+
#
|
347
|
+
# person = Person.new(name: 'Francesco', age: '22')
|
348
|
+
# person[:name] # => "Francesco"
|
349
|
+
# person[:age] # => 22
|
350
|
+
#
|
351
|
+
# person = Person.select('id').first
|
352
|
+
# person[:name] # => ActiveModel::MissingAttributeError: missing attribute: name
|
353
|
+
# person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute: organization_id
|
354
|
+
def [](attr_name)
|
355
|
+
read_attribute(attr_name) { |n| missing_attribute(n, caller) }
|
224
356
|
end
|
225
357
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
358
|
+
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
|
359
|
+
# (Alias for the protected <tt>write_attribute</tt> method).
|
360
|
+
#
|
361
|
+
# class Person < ActiveRecord::Base
|
362
|
+
# end
|
363
|
+
#
|
364
|
+
# person = Person.new
|
365
|
+
# person[:age] = '22'
|
366
|
+
# person[:age] # => 22
|
367
|
+
# person[:age].class # => Integer
|
368
|
+
def []=(attr_name, value)
|
369
|
+
write_attribute(attr_name, value)
|
233
370
|
end
|
234
371
|
|
235
|
-
|
372
|
+
protected
|
373
|
+
|
374
|
+
def clone_attribute_value(reader_method, attribute_name) # :nodoc:
|
236
375
|
value = send(reader_method, attribute_name)
|
237
376
|
value.duplicable? ? value.clone : value
|
238
377
|
rescue TypeError, NoMethodError
|
239
378
|
value
|
240
379
|
end
|
241
380
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
attrs = {}
|
246
|
-
klass = self.class
|
247
|
-
arel_table = klass.arel_table
|
381
|
+
def arel_attributes_with_values_for_create(attribute_names) # :nodoc:
|
382
|
+
arel_attributes_with_values(attributes_for_create(attribute_names))
|
383
|
+
end
|
248
384
|
|
249
|
-
|
250
|
-
|
385
|
+
def arel_attributes_with_values_for_update(attribute_names) # :nodoc:
|
386
|
+
arel_attributes_with_values(attributes_for_update(attribute_names))
|
387
|
+
end
|
251
388
|
|
252
|
-
|
389
|
+
def attribute_method?(attr_name) # :nodoc:
|
390
|
+
# We check defined? because Syck calls respond_to? before actually calling initialize.
|
391
|
+
defined?(@attributes) && @attributes.key?(attr_name)
|
392
|
+
end
|
253
393
|
|
254
|
-
|
255
|
-
@attributes[name].serialized_value
|
256
|
-
else
|
257
|
-
# FIXME: we need @attributes to be used consistently.
|
258
|
-
# If the values stored in @attributes were already type
|
259
|
-
# casted, this code could be simplified
|
260
|
-
read_attribute(name)
|
261
|
-
end
|
394
|
+
private
|
262
395
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
396
|
+
# Returns a Hash of the Arel::Attributes and attribute values that have been
|
397
|
+
# typecasted for use in an Arel insert/update method.
|
398
|
+
def arel_attributes_with_values(attribute_names)
|
399
|
+
attrs = {}
|
400
|
+
arel_table = self.class.arel_table
|
267
401
|
|
402
|
+
attribute_names.each do |name|
|
403
|
+
attrs[arel_table[name]] = typecasted_attribute_value(name)
|
404
|
+
end
|
268
405
|
attrs
|
269
406
|
end
|
270
407
|
|
271
|
-
|
272
|
-
|
408
|
+
# Filters the primary keys and readonly attributes from the attribute names.
|
409
|
+
def attributes_for_update(attribute_names)
|
410
|
+
attribute_names.reject do |name|
|
411
|
+
readonly_attribute?(name)
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
# Filters out the primary keys, from the attribute names, when the primary
|
416
|
+
# key is to be generated (e.g. the id attribute has no value).
|
417
|
+
def attributes_for_create(attribute_names)
|
418
|
+
attribute_names.reject do |name|
|
419
|
+
pk_attribute?(name) && id.nil?
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
def readonly_attribute?(name)
|
424
|
+
self.class.readonly_attributes.include?(name)
|
425
|
+
end
|
426
|
+
|
427
|
+
def pk_attribute?(name)
|
428
|
+
name == self.class.primary_key
|
429
|
+
end
|
430
|
+
|
431
|
+
def typecasted_attribute_value(name)
|
432
|
+
_read_attribute(name)
|
273
433
|
end
|
274
434
|
end
|
275
435
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'active_record/attribute'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class AttributeSet # :nodoc:
|
5
|
+
class Builder # :nodoc:
|
6
|
+
attr_reader :types, :always_initialized
|
7
|
+
|
8
|
+
def initialize(types, always_initialized = nil)
|
9
|
+
@types = types
|
10
|
+
@always_initialized = always_initialized
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_from_database(values = {}, additional_types = {})
|
14
|
+
if always_initialized && !values.key?(always_initialized)
|
15
|
+
values[always_initialized] = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
attributes = LazyAttributeHash.new(types, values, additional_types)
|
19
|
+
AttributeSet.new(attributes)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class LazyAttributeHash # :nodoc:
|
25
|
+
delegate :transform_values, to: :materialize
|
26
|
+
|
27
|
+
def initialize(types, values, additional_types)
|
28
|
+
@types = types
|
29
|
+
@values = values
|
30
|
+
@additional_types = additional_types
|
31
|
+
@materialized = false
|
32
|
+
@delegate_hash = {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def key?(key)
|
36
|
+
delegate_hash.key?(key) || values.key?(key) || types.key?(key)
|
37
|
+
end
|
38
|
+
|
39
|
+
def [](key)
|
40
|
+
delegate_hash[key] || assign_default_value(key)
|
41
|
+
end
|
42
|
+
|
43
|
+
def []=(key, value)
|
44
|
+
if frozen?
|
45
|
+
raise RuntimeError, "Can't modify frozen hash"
|
46
|
+
end
|
47
|
+
delegate_hash[key] = value
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialized_keys
|
51
|
+
delegate_hash.keys | values.keys
|
52
|
+
end
|
53
|
+
|
54
|
+
def initialize_dup(_)
|
55
|
+
@delegate_hash = delegate_hash.transform_values(&:dup)
|
56
|
+
super
|
57
|
+
end
|
58
|
+
|
59
|
+
def select
|
60
|
+
keys = types.keys | values.keys | delegate_hash.keys
|
61
|
+
keys.each_with_object({}) do |key, hash|
|
62
|
+
attribute = self[key]
|
63
|
+
if yield(key, attribute)
|
64
|
+
hash[key] = attribute
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def ==(other)
|
70
|
+
if other.is_a?(LazyAttributeHash)
|
71
|
+
materialize == other.materialize
|
72
|
+
else
|
73
|
+
materialize == other
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
attr_reader :types, :values, :additional_types, :delegate_hash
|
80
|
+
|
81
|
+
def materialize
|
82
|
+
unless @materialized
|
83
|
+
values.each_key { |key| self[key] }
|
84
|
+
types.each_key { |key| self[key] }
|
85
|
+
unless frozen?
|
86
|
+
@materialized = true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
delegate_hash
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def assign_default_value(name)
|
95
|
+
type = additional_types.fetch(name, types[name])
|
96
|
+
value_present = true
|
97
|
+
value = values.fetch(name) { value_present = false }
|
98
|
+
|
99
|
+
if value_present
|
100
|
+
delegate_hash[name] = Attribute.from_database(name, value, type)
|
101
|
+
elsif types.key?(name)
|
102
|
+
delegate_hash[name] = Attribute.uninitialized(name, type)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|