neo4j 7.2.3 → 8.0.0.alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -46
- data/Gemfile +15 -14
- data/README.md +21 -14
- data/bin/neo4j-jars +1 -1
- data/lib/neo4j.rb +12 -1
- data/lib/neo4j/active_base.rb +68 -0
- data/lib/neo4j/active_base/session_registry.rb +12 -0
- data/lib/neo4j/active_node.rb +13 -21
- data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +6 -6
- data/lib/neo4j/active_node/enum.rb +3 -6
- data/lib/neo4j/active_node/has_n.rb +24 -19
- data/lib/neo4j/active_node/has_n/association.rb +6 -2
- data/lib/neo4j/active_node/has_n/association/rel_factory.rb +1 -1
- data/lib/neo4j/active_node/has_n/association/rel_wrapper.rb +1 -1
- data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +1 -1
- data/lib/neo4j/active_node/id_property.rb +52 -15
- data/lib/neo4j/active_node/labels.rb +32 -10
- data/lib/neo4j/active_node/labels/index.rb +5 -55
- data/lib/neo4j/active_node/node_list_formatter.rb +13 -0
- data/lib/neo4j/active_node/node_wrapper.rb +39 -37
- data/lib/neo4j/active_node/persistence.rb +27 -13
- data/lib/neo4j/active_node/query/query_proxy.rb +11 -9
- data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +4 -4
- data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +1 -0
- data/lib/neo4j/active_node/query/query_proxy_link.rb +13 -9
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +76 -8
- data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +1 -1
- data/lib/neo4j/active_node/query_methods.rb +3 -3
- data/lib/neo4j/active_node/scope.rb +24 -7
- data/lib/neo4j/active_rel.rb +21 -3
- data/lib/neo4j/active_rel/initialize.rb +2 -2
- data/lib/neo4j/active_rel/persistence.rb +32 -6
- data/lib/neo4j/active_rel/persistence/query_factory.rb +3 -3
- data/lib/neo4j/active_rel/property.rb +9 -9
- data/lib/neo4j/active_rel/query.rb +6 -4
- data/lib/neo4j/active_rel/rel_wrapper.rb +24 -16
- data/lib/neo4j/active_rel/related_node.rb +5 -1
- data/lib/neo4j/active_rel/types.rb +2 -2
- data/lib/neo4j/config.rb +0 -1
- data/lib/neo4j/errors.rb +3 -0
- data/lib/neo4j/migration.rb +90 -71
- data/lib/neo4j/migrations.rb +10 -0
- data/lib/neo4j/migrations/base.rb +44 -0
- data/lib/neo4j/migrations/helpers.rb +101 -0
- data/lib/neo4j/migrations/helpers/id_property.rb +75 -0
- data/lib/neo4j/migrations/helpers/relationships.rb +66 -0
- data/lib/neo4j/migrations/helpers/schema.rb +53 -0
- data/lib/neo4j/migrations/migration_file.rb +24 -0
- data/lib/neo4j/migrations/runner.rb +110 -0
- data/lib/neo4j/migrations/schema_migration.rb +9 -0
- data/lib/neo4j/model_schema.rb +100 -0
- data/lib/neo4j/railtie.rb +29 -110
- data/lib/neo4j/schema/operation.rb +24 -13
- data/lib/neo4j/session_manager.rb +137 -0
- data/lib/neo4j/shared.rb +20 -11
- data/lib/neo4j/shared/attributes.rb +10 -16
- data/lib/neo4j/shared/callbacks.rb +3 -3
- data/lib/neo4j/shared/cypher.rb +1 -1
- data/lib/neo4j/shared/declared_properties.rb +1 -1
- data/lib/neo4j/shared/declared_property.rb +1 -1
- data/lib/neo4j/shared/enum.rb +6 -18
- data/lib/neo4j/shared/identity.rb +27 -21
- data/lib/neo4j/shared/persistence.rb +26 -17
- data/lib/neo4j/shared/property.rb +5 -2
- data/lib/neo4j/shared/query_factory.rb +4 -5
- data/lib/neo4j/shared/type_converters.rb +8 -9
- data/lib/neo4j/shared/validations.rb +1 -5
- data/lib/neo4j/tasks/migration.rake +83 -2
- data/lib/neo4j/version.rb +1 -1
- data/lib/rails/generators/neo4j/migration/migration_generator.rb +14 -0
- data/lib/rails/generators/neo4j/migration/templates/migration.erb +9 -0
- data/lib/rails/generators/neo4j/model/model_generator.rb +1 -3
- data/lib/rails/generators/neo4j_generator.rb +1 -0
- data/neo4j.gemspec +3 -3
- metadata +58 -65
- data/bin/rake +0 -17
- data/lib/neo4j/shared/permitted_attributes.rb +0 -28
data/lib/neo4j/shared.rb
CHANGED
@@ -10,21 +10,26 @@ module Neo4j
|
|
10
10
|
include ActiveModel::Serializers::JSON
|
11
11
|
|
12
12
|
module ClassMethods
|
13
|
-
|
13
|
+
# TODO: Deprecate neo4j_session_name(name)
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
# remove?
|
16
|
+
def neo4j_session
|
17
|
+
Neo4j::ActiveBase.current_session
|
18
|
+
end
|
17
19
|
|
18
|
-
|
20
|
+
# remove?
|
21
|
+
def current_transaction
|
22
|
+
Neo4j::ActiveBase.current_transaction
|
19
23
|
end
|
20
24
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
# This should be used everywhere. Should make it easy
|
26
|
+
# to support a session-per-model system
|
27
|
+
def neo4j_query(*args)
|
28
|
+
Neo4j::ActiveBase.query(*args)
|
29
|
+
end
|
30
|
+
|
31
|
+
def new_query
|
32
|
+
Neo4j::ActiveBase.new_query
|
28
33
|
end
|
29
34
|
end
|
30
35
|
|
@@ -47,5 +52,9 @@ module Neo4j
|
|
47
52
|
def declared_properties
|
48
53
|
self.class.declared_properties
|
49
54
|
end
|
55
|
+
|
56
|
+
def neo4j_query(*args)
|
57
|
+
self.class.neo4j_query(*args)
|
58
|
+
end
|
50
59
|
end
|
51
60
|
end
|
@@ -61,20 +61,16 @@ module Neo4j::Shared
|
|
61
61
|
#
|
62
62
|
# @raise [UnknownAttributeError] if the attribute is unknown
|
63
63
|
def write_attribute(name, value)
|
64
|
-
if respond_to? "#{name}="
|
65
|
-
|
66
|
-
|
67
|
-
fail Neo4j::UnknownAttributeError, "unknown attribute: #{name}"
|
68
|
-
end
|
64
|
+
fail Neo4j::UnknownAttributeError, "unknown attribute: #{name}" if !respond_to? "#{name}="
|
65
|
+
|
66
|
+
send "#{name}=", value
|
69
67
|
end
|
70
|
-
|
68
|
+
alias []= write_attribute
|
71
69
|
|
72
70
|
def query_attribute(name)
|
73
|
-
if respond_to? "#{name}?"
|
74
|
-
|
75
|
-
|
76
|
-
fail Neo4j::UnknownAttributeError, "unknown attribute: #{name}"
|
77
|
-
end
|
71
|
+
fail Neo4j::UnknownAttributeError, "unknown attribute: #{name}" if !respond_to? "#{name}?"
|
72
|
+
|
73
|
+
send "#{name}?"
|
78
74
|
end
|
79
75
|
|
80
76
|
private
|
@@ -126,11 +122,9 @@ module Neo4j::Shared
|
|
126
122
|
#
|
127
123
|
# @return [AttributeDefinition] Attribute's definition
|
128
124
|
def attribute(name)
|
129
|
-
if dangerous_attribute?(name)
|
130
|
-
|
131
|
-
|
132
|
-
attribute!(name)
|
133
|
-
end
|
125
|
+
fail Neo4j::DangerousAttributeError, %(an attribute method named "#{name}" would conflict with an existing method) if dangerous_attribute?(name)
|
126
|
+
|
127
|
+
attribute!(name)
|
134
128
|
end
|
135
129
|
|
136
130
|
# Returns an Array of attribute names as Strings
|
@@ -19,7 +19,7 @@ module Neo4j
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def destroy #:nodoc:
|
22
|
-
tx = Neo4j::
|
22
|
+
tx = Neo4j::ActiveBase.new_transaction
|
23
23
|
run_callbacks(:destroy) { super }
|
24
24
|
rescue
|
25
25
|
@_deleted = false
|
@@ -49,13 +49,13 @@ module Neo4j
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def create_model #:nodoc:
|
52
|
-
|
52
|
+
self.class.run_transaction do
|
53
53
|
run_callbacks(:create) { super }
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
57
|
def update_model(*) #:nodoc:
|
58
|
-
|
58
|
+
self.class.run_transaction do
|
59
59
|
run_callbacks(:update) { super }
|
60
60
|
end
|
61
61
|
end
|
data/lib/neo4j/shared/cypher.rb
CHANGED
@@ -156,7 +156,7 @@ module Neo4j::Shared
|
|
156
156
|
|
157
157
|
def inject_defaults!(object, props)
|
158
158
|
declared_property_defaults.each_pair do |k, v|
|
159
|
-
props[k.to_sym] = v if object.send(k).nil? && props[k.to_sym].nil?
|
159
|
+
props[k.to_sym] = v.respond_to?(:call) ? v.call : v if object.send(k).nil? && props[k.to_sym].nil?
|
160
160
|
end
|
161
161
|
props
|
162
162
|
end
|
data/lib/neo4j/shared/enum.rb
CHANGED
@@ -36,9 +36,6 @@ module Neo4j::Shared
|
|
36
36
|
# media.video_type!
|
37
37
|
# media.video_type? # => true
|
38
38
|
#
|
39
|
-
# @example Disable index: :exact for enum elements
|
40
|
-
# Media.enum type: [:image, :video, :unknown], _index: false
|
41
|
-
#
|
42
39
|
# @example Define a custom mapping for keys-numbers
|
43
40
|
# Media.enum type: { image: 1, video: 2, unknown: 3 }
|
44
41
|
#
|
@@ -67,13 +64,11 @@ module Neo4j::Shared
|
|
67
64
|
end
|
68
65
|
end
|
69
66
|
|
70
|
-
VALID_OPTIONS_FOR_ENUMS = [:
|
71
|
-
DEFAULT_OPTIONS_FOR_ENUMS = {
|
72
|
-
_index: true
|
73
|
-
}
|
67
|
+
VALID_OPTIONS_FOR_ENUMS = [:_prefix, :_suffix]
|
68
|
+
DEFAULT_OPTIONS_FOR_ENUMS = {}
|
74
69
|
|
75
70
|
def split_options_and_parameters(parameters)
|
76
|
-
options =
|
71
|
+
options = {}
|
77
72
|
new_parameters = {}
|
78
73
|
parameters.each do |k, v|
|
79
74
|
if VALID_OPTIONS_FOR_ENUMS.include? k
|
@@ -85,16 +80,9 @@ module Neo4j::Shared
|
|
85
80
|
[options, new_parameters]
|
86
81
|
end
|
87
82
|
|
88
|
-
def define_property(property_name, enum_keys,
|
89
|
-
|
90
|
-
|
91
|
-
serialize property_name, Neo4j::Shared::TypeConverters::EnumConverter.new(enum_keys, property_options)
|
92
|
-
end
|
93
|
-
|
94
|
-
def build_property_options(_enum_keys, options = {})
|
95
|
-
{
|
96
|
-
default: options[:_default]
|
97
|
-
}
|
83
|
+
def define_property(property_name, enum_keys, _options)
|
84
|
+
property property_name, default: enum_keys.keys.first # .merge(options)
|
85
|
+
serialize property_name, Neo4j::Shared::TypeConverters::EnumConverter.new(enum_keys)
|
98
86
|
end
|
99
87
|
|
100
88
|
def define_enum_methods(property_name, enum_keys, options)
|
@@ -1,28 +1,34 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
module Neo4j
|
2
|
+
module Shared
|
3
|
+
module Identity
|
4
|
+
def ==(other)
|
5
|
+
other.class == self.class && other.id == id
|
6
|
+
end
|
7
|
+
alias eql? ==
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
# Returns an Enumerable of all (primary) key attributes
|
10
|
+
# or nil if model.persisted? is false
|
11
|
+
def to_key
|
12
|
+
_persisted_obj ? [id] : nil
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
# @return [Integer, nil] the neo4j id of the node if persisted or nil
|
16
|
+
def neo_id
|
17
|
+
_persisted_obj ? _persisted_obj.neo_id : nil
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
def id
|
21
|
+
if self.class.id_property_name
|
22
|
+
send(self.class.id_property_name)
|
23
|
+
else
|
24
|
+
# ActiveRel
|
25
|
+
neo_id
|
26
|
+
end
|
27
|
+
end
|
23
28
|
|
24
|
-
|
25
|
-
|
29
|
+
def hash
|
30
|
+
id.hash
|
31
|
+
end
|
26
32
|
end
|
27
33
|
end
|
28
34
|
end
|
@@ -9,7 +9,7 @@ module Neo4j::Shared
|
|
9
9
|
|
10
10
|
def update_model
|
11
11
|
return if !changed_attributes || changed_attributes.empty?
|
12
|
-
|
12
|
+
neo4j_query(query_as(:n).set(n: props_for_update))
|
13
13
|
changed_attributes.clear
|
14
14
|
end
|
15
15
|
|
@@ -81,14 +81,13 @@ module Neo4j::Shared
|
|
81
81
|
@_create_or_updating = true
|
82
82
|
apply_default_values
|
83
83
|
result = _persisted_obj ? update_model : create_model
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
end
|
84
|
+
current_transaction = Neo4j::ActiveBase.current_transaction
|
85
|
+
|
86
|
+
current_transaction.mark_failed if result == false && current_transaction
|
87
|
+
|
88
|
+
result != false
|
90
89
|
rescue => e
|
91
|
-
|
90
|
+
current_transaction.mark_failed if current_transaction
|
92
91
|
raise e
|
93
92
|
ensure
|
94
93
|
@_create_or_updating = nil
|
@@ -97,7 +96,7 @@ module Neo4j::Shared
|
|
97
96
|
def apply_default_values
|
98
97
|
return if self.class.declared_property_defaults.empty?
|
99
98
|
self.class.declared_property_defaults.each_pair do |key, value|
|
100
|
-
self.send("#{key}=", value) if self.send(key).nil?
|
99
|
+
self.send("#{key}=", value.respond_to?(:call) ? value.call : value) if self.send(key).nil?
|
101
100
|
end
|
102
101
|
end
|
103
102
|
|
@@ -116,16 +115,20 @@ module Neo4j::Shared
|
|
116
115
|
!_persisted_obj
|
117
116
|
end
|
118
117
|
|
119
|
-
|
118
|
+
alias new? new_record?
|
120
119
|
|
121
120
|
def destroy
|
122
121
|
freeze
|
123
|
-
|
122
|
+
|
123
|
+
destroy_query.exec if _persisted_obj
|
124
|
+
|
124
125
|
@_deleted = true
|
125
126
|
end
|
126
127
|
|
127
128
|
def exist?
|
128
|
-
|
129
|
+
return if !_persisted_obj
|
130
|
+
|
131
|
+
neo4j_query(query_as(:n).return('ID(n)')).any?
|
129
132
|
end
|
130
133
|
|
131
134
|
# Returns +true+ if the object was destroyed.
|
@@ -170,14 +173,14 @@ module Neo4j::Shared
|
|
170
173
|
self.attributes = process_attributes(attributes)
|
171
174
|
save
|
172
175
|
end
|
173
|
-
|
176
|
+
alias update_attributes update
|
174
177
|
|
175
178
|
# Same as {#update_attributes}, but raises an exception if saving fails.
|
176
179
|
def update!(attributes)
|
177
180
|
self.attributes = process_attributes(attributes)
|
178
181
|
save!
|
179
182
|
end
|
180
|
-
|
183
|
+
alias update_attributes! update!
|
181
184
|
|
182
185
|
def cache_key
|
183
186
|
if self.new_record?
|
@@ -189,13 +192,19 @@ module Neo4j::Shared
|
|
189
192
|
end
|
190
193
|
end
|
191
194
|
|
195
|
+
module ClassMethods
|
196
|
+
def run_transaction(run_in_tx = true)
|
197
|
+
Neo4j::ActiveBase.run_transaction(run_in_tx) { |tx| yield tx }
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
192
201
|
protected
|
193
202
|
|
194
203
|
def increment_by_query!(match_query, attribute, by, element_name = :n)
|
195
204
|
new_attribute = match_query.with(element_name)
|
196
|
-
|
197
|
-
|
198
|
-
|
205
|
+
.set("#{element_name}.`#{attribute}` = COALESCE(#{element_name}.`#{attribute}`, 0) + {by}")
|
206
|
+
.params(by: by).limit(1)
|
207
|
+
.pluck("#{element_name}.`#{attribute}`").first
|
199
208
|
return false unless new_attribute
|
200
209
|
self[attribute] = new_attribute
|
201
210
|
changed_attributes.delete(attribute)
|
@@ -37,7 +37,7 @@ module Neo4j::Shared
|
|
37
37
|
def read_attribute(name)
|
38
38
|
respond_to?(name) ? send(name) : nil
|
39
39
|
end
|
40
|
-
|
40
|
+
alias [] read_attribute
|
41
41
|
|
42
42
|
def send_props(hash)
|
43
43
|
return hash if hash.blank?
|
@@ -58,7 +58,7 @@ module Neo4j::Shared
|
|
58
58
|
return attributes if attributes.blank?
|
59
59
|
invalid_properties = attributes.keys.map(&:to_s) - self.attributes.keys
|
60
60
|
invalid_properties.reject! { |name| self.respond_to?("#{name}=") }
|
61
|
-
fail UndefinedPropertyError, "Undefined properties: #{invalid_properties.join(',')}" if invalid_properties.
|
61
|
+
fail UndefinedPropertyError, "Undefined properties: #{invalid_properties.join(',')}" if !invalid_properties.empty?
|
62
62
|
end
|
63
63
|
|
64
64
|
def extract_writer_methods!(attributes)
|
@@ -113,6 +113,7 @@ module Neo4j::Shared
|
|
113
113
|
|
114
114
|
def_delegators :declared_properties, :serialized_properties, :serialized_properties=, :serialize, :declared_property_defaults
|
115
115
|
|
116
|
+
VALID_PROPERTY_OPTIONS = %w(type default index constraint serializer typecaster null).map(&:to_sym)
|
116
117
|
# Defines a property on the class
|
117
118
|
#
|
118
119
|
# See active_attr gem for allowed options, e.g which type
|
@@ -142,6 +143,8 @@ module Neo4j::Shared
|
|
142
143
|
# property :name, constraint: :unique
|
143
144
|
# end
|
144
145
|
def property(name, options = {})
|
146
|
+
invalid_option_keys = options.keys.map(&:to_sym) - VALID_PROPERTY_OPTIONS
|
147
|
+
fail ArgumentError, "Invalid options for property `#{name}` on `#{self.name}`: #{invalid_option_keys.join(', ')}" if invalid_option_keys.any?
|
145
148
|
build_property(name, options) do |prop|
|
146
149
|
attribute(prop)
|
147
150
|
end
|
@@ -36,7 +36,7 @@ module Neo4j::Shared
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def base_query
|
39
|
-
@base_query || Neo4j::
|
39
|
+
@base_query || Neo4j::ActiveBase.new_query
|
40
40
|
end
|
41
41
|
|
42
42
|
protected
|
@@ -69,8 +69,7 @@ module Neo4j::Shared
|
|
69
69
|
|
70
70
|
def create_query
|
71
71
|
return match_query if graph_object.persisted?
|
72
|
-
|
73
|
-
base_query.create("(#{identifier}#{labels} {#{identifier}_params})").params(identifier_params => graph_object.props_for_create)
|
72
|
+
base_query.create(identifier => {graph_object.labels_for_create => graph_object.props_for_create})
|
74
73
|
end
|
75
74
|
end
|
76
75
|
|
@@ -85,8 +84,8 @@ module Neo4j::Shared
|
|
85
84
|
return match_query if graph_object.persisted?
|
86
85
|
create_props, set_props = filtered_props
|
87
86
|
base_query.send(graph_object.create_method, query_string).break
|
88
|
-
|
89
|
-
|
87
|
+
.set(identifier => set_props)
|
88
|
+
.params(:"#{identifier}_create_props" => create_props)
|
90
89
|
end
|
91
90
|
|
92
91
|
private
|
@@ -38,7 +38,7 @@ module Neo4j::Shared
|
|
38
38
|
value.to_i
|
39
39
|
end
|
40
40
|
|
41
|
-
|
41
|
+
alias to_ruby to_db
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -55,7 +55,7 @@ module Neo4j::Shared
|
|
55
55
|
def to_db(value)
|
56
56
|
value.to_f
|
57
57
|
end
|
58
|
-
|
58
|
+
alias to_ruby to_db
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
@@ -79,7 +79,7 @@ module Neo4j::Shared
|
|
79
79
|
BigDecimal.new(value.to_s)
|
80
80
|
end
|
81
81
|
end
|
82
|
-
|
82
|
+
alias to_ruby to_db
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
@@ -96,7 +96,7 @@ module Neo4j::Shared
|
|
96
96
|
def to_db(value)
|
97
97
|
value.to_s
|
98
98
|
end
|
99
|
-
|
99
|
+
alias to_ruby to_db
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
@@ -116,7 +116,7 @@ module Neo4j::Shared
|
|
116
116
|
Neo4j::Shared::Boolean
|
117
117
|
end
|
118
118
|
|
119
|
-
|
119
|
+
alias convert_type db_type
|
120
120
|
|
121
121
|
def to_db(value)
|
122
122
|
return false if FALSE_VALUES.include?(value)
|
@@ -130,7 +130,7 @@ module Neo4j::Shared
|
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
133
|
-
|
133
|
+
alias to_ruby to_db
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
@@ -264,9 +264,8 @@ module Neo4j::Shared
|
|
264
264
|
end
|
265
265
|
|
266
266
|
class EnumConverter
|
267
|
-
def initialize(enum_keys
|
267
|
+
def initialize(enum_keys)
|
268
268
|
@enum_keys = enum_keys
|
269
|
-
@options = options
|
270
269
|
end
|
271
270
|
|
272
271
|
def converted?(value)
|
@@ -289,7 +288,7 @@ module Neo4j::Shared
|
|
289
288
|
@enum_keys.key(value) unless value.nil?
|
290
289
|
end
|
291
290
|
|
292
|
-
|
291
|
+
alias call to_ruby
|
293
292
|
|
294
293
|
def to_db(value)
|
295
294
|
if value.is_a?(Array)
|