activerecord 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 +7 -0
- data/CHANGELOG.md +1372 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +218 -0
- data/examples/performance.rb +184 -0
- data/examples/simple.rb +14 -0
- data/lib/active_record.rb +173 -0
- data/lib/active_record/aggregations.rb +266 -0
- data/lib/active_record/association_relation.rb +22 -0
- data/lib/active_record/associations.rb +1724 -0
- data/lib/active_record/associations/alias_tracker.rb +87 -0
- data/lib/active_record/associations/association.rb +253 -0
- data/lib/active_record/associations/association_scope.rb +194 -0
- data/lib/active_record/associations/belongs_to_association.rb +111 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
- data/lib/active_record/associations/builder/association.rb +149 -0
- data/lib/active_record/associations/builder/belongs_to.rb +116 -0
- data/lib/active_record/associations/builder/collection_association.rb +91 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
- data/lib/active_record/associations/builder/has_many.rb +15 -0
- data/lib/active_record/associations/builder/has_one.rb +23 -0
- data/lib/active_record/associations/builder/singular_association.rb +38 -0
- data/lib/active_record/associations/collection_association.rb +634 -0
- data/lib/active_record/associations/collection_proxy.rb +1027 -0
- data/lib/active_record/associations/has_many_association.rb +184 -0
- data/lib/active_record/associations/has_many_through_association.rb +238 -0
- data/lib/active_record/associations/has_one_association.rb +105 -0
- data/lib/active_record/associations/has_one_through_association.rb +36 -0
- data/lib/active_record/associations/join_dependency.rb +282 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
- data/lib/active_record/associations/preloader.rb +203 -0
- data/lib/active_record/associations/preloader/association.rb +162 -0
- data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
- data/lib/active_record/associations/preloader/collection_association.rb +24 -0
- data/lib/active_record/associations/preloader/has_many.rb +17 -0
- data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
- data/lib/active_record/associations/preloader/has_one.rb +23 -0
- data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
- data/lib/active_record/associations/preloader/singular_association.rb +21 -0
- data/lib/active_record/associations/preloader/through_association.rb +96 -0
- data/lib/active_record/associations/singular_association.rb +86 -0
- data/lib/active_record/associations/through_association.rb +96 -0
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +212 -0
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods.rb +439 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
- data/lib/active_record/attribute_methods/dirty.rb +181 -0
- data/lib/active_record/attribute_methods/primary_key.rb +128 -0
- data/lib/active_record/attribute_methods/query.rb +40 -0
- data/lib/active_record/attribute_methods/read.rb +103 -0
- data/lib/active_record/attribute_methods/serialization.rb +70 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
- data/lib/active_record/attribute_methods/write.rb +83 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +439 -0
- data/lib/active_record/base.rb +317 -0
- data/lib/active_record/callbacks.rb +313 -0
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +38 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
- 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 +574 -0
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
- data/lib/active_record/connection_adapters/column.rb +82 -0
- data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
- 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.rb +36 -0
- 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/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 +588 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
- data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
- data/lib/active_record/connection_handling.rb +132 -0
- data/lib/active_record/core.rb +566 -0
- data/lib/active_record/counter_cache.rb +175 -0
- data/lib/active_record/dynamic_matchers.rb +140 -0
- data/lib/active_record/enum.rb +198 -0
- data/lib/active_record/errors.rb +252 -0
- data/lib/active_record/explain.rb +38 -0
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +29 -0
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +1007 -0
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +247 -0
- data/lib/active_record/integration.rb +113 -0
- data/lib/active_record/locale/en.yml +47 -0
- data/lib/active_record/locking/optimistic.rb +204 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/log_subscriber.rb +75 -0
- data/lib/active_record/migration.rb +1051 -0
- data/lib/active_record/migration/command_recorder.rb +197 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +340 -0
- data/lib/active_record/nested_attributes.rb +548 -0
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +81 -0
- data/lib/active_record/persistence.rb +532 -0
- data/lib/active_record/query_cache.rb +56 -0
- data/lib/active_record/querying.rb +68 -0
- data/lib/active_record/railtie.rb +162 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +50 -0
- data/lib/active_record/railties/databases.rake +391 -0
- data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
- data/lib/active_record/readonly_attributes.rb +23 -0
- data/lib/active_record/reflection.rb +881 -0
- data/lib/active_record/relation.rb +681 -0
- data/lib/active_record/relation/batches.rb +138 -0
- data/lib/active_record/relation/calculations.rb +403 -0
- data/lib/active_record/relation/delegation.rb +140 -0
- data/lib/active_record/relation/finder_methods.rb +528 -0
- data/lib/active_record/relation/merger.rb +170 -0
- data/lib/active_record/relation/predicate_builder.rb +126 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/query_methods.rb +1176 -0
- data/lib/active_record/relation/spawn_methods.rb +75 -0
- data/lib/active_record/result.rb +131 -0
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +191 -0
- data/lib/active_record/schema.rb +64 -0
- data/lib/active_record/schema_dumper.rb +251 -0
- data/lib/active_record/schema_migration.rb +56 -0
- data/lib/active_record/scoping.rb +87 -0
- data/lib/active_record/scoping/default.rb +134 -0
- data/lib/active_record/scoping/named.rb +164 -0
- data/lib/active_record/serialization.rb +22 -0
- data/lib/active_record/serializers/xml_serializer.rb +193 -0
- data/lib/active_record/statement_cache.rb +111 -0
- data/lib/active_record/store.rb +205 -0
- data/lib/active_record/tasks/database_tasks.rb +296 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
- data/lib/active_record/timestamp.rb +121 -0
- data/lib/active_record/transactions.rb +417 -0
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/type.rb +23 -0
- 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/validations.rb +90 -0
- data/lib/active_record/validations/associated.rb +51 -0
- data/lib/active_record/validations/presence.rb +67 -0
- data/lib/active_record/validations/uniqueness.rb +229 -0
- data/lib/active_record/version.rb +8 -0
- data/lib/rails/generators/active_record.rb +17 -0
- data/lib/rails/generators/active_record/migration.rb +18 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
- metadata +309 -0
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'active_model/forbidden_attributes_protection'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module AttributeAssignment
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
include ActiveModel::ForbiddenAttributesProtection
|
7
|
+
|
8
|
+
# Allows you to set all the attributes by passing in a hash of attributes with
|
9
|
+
# keys matching the attribute names (which again matches the column names).
|
10
|
+
#
|
11
|
+
# If the passed hash responds to <tt>permitted?</tt> method and the return value
|
12
|
+
# of this method is +false+ an <tt>ActiveModel::ForbiddenAttributesError</tt>
|
13
|
+
# exception is raised.
|
14
|
+
#
|
15
|
+
# cat = Cat.new(name: "Gorby", status: "yawning")
|
16
|
+
# cat.attributes # => { "name" => "Gorby", "status" => "yawning", "created_at" => nil, "updated_at" => nil}
|
17
|
+
# cat.assign_attributes(status: "sleeping")
|
18
|
+
# cat.attributes # => { "name" => "Gorby", "status" => "sleeping", "created_at" => nil, "updated_at" => nil }
|
19
|
+
#
|
20
|
+
# New attributes will be persisted in the database when the object is saved.
|
21
|
+
#
|
22
|
+
# Aliased to <tt>attributes=</tt>.
|
23
|
+
def assign_attributes(new_attributes)
|
24
|
+
if !new_attributes.respond_to?(:stringify_keys)
|
25
|
+
raise ArgumentError, "When assigning attributes, you must pass a hash as an argument."
|
26
|
+
end
|
27
|
+
return if new_attributes.blank?
|
28
|
+
|
29
|
+
attributes = new_attributes.stringify_keys
|
30
|
+
multi_parameter_attributes = []
|
31
|
+
nested_parameter_attributes = []
|
32
|
+
|
33
|
+
attributes = sanitize_for_mass_assignment(attributes)
|
34
|
+
|
35
|
+
attributes.each do |k, v|
|
36
|
+
if k.include?("(")
|
37
|
+
multi_parameter_attributes << [ k, v ]
|
38
|
+
elsif v.is_a?(Hash)
|
39
|
+
nested_parameter_attributes << [ k, v ]
|
40
|
+
else
|
41
|
+
_assign_attribute(k, v)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
|
46
|
+
assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
alias attributes= assign_attributes
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def _assign_attribute(k, v)
|
54
|
+
public_send("#{k}=", v)
|
55
|
+
rescue NoMethodError
|
56
|
+
if respond_to?("#{k}=")
|
57
|
+
raise
|
58
|
+
else
|
59
|
+
raise UnknownAttributeError.new(self, k)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Assign any deferred nested attributes after the base attributes have been set.
|
64
|
+
def assign_nested_parameter_attributes(pairs)
|
65
|
+
pairs.each { |k, v| _assign_attribute(k, v) }
|
66
|
+
end
|
67
|
+
|
68
|
+
# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
|
69
|
+
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
|
70
|
+
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
|
71
|
+
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
|
72
|
+
# parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum and
|
73
|
+
# f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
|
74
|
+
def assign_multiparameter_attributes(pairs)
|
75
|
+
execute_callstack_for_multiparameter_attributes(
|
76
|
+
extract_callstack_for_multiparameter_attributes(pairs)
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def execute_callstack_for_multiparameter_attributes(callstack)
|
81
|
+
errors = []
|
82
|
+
callstack.each do |name, values_with_empty_parameters|
|
83
|
+
begin
|
84
|
+
send("#{name}=", MultiparameterAttribute.new(self, name, values_with_empty_parameters).read_value)
|
85
|
+
rescue => ex
|
86
|
+
errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
unless errors.empty?
|
90
|
+
error_descriptions = errors.map { |ex| ex.message }.join(",")
|
91
|
+
raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def extract_callstack_for_multiparameter_attributes(pairs)
|
96
|
+
attributes = {}
|
97
|
+
|
98
|
+
pairs.each do |(multiparameter_name, value)|
|
99
|
+
attribute_name = multiparameter_name.split("(").first
|
100
|
+
attributes[attribute_name] ||= {}
|
101
|
+
|
102
|
+
parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
|
103
|
+
attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
|
104
|
+
end
|
105
|
+
|
106
|
+
attributes
|
107
|
+
end
|
108
|
+
|
109
|
+
def type_cast_attribute_value(multiparameter_name, value)
|
110
|
+
multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
|
111
|
+
end
|
112
|
+
|
113
|
+
def find_parameter_position(multiparameter_name)
|
114
|
+
multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
|
115
|
+
end
|
116
|
+
|
117
|
+
class MultiparameterAttribute #:nodoc:
|
118
|
+
attr_reader :object, :name, :values, :cast_type
|
119
|
+
|
120
|
+
def initialize(object, name, values)
|
121
|
+
@object = object
|
122
|
+
@name = name
|
123
|
+
@values = values
|
124
|
+
end
|
125
|
+
|
126
|
+
def read_value
|
127
|
+
return if values.values.compact.empty?
|
128
|
+
|
129
|
+
@cast_type = object.type_for_attribute(name)
|
130
|
+
klass = cast_type.klass
|
131
|
+
|
132
|
+
if klass == Time
|
133
|
+
read_time
|
134
|
+
elsif klass == Date
|
135
|
+
read_date
|
136
|
+
else
|
137
|
+
read_other
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def instantiate_time_object(set_values)
|
144
|
+
if object.class.send(:create_time_zone_conversion_attribute?, name, cast_type)
|
145
|
+
Time.zone.local(*set_values)
|
146
|
+
else
|
147
|
+
Time.send(object.class.default_timezone, *set_values)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def read_time
|
152
|
+
# If column is a :time (and not :date or :datetime) there is no need to validate if
|
153
|
+
# there are year/month/day fields
|
154
|
+
if cast_type.type == :time
|
155
|
+
# if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil
|
156
|
+
{ 1 => 1970, 2 => 1, 3 => 1 }.each do |key,value|
|
157
|
+
values[key] ||= value
|
158
|
+
end
|
159
|
+
else
|
160
|
+
# else column is a timestamp, so if Date bits were not provided, error
|
161
|
+
validate_required_parameters!([1,2,3])
|
162
|
+
|
163
|
+
# If Date bits were provided but blank, then return nil
|
164
|
+
return if blank_date_parameter?
|
165
|
+
end
|
166
|
+
|
167
|
+
max_position = extract_max_param(6)
|
168
|
+
set_values = values.values_at(*(1..max_position))
|
169
|
+
# If Time bits are not there, then default to 0
|
170
|
+
(3..5).each { |i| set_values[i] = set_values[i].presence || 0 }
|
171
|
+
instantiate_time_object(set_values)
|
172
|
+
end
|
173
|
+
|
174
|
+
def read_date
|
175
|
+
return if blank_date_parameter?
|
176
|
+
set_values = values.values_at(1,2,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(set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def read_other
|
185
|
+
max_position = extract_max_param
|
186
|
+
positions = (1..max_position)
|
187
|
+
validate_required_parameters!(positions)
|
188
|
+
|
189
|
+
values.slice(*positions)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Checks whether some blank date parameter exists. Note that this is different
|
193
|
+
# than the validate_required_parameters! method, since it just checks for blank
|
194
|
+
# positions instead of missing ones, and does not raise in case one blank position
|
195
|
+
# exists. The caller is responsible to handle the case of this returning true.
|
196
|
+
def blank_date_parameter?
|
197
|
+
(1..3).any? { |position| values[position].blank? }
|
198
|
+
end
|
199
|
+
|
200
|
+
# If some position is not provided, it errors out a missing parameter exception.
|
201
|
+
def validate_required_parameters!(positions)
|
202
|
+
if missing_parameter = positions.detect { |position| !values.key?(position) }
|
203
|
+
raise ArgumentError.new("Missing Parameter - #{name}(#{missing_parameter})")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def extract_max_param(upper_cap = 100)
|
208
|
+
[values.keys.max, upper_cap].min
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeDecorators # :nodoc:
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :attribute_type_decorations, instance_accessor: false # :internal:
|
7
|
+
self.attribute_type_decorations = TypeDecorator.new
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods # :nodoc:
|
11
|
+
def decorate_attribute_type(column_name, decorator_name, &block)
|
12
|
+
matcher = ->(name, _) { name == column_name.to_s }
|
13
|
+
key = "_#{column_name}_#{decorator_name}"
|
14
|
+
decorate_matching_attribute_types(matcher, key, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def decorate_matching_attribute_types(matcher, decorator_name, &block)
|
18
|
+
clear_caches_calculated_from_columns
|
19
|
+
decorator_name = decorator_name.to_s
|
20
|
+
|
21
|
+
# Create new hashes so we don't modify parent classes
|
22
|
+
self.attribute_type_decorations = attribute_type_decorations.merge(decorator_name => [matcher, block])
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def add_user_provided_columns(*)
|
28
|
+
super.map do |column|
|
29
|
+
decorated_type = attribute_type_decorations.apply(column.name, column.cast_type)
|
30
|
+
column.with_type(decorated_type)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class TypeDecorator # :nodoc:
|
36
|
+
delegate :clear, to: :@decorations
|
37
|
+
|
38
|
+
def initialize(decorations = {})
|
39
|
+
@decorations = decorations
|
40
|
+
end
|
41
|
+
|
42
|
+
def merge(*args)
|
43
|
+
TypeDecorator.new(@decorations.merge(*args))
|
44
|
+
end
|
45
|
+
|
46
|
+
def apply(name, type)
|
47
|
+
decorations = decorators_for(name, type)
|
48
|
+
decorations.inject(type) do |new_type, block|
|
49
|
+
block.call(new_type)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def decorators_for(name, type)
|
56
|
+
matching(name, type).map(&:last)
|
57
|
+
end
|
58
|
+
|
59
|
+
def matching(name, type)
|
60
|
+
@decorations.values.select do |(matcher, _)|
|
61
|
+
matcher.call(name, type)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,439 @@
|
|
1
|
+
require 'active_support/core_ext/enumerable'
|
2
|
+
require 'active_support/core_ext/string/filters'
|
3
|
+
require 'mutex_m'
|
4
|
+
require 'thread_safe'
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
# = Active Record Attribute Methods
|
8
|
+
module AttributeMethods
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
include ActiveModel::AttributeMethods
|
11
|
+
|
12
|
+
included do
|
13
|
+
initialize_generated_modules
|
14
|
+
include Read
|
15
|
+
include Write
|
16
|
+
include BeforeTypeCast
|
17
|
+
include Query
|
18
|
+
include PrimaryKey
|
19
|
+
include TimeZoneConversion
|
20
|
+
include Dirty
|
21
|
+
include Serialization
|
22
|
+
|
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
|
41
|
+
end
|
42
|
+
|
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."
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class GeneratedAttributeMethods < Module; end # :nodoc:
|
62
|
+
|
63
|
+
module ClassMethods
|
64
|
+
def inherited(child_class) #:nodoc:
|
65
|
+
child_class.initialize_generated_modules
|
66
|
+
super
|
67
|
+
end
|
68
|
+
|
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
|
73
|
+
|
74
|
+
super
|
75
|
+
end
|
76
|
+
|
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
|
82
|
+
# attribute methods.
|
83
|
+
generated_attribute_methods.synchronize do
|
84
|
+
return false if @attribute_methods_generated
|
85
|
+
superclass.define_attribute_methods unless self == base_class
|
86
|
+
super(column_names)
|
87
|
+
@attribute_methods_generated = true
|
88
|
+
end
|
89
|
+
true
|
90
|
+
end
|
91
|
+
|
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
|
97
|
+
end
|
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
|
113
|
+
def instance_method_already_implemented?(method_name)
|
114
|
+
if dangerous_attribute_method?(method_name)
|
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."
|
116
|
+
end
|
117
|
+
|
118
|
+
if superclass == Base
|
119
|
+
super
|
120
|
+
else
|
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
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# A method name is 'dangerous' if it is already (re)defined by Active Record, but
|
130
|
+
# not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
|
131
|
+
def dangerous_attribute_method?(name) # :nodoc:
|
132
|
+
method_defined_within?(name, Base)
|
133
|
+
end
|
134
|
+
|
135
|
+
def method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
|
136
|
+
if klass.method_defined?(name) || klass.private_method_defined?(name)
|
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
|
157
|
+
else
|
158
|
+
true
|
159
|
+
end
|
160
|
+
else
|
161
|
+
false
|
162
|
+
end
|
163
|
+
end
|
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
|
174
|
+
def attribute_method?(attribute)
|
175
|
+
super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
|
176
|
+
end
|
177
|
+
|
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"]
|
186
|
+
def attribute_names
|
187
|
+
@attribute_names ||= if !abstract_class? && table_exists?
|
188
|
+
column_names
|
189
|
+
else
|
190
|
+
[]
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
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
|
214
|
+
end
|
215
|
+
column
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
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)
|
245
|
+
end
|
246
|
+
|
247
|
+
return true
|
248
|
+
end
|
249
|
+
|
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
|
259
|
+
def has_attribute?(attr_name)
|
260
|
+
@attributes.key?(attr_name.to_s)
|
261
|
+
end
|
262
|
+
|
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"]
|
271
|
+
def attribute_names
|
272
|
+
@attributes.keys
|
273
|
+
end
|
274
|
+
|
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}
|
283
|
+
def attributes
|
284
|
+
@attributes.to_hash
|
285
|
+
end
|
286
|
+
|
287
|
+
# Returns an <tt>#inspect</tt>-like string for the value of the
|
288
|
+
# attribute +attr_name+. String attributes are truncated up to 50
|
289
|
+
# characters, Date and Time attributes are returned in the
|
290
|
+
# <tt>:db</tt> format, Array attributes are truncated up to 10 values.
|
291
|
+
# Other attributes return the value of <tt>#inspect</tt> without
|
292
|
+
# modification.
|
293
|
+
#
|
294
|
+
# person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
|
295
|
+
#
|
296
|
+
# person.attribute_for_inspect(:name)
|
297
|
+
# # => "\"David Heinemeier Hansson David Heinemeier Hansson ...\""
|
298
|
+
#
|
299
|
+
# person.attribute_for_inspect(:created_at)
|
300
|
+
# # => "\"2012-10-22 00:15:07\""
|
301
|
+
#
|
302
|
+
# person.attribute_for_inspect(:tag_ids)
|
303
|
+
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]"
|
304
|
+
def attribute_for_inspect(attr_name)
|
305
|
+
value = read_attribute(attr_name)
|
306
|
+
|
307
|
+
if value.is_a?(String) && value.length > 50
|
308
|
+
"#{value[0, 50]}...".inspect
|
309
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
310
|
+
%("#{value.to_s(:db)}")
|
311
|
+
elsif value.is_a?(Array) && value.size > 10
|
312
|
+
inspected = value.first(10).inspect
|
313
|
+
%(#{inspected[0...-1]}, ...])
|
314
|
+
else
|
315
|
+
value.inspect
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
# Returns +true+ if the specified +attribute+ has been set by the user or by a
|
320
|
+
# database load and is neither +nil+ nor <tt>empty?</tt> (the latter only applies
|
321
|
+
# to objects that respond to <tt>empty?</tt>, most notably Strings). Otherwise, +false+.
|
322
|
+
# Note that it always returns +true+ with boolean attributes.
|
323
|
+
#
|
324
|
+
# class Task < ActiveRecord::Base
|
325
|
+
# end
|
326
|
+
#
|
327
|
+
# task = Task.new(title: '', is_done: false)
|
328
|
+
# task.attribute_present?(:title) # => false
|
329
|
+
# task.attribute_present?(:is_done) # => true
|
330
|
+
# task.title = 'Buy milk'
|
331
|
+
# task.is_done = true
|
332
|
+
# task.attribute_present?(:title) # => true
|
333
|
+
# task.attribute_present?(:is_done) # => true
|
334
|
+
def attribute_present?(attribute)
|
335
|
+
value = _read_attribute(attribute)
|
336
|
+
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
337
|
+
end
|
338
|
+
|
339
|
+
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
|
340
|
+
# "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
|
341
|
+
# <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
|
342
|
+
#
|
343
|
+
# Note: +:id+ is always present.
|
344
|
+
#
|
345
|
+
# Alias for the <tt>read_attribute</tt> method.
|
346
|
+
#
|
347
|
+
# class Person < ActiveRecord::Base
|
348
|
+
# belongs_to :organization
|
349
|
+
# end
|
350
|
+
#
|
351
|
+
# person = Person.new(name: 'Francesco', age: '22')
|
352
|
+
# person[:name] # => "Francesco"
|
353
|
+
# person[:age] # => 22
|
354
|
+
#
|
355
|
+
# person = Person.select('id').first
|
356
|
+
# person[:name] # => ActiveModel::MissingAttributeError: missing attribute: name
|
357
|
+
# person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute: organization_id
|
358
|
+
def [](attr_name)
|
359
|
+
read_attribute(attr_name) { |n| missing_attribute(n, caller) }
|
360
|
+
end
|
361
|
+
|
362
|
+
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
|
363
|
+
# (Alias for the protected <tt>write_attribute</tt> method).
|
364
|
+
#
|
365
|
+
# class Person < ActiveRecord::Base
|
366
|
+
# end
|
367
|
+
#
|
368
|
+
# person = Person.new
|
369
|
+
# person[:age] = '22'
|
370
|
+
# person[:age] # => 22
|
371
|
+
# person[:age] # => Fixnum
|
372
|
+
def []=(attr_name, value)
|
373
|
+
write_attribute(attr_name, value)
|
374
|
+
end
|
375
|
+
|
376
|
+
protected
|
377
|
+
|
378
|
+
def clone_attribute_value(reader_method, attribute_name) # :nodoc:
|
379
|
+
value = send(reader_method, attribute_name)
|
380
|
+
value.duplicable? ? value.clone : value
|
381
|
+
rescue TypeError, NoMethodError
|
382
|
+
value
|
383
|
+
end
|
384
|
+
|
385
|
+
def arel_attributes_with_values_for_create(attribute_names) # :nodoc:
|
386
|
+
arel_attributes_with_values(attributes_for_create(attribute_names))
|
387
|
+
end
|
388
|
+
|
389
|
+
def arel_attributes_with_values_for_update(attribute_names) # :nodoc:
|
390
|
+
arel_attributes_with_values(attributes_for_update(attribute_names))
|
391
|
+
end
|
392
|
+
|
393
|
+
def attribute_method?(attr_name) # :nodoc:
|
394
|
+
# We check defined? because Syck calls respond_to? before actually calling initialize.
|
395
|
+
defined?(@attributes) && @attributes.key?(attr_name)
|
396
|
+
end
|
397
|
+
|
398
|
+
private
|
399
|
+
|
400
|
+
# Returns a Hash of the Arel::Attributes and attribute values that have been
|
401
|
+
# typecasted for use in an Arel insert/update method.
|
402
|
+
def arel_attributes_with_values(attribute_names)
|
403
|
+
attrs = {}
|
404
|
+
arel_table = self.class.arel_table
|
405
|
+
|
406
|
+
attribute_names.each do |name|
|
407
|
+
attrs[arel_table[name]] = typecasted_attribute_value(name)
|
408
|
+
end
|
409
|
+
attrs
|
410
|
+
end
|
411
|
+
|
412
|
+
# Filters the primary keys and readonly attributes from the attribute names.
|
413
|
+
def attributes_for_update(attribute_names)
|
414
|
+
attribute_names.reject do |name|
|
415
|
+
readonly_attribute?(name)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
# Filters out the primary keys, from the attribute names, when the primary
|
420
|
+
# key is to be generated (e.g. the id attribute has no value).
|
421
|
+
def attributes_for_create(attribute_names)
|
422
|
+
attribute_names.reject do |name|
|
423
|
+
pk_attribute?(name) && id.nil?
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
def readonly_attribute?(name)
|
428
|
+
self.class.readonly_attributes.include?(name)
|
429
|
+
end
|
430
|
+
|
431
|
+
def pk_attribute?(name)
|
432
|
+
name == self.class.primary_key
|
433
|
+
end
|
434
|
+
|
435
|
+
def typecasted_attribute_value(name)
|
436
|
+
_read_attribute(name)
|
437
|
+
end
|
438
|
+
end
|
439
|
+
end
|