activeentity 0.0.1.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +42 -0
- data/README.md +145 -0
- data/Rakefile +29 -0
- data/lib/active_entity.rb +73 -0
- data/lib/active_entity/aggregations.rb +276 -0
- data/lib/active_entity/associations.rb +146 -0
- data/lib/active_entity/associations/embedded/association.rb +134 -0
- data/lib/active_entity/associations/embedded/builder/association.rb +100 -0
- data/lib/active_entity/associations/embedded/builder/collection_association.rb +69 -0
- data/lib/active_entity/associations/embedded/builder/embedded_in.rb +38 -0
- data/lib/active_entity/associations/embedded/builder/embeds_many.rb +13 -0
- data/lib/active_entity/associations/embedded/builder/embeds_one.rb +16 -0
- data/lib/active_entity/associations/embedded/builder/singular_association.rb +28 -0
- data/lib/active_entity/associations/embedded/collection_association.rb +188 -0
- data/lib/active_entity/associations/embedded/collection_proxy.rb +310 -0
- data/lib/active_entity/associations/embedded/embedded_in_association.rb +31 -0
- data/lib/active_entity/associations/embedded/embeds_many_association.rb +15 -0
- data/lib/active_entity/associations/embedded/embeds_one_association.rb +19 -0
- data/lib/active_entity/associations/embedded/singular_association.rb +35 -0
- data/lib/active_entity/attribute_assignment.rb +85 -0
- data/lib/active_entity/attribute_decorators.rb +90 -0
- data/lib/active_entity/attribute_methods.rb +330 -0
- data/lib/active_entity/attribute_methods/before_type_cast.rb +78 -0
- data/lib/active_entity/attribute_methods/primary_key.rb +98 -0
- data/lib/active_entity/attribute_methods/query.rb +35 -0
- data/lib/active_entity/attribute_methods/read.rb +47 -0
- data/lib/active_entity/attribute_methods/serialization.rb +90 -0
- data/lib/active_entity/attribute_methods/time_zone_conversion.rb +91 -0
- data/lib/active_entity/attribute_methods/write.rb +63 -0
- data/lib/active_entity/attributes.rb +165 -0
- data/lib/active_entity/base.rb +303 -0
- data/lib/active_entity/coders/json.rb +15 -0
- data/lib/active_entity/coders/yaml_column.rb +50 -0
- data/lib/active_entity/core.rb +281 -0
- data/lib/active_entity/define_callbacks.rb +17 -0
- data/lib/active_entity/enum.rb +234 -0
- data/lib/active_entity/errors.rb +80 -0
- data/lib/active_entity/gem_version.rb +17 -0
- data/lib/active_entity/inheritance.rb +278 -0
- data/lib/active_entity/integration.rb +78 -0
- data/lib/active_entity/locale/en.yml +45 -0
- data/lib/active_entity/model_schema.rb +115 -0
- data/lib/active_entity/nested_attributes.rb +592 -0
- data/lib/active_entity/readonly_attributes.rb +47 -0
- data/lib/active_entity/reflection.rb +441 -0
- data/lib/active_entity/serialization.rb +25 -0
- data/lib/active_entity/store.rb +242 -0
- data/lib/active_entity/translation.rb +24 -0
- data/lib/active_entity/type.rb +73 -0
- data/lib/active_entity/type/date.rb +9 -0
- data/lib/active_entity/type/date_time.rb +9 -0
- data/lib/active_entity/type/decimal_without_scale.rb +15 -0
- data/lib/active_entity/type/hash_lookup_type_map.rb +25 -0
- data/lib/active_entity/type/internal/timezone.rb +17 -0
- data/lib/active_entity/type/json.rb +30 -0
- data/lib/active_entity/type/modifiers/array.rb +72 -0
- data/lib/active_entity/type/registry.rb +92 -0
- data/lib/active_entity/type/serialized.rb +71 -0
- data/lib/active_entity/type/text.rb +11 -0
- data/lib/active_entity/type/time.rb +21 -0
- data/lib/active_entity/type/type_map.rb +62 -0
- data/lib/active_entity/type/unsigned_integer.rb +17 -0
- data/lib/active_entity/validate_embedded_association.rb +305 -0
- data/lib/active_entity/validations.rb +50 -0
- data/lib/active_entity/validations/absence.rb +25 -0
- data/lib/active_entity/validations/associated.rb +60 -0
- data/lib/active_entity/validations/length.rb +26 -0
- data/lib/active_entity/validations/presence.rb +68 -0
- data/lib/active_entity/validations/subset.rb +76 -0
- data/lib/active_entity/validations/uniqueness_in_embedding.rb +99 -0
- data/lib/active_entity/version.rb +10 -0
- data/lib/tasks/active_entity_tasks.rake +6 -0
- metadata +155 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveEntity
|
4
|
+
module AttributeMethods
|
5
|
+
# = Active Entity Attribute Methods Before Type Cast
|
6
|
+
#
|
7
|
+
# ActiveEntity::AttributeMethods::BeforeTypeCast provides a way to
|
8
|
+
# read the value of the attributes before typecasting and deserialization.
|
9
|
+
#
|
10
|
+
# class Task < ActiveEntity::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"
|
28
|
+
module BeforeTypeCast
|
29
|
+
extend ActiveSupport::Concern
|
30
|
+
|
31
|
+
included do
|
32
|
+
attribute_method_suffix "_before_type_cast"
|
33
|
+
attribute_method_suffix "_came_from_user?"
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the value of the attribute identified by +attr_name+ before
|
37
|
+
# typecasting and deserialization.
|
38
|
+
#
|
39
|
+
# class Task < ActiveEntity::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"
|
48
|
+
def read_attribute_before_type_cast(attr_name)
|
49
|
+
@attributes[attr_name.to_s].value_before_type_cast
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a hash of attributes before typecasting and deserialization.
|
53
|
+
#
|
54
|
+
# class Task < ActiveEntity::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}
|
62
|
+
def attributes_before_type_cast
|
63
|
+
@attributes.values_before_type_cast
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# Handle *_before_type_cast for method_missing.
|
69
|
+
def attribute_before_type_cast(attribute_name)
|
70
|
+
read_attribute_before_type_cast(attribute_name)
|
71
|
+
end
|
72
|
+
|
73
|
+
def attribute_came_from_user?(attribute_name)
|
74
|
+
@attributes[attribute_name].came_from_user?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
|
5
|
+
module ActiveEntity
|
6
|
+
module AttributeMethods
|
7
|
+
module PrimaryKey
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
# Returns this record's primary key value wrapped in an array if one is
|
11
|
+
# available.
|
12
|
+
def to_key
|
13
|
+
key = id
|
14
|
+
[key] if key
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the primary key column's value.
|
18
|
+
def id
|
19
|
+
primary_key = self.class.primary_key
|
20
|
+
_read_attribute(primary_key) if primary_key
|
21
|
+
end
|
22
|
+
|
23
|
+
# Sets the primary key column's value.
|
24
|
+
def id=(value)
|
25
|
+
primary_key = self.class.primary_key
|
26
|
+
_write_attribute(primary_key, value) if primary_key
|
27
|
+
end
|
28
|
+
|
29
|
+
# Queries the primary key column's value.
|
30
|
+
def id?
|
31
|
+
query_attribute(self.class.primary_key)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the primary key column's value before type cast.
|
35
|
+
def id_before_type_cast
|
36
|
+
read_attribute_before_type_cast(self.class.primary_key)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the primary key column's previous value.
|
40
|
+
def id_was
|
41
|
+
attribute_was(self.class.primary_key)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def attribute_method?(attr_name)
|
47
|
+
attr_name == "id" || super
|
48
|
+
end
|
49
|
+
|
50
|
+
module ClassMethods
|
51
|
+
ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was id_in_database).to_set
|
52
|
+
|
53
|
+
def instance_method_already_implemented?(method_name)
|
54
|
+
super || primary_key && ID_ATTRIBUTE_METHODS.include?(method_name)
|
55
|
+
end
|
56
|
+
|
57
|
+
def dangerous_attribute_method?(method_name)
|
58
|
+
super && !ID_ATTRIBUTE_METHODS.include?(method_name)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Defines the primary key field -- can be overridden in subclasses.
|
62
|
+
# Overwriting will negate any effect of the +primary_key_prefix_type+
|
63
|
+
# setting, though.
|
64
|
+
def primary_key
|
65
|
+
unless defined? @primary_key
|
66
|
+
@primary_key =
|
67
|
+
if has_attribute?("id")
|
68
|
+
"id"
|
69
|
+
else
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
@primary_key
|
75
|
+
end
|
76
|
+
|
77
|
+
# Sets the name of the primary key column.
|
78
|
+
#
|
79
|
+
# class Project < ActiveEntity::Base
|
80
|
+
# self.primary_key = 'sysid'
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# You can also define the #primary_key method yourself:
|
84
|
+
#
|
85
|
+
# class Project < ActiveEntity::Base
|
86
|
+
# def self.primary_key
|
87
|
+
# 'foo_' + super
|
88
|
+
# end
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# Project.primary_key # => "foo_id"
|
92
|
+
def primary_key=(value)
|
93
|
+
@primary_key = value&.to_s
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveEntity
|
4
|
+
module AttributeMethods
|
5
|
+
module Query
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attribute_method_suffix "?"
|
10
|
+
end
|
11
|
+
|
12
|
+
def query_attribute(attr_name)
|
13
|
+
value = self[attr_name]
|
14
|
+
|
15
|
+
case value
|
16
|
+
when true then true
|
17
|
+
when false, nil then false
|
18
|
+
else
|
19
|
+
if Numeric === value || value !~ /[^0-9]/
|
20
|
+
!value.to_i.zero?
|
21
|
+
else
|
22
|
+
return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value)
|
23
|
+
!value.blank?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
# Handle *? for method_missing.
|
30
|
+
def attribute?(attribute_name)
|
31
|
+
query_attribute(attribute_name)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveEntity
|
4
|
+
module AttributeMethods
|
5
|
+
module Read
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
module ClassMethods # :nodoc:
|
9
|
+
private
|
10
|
+
|
11
|
+
def define_method_attribute(name)
|
12
|
+
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
13
|
+
generated_attribute_methods, name
|
14
|
+
) do |temp_method_name, attr_name_expr|
|
15
|
+
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
16
|
+
def #{temp_method_name}
|
17
|
+
name = #{attr_name_expr}
|
18
|
+
_read_attribute(name) { |n| missing_attribute(n, caller) }
|
19
|
+
end
|
20
|
+
RUBY
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the value of the attribute identified by <tt>attr_name</tt> after
|
26
|
+
# it has been typecast (for example, "2004-12-12" in a date column is cast
|
27
|
+
# to a date object, like Date.new(2004, 12, 12)).
|
28
|
+
def read_attribute(attr_name, &block)
|
29
|
+
name = attr_name.to_s
|
30
|
+
if self.class.attribute_alias?(name)
|
31
|
+
name = self.class.attribute_alias(name)
|
32
|
+
end
|
33
|
+
|
34
|
+
_read_attribute(name, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
# This method exists to avoid the expensive primary_key check internally, without
|
38
|
+
# breaking compatibility with the read_attribute API
|
39
|
+
def _read_attribute(attr_name, &block) # :nodoc
|
40
|
+
@attributes.fetch_value(attr_name.to_s, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
alias :attribute :_read_attribute
|
44
|
+
private :attribute
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveEntity
|
4
|
+
module AttributeMethods
|
5
|
+
module Serialization
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
class ColumnNotSerializableError < StandardError
|
9
|
+
def initialize(name, type)
|
10
|
+
super <<~EOS
|
11
|
+
Column `#{name}` of type #{type.class} does not support `serialize` feature.
|
12
|
+
Usually it means that you are trying to use `serialize`
|
13
|
+
on a column that already implements serialization natively.
|
14
|
+
EOS
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
# If you have an attribute that needs to be saved to the database as an
|
20
|
+
# object, and retrieved as the same object, then specify the name of that
|
21
|
+
# attribute using this method and it will be handled automatically. The
|
22
|
+
# serialization is done through YAML. If +class_name+ is specified, the
|
23
|
+
# serialized object must be of that class on assignment and retrieval.
|
24
|
+
# Otherwise SerializationTypeMismatch will be raised.
|
25
|
+
#
|
26
|
+
# Empty objects as <tt>{}</tt>, in the case of +Hash+, or <tt>[]</tt>, in the case of
|
27
|
+
# +Array+, will always be persisted as null.
|
28
|
+
#
|
29
|
+
# Keep in mind that database adapters handle certain serialization tasks
|
30
|
+
# for you. For instance: +json+ and +jsonb+ types in PostgreSQL will be
|
31
|
+
# converted between JSON object/array syntax and Ruby +Hash+ or +Array+
|
32
|
+
# objects transparently. There is no need to use #serialize in this
|
33
|
+
# case.
|
34
|
+
#
|
35
|
+
# For more complex cases, such as conversion to or from your application
|
36
|
+
# domain objects, consider using the ActiveEntity::Attributes API.
|
37
|
+
#
|
38
|
+
# ==== Parameters
|
39
|
+
#
|
40
|
+
# * +attr_name+ - The field name that should be serialized.
|
41
|
+
# * +class_name_or_coder+ - Optional, a coder object, which responds to +.load+ and +.dump+
|
42
|
+
# or a class name that the object type should be equal to.
|
43
|
+
#
|
44
|
+
# ==== Example
|
45
|
+
#
|
46
|
+
# # Serialize a preferences attribute.
|
47
|
+
# class User < ActiveEntity::Base
|
48
|
+
# serialize :preferences
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# # Serialize preferences using JSON as coder.
|
52
|
+
# class User < ActiveEntity::Base
|
53
|
+
# serialize :preferences, JSON
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# # Serialize preferences as Hash using YAML coder.
|
57
|
+
# class User < ActiveEntity::Base
|
58
|
+
# serialize :preferences, Hash
|
59
|
+
# end
|
60
|
+
def serialize(attr_name, class_name_or_coder = Object)
|
61
|
+
# When ::JSON is used, force it to go through the Active Support JSON encoder
|
62
|
+
# to ensure special objects (e.g. Active Entity models) are dumped correctly
|
63
|
+
# using the #as_json hook.
|
64
|
+
coder = if class_name_or_coder == ::JSON
|
65
|
+
Coders::JSON
|
66
|
+
elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
|
67
|
+
class_name_or_coder
|
68
|
+
else
|
69
|
+
Coders::YAMLColumn.new(attr_name, class_name_or_coder)
|
70
|
+
end
|
71
|
+
|
72
|
+
decorate_attribute_type(attr_name, :serialize) do |type|
|
73
|
+
if type_incompatible_with_serialize?(type, class_name_or_coder)
|
74
|
+
raise ColumnNotSerializableError.new(attr_name, type)
|
75
|
+
end
|
76
|
+
|
77
|
+
Type::Serialized.new(type, coder)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def type_incompatible_with_serialize?(type, class_name)
|
84
|
+
type.is_a?(ActiveEntity::Type::Json) && class_name == ::JSON ||
|
85
|
+
type.respond_to?(:type_cast_array, true) && class_name == ::Array
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveEntity
|
4
|
+
module AttributeMethods
|
5
|
+
module TimeZoneConversion
|
6
|
+
class TimeZoneConverter < DelegateClass(Type::Value) # :nodoc:
|
7
|
+
def deserialize(value)
|
8
|
+
convert_time_to_time_zone(super)
|
9
|
+
end
|
10
|
+
|
11
|
+
def cast(value)
|
12
|
+
return if value.nil?
|
13
|
+
|
14
|
+
if value.is_a?(Hash)
|
15
|
+
set_time_zone_without_conversion(super)
|
16
|
+
elsif value.respond_to?(:in_time_zone)
|
17
|
+
begin
|
18
|
+
super(user_input_in_time_zone(value)) || super
|
19
|
+
rescue ArgumentError
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
else
|
23
|
+
map_avoiding_infinite_recursion(super) { |v| cast(v) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def convert_time_to_time_zone(value)
|
30
|
+
return if value.nil?
|
31
|
+
|
32
|
+
if value.acts_like?(:time)
|
33
|
+
value.in_time_zone
|
34
|
+
elsif value.is_a?(::Float)
|
35
|
+
value
|
36
|
+
else
|
37
|
+
map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_time_zone_without_conversion(value)
|
42
|
+
::Time.zone.local_to_utc(value).try(:in_time_zone) if value
|
43
|
+
end
|
44
|
+
|
45
|
+
def map_avoiding_infinite_recursion(value)
|
46
|
+
map(value) do |v|
|
47
|
+
if value.equal?(v)
|
48
|
+
nil
|
49
|
+
else
|
50
|
+
yield(v)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
extend ActiveSupport::Concern
|
57
|
+
|
58
|
+
included do
|
59
|
+
mattr_accessor :time_zone_aware_attributes, instance_writer: false, default: false
|
60
|
+
|
61
|
+
class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false, default: []
|
62
|
+
class_attribute :time_zone_aware_types, instance_writer: false, default: [ :datetime, :time ]
|
63
|
+
end
|
64
|
+
|
65
|
+
module ClassMethods # :nodoc:
|
66
|
+
private
|
67
|
+
|
68
|
+
def inherited(subclass)
|
69
|
+
super
|
70
|
+
# We need to apply this decorator here, rather than on module inclusion. The closure
|
71
|
+
# created by the matcher would otherwise evaluate for `ActiveEntity::Base`, not the
|
72
|
+
# sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
|
73
|
+
# `skip_time_zone_conversion_for_attributes` would not be picked up.
|
74
|
+
subclass.class_eval do
|
75
|
+
matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
|
76
|
+
decorate_matching_attribute_types(matcher, "_time_zone_conversion") do |type|
|
77
|
+
TimeZoneConverter.new(type)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def create_time_zone_conversion_attribute?(name, cast_type)
|
83
|
+
enabled_for_column = time_zone_aware_attributes &&
|
84
|
+
!skip_time_zone_conversion_for_attributes.include?(name.to_sym)
|
85
|
+
|
86
|
+
enabled_for_column && time_zone_aware_types.include?(cast_type.type)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveEntity
|
4
|
+
module AttributeMethods
|
5
|
+
module Write
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attribute_method_suffix "="
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods # :nodoc:
|
13
|
+
private
|
14
|
+
|
15
|
+
def define_method_attribute=(name)
|
16
|
+
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
17
|
+
generated_attribute_methods, name, writer: true,
|
18
|
+
) do |temp_method_name, attr_name_expr|
|
19
|
+
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
20
|
+
def #{temp_method_name}(value)
|
21
|
+
name = #{attr_name_expr}
|
22
|
+
_write_attribute(name, value)
|
23
|
+
end
|
24
|
+
RUBY
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Updates the attribute identified by <tt>attr_name</tt> with the
|
30
|
+
# specified +value+. Empty strings for Integer and Float columns are
|
31
|
+
# turned into +nil+.
|
32
|
+
def write_attribute(attr_name, value)
|
33
|
+
name = attr_name.to_s
|
34
|
+
if self.class.attribute_alias?(name)
|
35
|
+
name = self.class.attribute_alias(name)
|
36
|
+
end
|
37
|
+
|
38
|
+
_write_attribute(name, value)
|
39
|
+
end
|
40
|
+
|
41
|
+
# This method exists to avoid the expensive primary_key check internally, without
|
42
|
+
# breaking compatibility with the write_attribute API
|
43
|
+
def _write_attribute(attr_name, value) # :nodoc:
|
44
|
+
return if readonly_attribute?(attr_name) && readonly_enabled?
|
45
|
+
|
46
|
+
@attributes.write_from_user(attr_name.to_s, value)
|
47
|
+
value
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def write_attribute_without_type_cast(attr_name, value)
|
52
|
+
name = attr_name.to_s
|
53
|
+
@attributes.write_cast_value(name, value)
|
54
|
+
value
|
55
|
+
end
|
56
|
+
|
57
|
+
# Handle *= for method_missing.
|
58
|
+
def attribute=(attribute_name, value)
|
59
|
+
_write_attribute(attribute_name, value)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|