/
array_type.rb
87 lines (77 loc) · 2.32 KB
/
array_type.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# frozen_string_literal: true
require "active_model"
module StoreModel
module Types
# Implements ActiveModel::Type::Value type for handling an array of
# StoreModel::Model
class ArrayType < ActiveModel::Type::Value
# Initializes type for model class
#
# @param model_klass [StoreModel::Model] model class to handle
#
# @return [StoreModel::Types::ArrayType]
def initialize(model_klass)
@model_klass = model_klass
end
# Returns type
#
# @return [Symbol]
def type
:array
end
# Casts +value+ from DB or user to StoreModel::Model instance
#
# @param value [Object] a value to cast
#
# @return StoreModel::Model
def cast_value(value)
case value
when String then decode_and_initialize(value)
when Array then ensure_model_class(value)
when nil then value
else
raise StoreModel::Types::CastError,
"failed casting #{value.inspect}, only String or Array instances are allowed"
end
end
# Casts a value from the ruby type to a type that the database knows how
# to understand.
#
# @param value [Object] value to serialize
#
# @return [String] serialized value
def serialize(value)
case value
when Array
ActiveSupport::JSON.encode(value)
else
super
end
end
# Determines whether the mutable value has been modified since it was read
#
# @param raw_old_value [Object] old value
# @param new_value [Object] new value
#
# @return [Boolean]
def changed_in_place?(raw_old_value, new_value)
cast_value(raw_old_value) != new_value
end
private
# rubocop:disable Style/RescueModifier
def decode_and_initialize(array_value)
decoded = ActiveSupport::JSON.decode(array_value) rescue []
decoded.map { |attributes| cast_model_type_value(attributes) }
end
# rubocop:enable Style/RescueModifier
def ensure_model_class(array)
array.map do |object|
object.is_a?(@model_klass) ? object : cast_model_type_value(object)
end
end
def cast_model_type_value(value)
@model_klass.to_type.cast_value(value)
end
end
end
end