neo4j_legacy 7.2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1357 -0
- data/CONTRIBUTORS +8 -0
- data/Gemfile +38 -0
- data/README.md +103 -0
- data/bin/neo4j-jars +33 -0
- data/bin/rake +17 -0
- data/config/locales/en.yml +5 -0
- data/config/neo4j/add_classnames.yml +1 -0
- data/config/neo4j/config.yml +35 -0
- data/lib/active_support/per_thread_registry.rb +1 -0
- data/lib/backports/action_controller/metal/strong_parameters.rb +672 -0
- data/lib/backports/active_model/forbidden_attributes_protection.rb +30 -0
- data/lib/backports/active_support/concern.rb +13 -0
- data/lib/backports/active_support/core_ext/module/attribute_accessors.rb +10 -0
- data/lib/backports/active_support/logger.rb +99 -0
- data/lib/backports/active_support/logger_silence.rb +27 -0
- data/lib/backports/active_support/logger_thread_safe_level.rb +32 -0
- data/lib/backports/active_support/per_thread_registry.rb +60 -0
- data/lib/backports.rb +4 -0
- data/lib/neo4j/active_node/callbacks.rb +8 -0
- data/lib/neo4j/active_node/dependent/association_methods.rb +48 -0
- data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +50 -0
- data/lib/neo4j/active_node/dependent.rb +11 -0
- data/lib/neo4j/active_node/enum.rb +29 -0
- data/lib/neo4j/active_node/has_n/association/rel_factory.rb +61 -0
- data/lib/neo4j/active_node/has_n/association/rel_wrapper.rb +23 -0
- data/lib/neo4j/active_node/has_n/association.rb +280 -0
- data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +108 -0
- data/lib/neo4j/active_node/has_n.rb +532 -0
- data/lib/neo4j/active_node/id_property/accessor.rb +62 -0
- data/lib/neo4j/active_node/id_property.rb +187 -0
- data/lib/neo4j/active_node/initialize.rb +21 -0
- data/lib/neo4j/active_node/labels/index.rb +87 -0
- data/lib/neo4j/active_node/labels/reloading.rb +21 -0
- data/lib/neo4j/active_node/labels.rb +198 -0
- data/lib/neo4j/active_node/node_wrapper.rb +52 -0
- data/lib/neo4j/active_node/orm_adapter.rb +82 -0
- data/lib/neo4j/active_node/persistence.rb +175 -0
- data/lib/neo4j/active_node/property.rb +60 -0
- data/lib/neo4j/active_node/query/query_proxy.rb +361 -0
- data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +61 -0
- data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +90 -0
- data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +19 -0
- data/lib/neo4j/active_node/query/query_proxy_link.rb +117 -0
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +210 -0
- data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +83 -0
- data/lib/neo4j/active_node/query.rb +76 -0
- data/lib/neo4j/active_node/query_methods.rb +65 -0
- data/lib/neo4j/active_node/reflection.rb +86 -0
- data/lib/neo4j/active_node/rels.rb +11 -0
- data/lib/neo4j/active_node/scope.rb +146 -0
- data/lib/neo4j/active_node/unpersisted.rb +48 -0
- data/lib/neo4j/active_node/validations.rb +59 -0
- data/lib/neo4j/active_node.rb +105 -0
- data/lib/neo4j/active_rel/callbacks.rb +15 -0
- data/lib/neo4j/active_rel/initialize.rb +28 -0
- data/lib/neo4j/active_rel/persistence/query_factory.rb +95 -0
- data/lib/neo4j/active_rel/persistence.rb +114 -0
- data/lib/neo4j/active_rel/property.rb +95 -0
- data/lib/neo4j/active_rel/query.rb +95 -0
- data/lib/neo4j/active_rel/rel_wrapper.rb +22 -0
- data/lib/neo4j/active_rel/related_node.rb +83 -0
- data/lib/neo4j/active_rel/types.rb +82 -0
- data/lib/neo4j/active_rel/validations.rb +8 -0
- data/lib/neo4j/active_rel.rb +67 -0
- data/lib/neo4j/class_arguments.rb +39 -0
- data/lib/neo4j/config.rb +124 -0
- data/lib/neo4j/core/query.rb +22 -0
- data/lib/neo4j/errors.rb +28 -0
- data/lib/neo4j/migration.rb +127 -0
- data/lib/neo4j/paginated.rb +27 -0
- data/lib/neo4j/railtie.rb +169 -0
- data/lib/neo4j/schema/operation.rb +91 -0
- data/lib/neo4j/shared/attributes.rb +220 -0
- data/lib/neo4j/shared/callbacks.rb +64 -0
- data/lib/neo4j/shared/cypher.rb +37 -0
- data/lib/neo4j/shared/declared_properties.rb +204 -0
- data/lib/neo4j/shared/declared_property/index.rb +37 -0
- data/lib/neo4j/shared/declared_property.rb +118 -0
- data/lib/neo4j/shared/enum.rb +148 -0
- data/lib/neo4j/shared/filtered_hash.rb +79 -0
- data/lib/neo4j/shared/identity.rb +28 -0
- data/lib/neo4j/shared/initialize.rb +28 -0
- data/lib/neo4j/shared/marshal.rb +23 -0
- data/lib/neo4j/shared/mass_assignment.rb +58 -0
- data/lib/neo4j/shared/permitted_attributes.rb +28 -0
- data/lib/neo4j/shared/persistence.rb +231 -0
- data/lib/neo4j/shared/property.rb +220 -0
- data/lib/neo4j/shared/query_factory.rb +101 -0
- data/lib/neo4j/shared/rel_type_converters.rb +43 -0
- data/lib/neo4j/shared/serialized_properties.rb +30 -0
- data/lib/neo4j/shared/type_converters.rb +418 -0
- data/lib/neo4j/shared/typecasted_attributes.rb +98 -0
- data/lib/neo4j/shared/typecaster.rb +53 -0
- data/lib/neo4j/shared/validations.rb +48 -0
- data/lib/neo4j/shared.rb +51 -0
- data/lib/neo4j/tasks/migration.rake +24 -0
- data/lib/neo4j/timestamps/created.rb +9 -0
- data/lib/neo4j/timestamps/updated.rb +9 -0
- data/lib/neo4j/timestamps.rb +11 -0
- data/lib/neo4j/type_converters.rb +7 -0
- data/lib/neo4j/version.rb +3 -0
- data/lib/neo4j/wrapper.rb +4 -0
- data/lib/neo4j.rb +96 -0
- data/lib/rails/generators/neo4j/model/model_generator.rb +86 -0
- data/lib/rails/generators/neo4j/model/templates/model.erb +15 -0
- data/lib/rails/generators/neo4j_generator.rb +67 -0
- data/neo4j.gemspec +43 -0
- metadata +389 -0
@@ -0,0 +1,148 @@
|
|
1
|
+
module Neo4j::Shared
|
2
|
+
module Enum
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class ConflictingEnumMethodError < Neo4j::Error; end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
attr_reader :neo4j_enum_data
|
9
|
+
|
10
|
+
# Similar to ActiveRecord enum, maps an integer value on the database to
|
11
|
+
# a set of enum keys.
|
12
|
+
#
|
13
|
+
# @example Base example
|
14
|
+
# class Media
|
15
|
+
# include Neo4j::ActiveNode
|
16
|
+
# enum type: [:image, :video, :unknown]
|
17
|
+
# end
|
18
|
+
# Media.types # => { :images => 0, :video => 1, :unknown => 2 }
|
19
|
+
#
|
20
|
+
# media.video!
|
21
|
+
# media.image? # => false
|
22
|
+
# media.type # => :video
|
23
|
+
#
|
24
|
+
# Media.videos # => All medias with type = 1 (:video)
|
25
|
+
# Media.where(type: :video) # => All medias with type = 1 (:video)
|
26
|
+
#
|
27
|
+
# @example Prefix-ing an enum
|
28
|
+
# Media.enum type: [:image, :video, :unknown], _prefix: :enum
|
29
|
+
#
|
30
|
+
# media.enum_video!
|
31
|
+
# media.enum_video? # => true
|
32
|
+
#
|
33
|
+
# @example Suffix-ing an enum
|
34
|
+
# Media.enum type: [:image, :video, :unknown], _suffix: true
|
35
|
+
#
|
36
|
+
# media.video_type!
|
37
|
+
# media.video_type? # => true
|
38
|
+
#
|
39
|
+
# @example Disable index: :exact for enum elements
|
40
|
+
# Media.enum type: [:image, :video, :unknown], _index: false
|
41
|
+
#
|
42
|
+
# @example Define a custom mapping for keys-numbers
|
43
|
+
# Media.enum type: { image: 1, video: 2, unknown: 3 }
|
44
|
+
#
|
45
|
+
# @see http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html
|
46
|
+
def enum(parameters = {})
|
47
|
+
options, parameters = *split_options_and_parameters(parameters)
|
48
|
+
parameters.each do |property_name, enum_keys|
|
49
|
+
enum_keys = normalize_key_list enum_keys
|
50
|
+
@neo4j_enum_data ||= {}
|
51
|
+
@neo4j_enum_data[property_name] = enum_keys
|
52
|
+
define_property(property_name, enum_keys, options)
|
53
|
+
define_enum_methods(property_name, enum_keys, options)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
def normalize_key_list(enum_keys)
|
60
|
+
case enum_keys
|
61
|
+
when Hash
|
62
|
+
enum_keys
|
63
|
+
when Array
|
64
|
+
Hash[enum_keys.each_with_index.to_a]
|
65
|
+
else
|
66
|
+
fail ArgumentError, 'Invalid parameter for enum. Please provide an Array or an Hash.'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
VALID_OPTIONS_FOR_ENUMS = [:_index, :_prefix, :_suffix, :_default]
|
71
|
+
DEFAULT_OPTIONS_FOR_ENUMS = {
|
72
|
+
_index: true
|
73
|
+
}
|
74
|
+
|
75
|
+
def split_options_and_parameters(parameters)
|
76
|
+
options = DEFAULT_OPTIONS_FOR_ENUMS.clone
|
77
|
+
new_parameters = {}
|
78
|
+
parameters.each do |k, v|
|
79
|
+
if VALID_OPTIONS_FOR_ENUMS.include? k
|
80
|
+
options[k] = v
|
81
|
+
else
|
82
|
+
new_parameters[k] = v
|
83
|
+
end
|
84
|
+
end
|
85
|
+
[options, new_parameters]
|
86
|
+
end
|
87
|
+
|
88
|
+
def define_property(property_name, enum_keys, options)
|
89
|
+
property_options = build_property_options(enum_keys, options)
|
90
|
+
property property_name, property_options
|
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
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
def define_enum_methods(property_name, enum_keys, options)
|
101
|
+
define_enum_methods_?(property_name, enum_keys, options)
|
102
|
+
define_enum_methods_!(property_name, enum_keys, options)
|
103
|
+
define_class_methods(property_name, enum_keys)
|
104
|
+
end
|
105
|
+
|
106
|
+
def define_class_methods(property_name, enum_keys)
|
107
|
+
plural_property_name = property_name.to_s.pluralize.to_sym
|
108
|
+
define_singleton_method(plural_property_name) do
|
109
|
+
enum_keys
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def define_enum_methods_?(property_name, enum_keys, options)
|
114
|
+
enum_keys.keys.each do |enum_value|
|
115
|
+
method_name = build_method_name(enum_value, property_name, options)
|
116
|
+
check_enum_method_conflicts! property_name, :"#{method_name}?"
|
117
|
+
define_method("#{method_name}?") do
|
118
|
+
__send__(property_name).to_s.to_sym == enum_value
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def define_enum_methods_!(property_name, enum_keys, options)
|
124
|
+
enum_keys.keys.each do |enum_value|
|
125
|
+
method_name = build_method_name(enum_value, property_name, options)
|
126
|
+
check_enum_method_conflicts! property_name, :"#{method_name}!"
|
127
|
+
define_method("#{method_name}!") do
|
128
|
+
__send__("#{property_name}=", enum_value)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def build_method_name(base_name, property_name, options)
|
134
|
+
method_name = base_name
|
135
|
+
method_name = "#{method_name}_#{property_name}" if options[:_suffix]
|
136
|
+
method_name = "#{options[:_prefix]}_#{method_name}" if options[:_prefix]
|
137
|
+
method_name
|
138
|
+
end
|
139
|
+
|
140
|
+
def check_enum_method_conflicts!(property_name, method_name)
|
141
|
+
fail ConflictingEnumMethodError,
|
142
|
+
"The enum `#{property_name}` is trying to define a `#{method_name}` method, "\
|
143
|
+
'that is already defined. Try to use options `:prefix` or `:suffix` '\
|
144
|
+
'to avoid conflicts.' if instance_methods(false).include?(method_name)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Neo4j::Shared
|
2
|
+
class FilteredHash
|
3
|
+
class InvalidHashFilterType < Neo4j::Error; end
|
4
|
+
VALID_SYMBOL_INSTRUCTIONS = [:all, :none]
|
5
|
+
VALID_HASH_INSTRUCTIONS = [:on]
|
6
|
+
VALID_INSTRUCTIONS_TYPES = [Hash, Symbol]
|
7
|
+
|
8
|
+
attr_reader :base, :instructions, :instructions_type
|
9
|
+
|
10
|
+
def initialize(base, instructions)
|
11
|
+
@base = base
|
12
|
+
@instructions = instructions
|
13
|
+
@instructions_type = instructions.class
|
14
|
+
validate_instructions!(instructions)
|
15
|
+
end
|
16
|
+
|
17
|
+
def filtered_base
|
18
|
+
case instructions
|
19
|
+
when Symbol
|
20
|
+
filtered_base_by_symbol
|
21
|
+
when Hash
|
22
|
+
filtered_base_by_hash
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def filtered_base_by_symbol
|
29
|
+
case instructions
|
30
|
+
when :all
|
31
|
+
[base, {}]
|
32
|
+
when :none
|
33
|
+
[{}, base]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def filtered_base_by_hash
|
38
|
+
behavior_key = instructions.keys.first
|
39
|
+
filter_keys = keys_array(behavior_key)
|
40
|
+
[filter(filter_keys, :with), filter(filter_keys, :without)]
|
41
|
+
end
|
42
|
+
|
43
|
+
def key?(filter_keys, key)
|
44
|
+
filter_keys.include?(key)
|
45
|
+
end
|
46
|
+
|
47
|
+
def filter(filter_keys, key)
|
48
|
+
filtering = key == :with
|
49
|
+
base.select { |k, _v| key?(filter_keys, k) == filtering }
|
50
|
+
end
|
51
|
+
|
52
|
+
def keys_array(key)
|
53
|
+
instructions[key].is_a?(Array) ? instructions[key] : [instructions[key]]
|
54
|
+
end
|
55
|
+
|
56
|
+
def validate_instructions!(instructions)
|
57
|
+
fail InvalidHashFilterType, "Filtering instructions #{instructions} are invalid" unless VALID_INSTRUCTIONS_TYPES.include?(instructions.class)
|
58
|
+
clazz = instructions_type.name.downcase
|
59
|
+
return if send(:"valid_#{clazz}_instructions?", instructions)
|
60
|
+
fail InvalidHashFilterType, "Invalid instructions #{instructions}, valid options for #{clazz}: #{send(:"valid_#{clazz}_instructions")}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def valid_symbol_instructions?(instructions)
|
64
|
+
valid_symbol_instructions.include?(instructions)
|
65
|
+
end
|
66
|
+
|
67
|
+
def valid_hash_instructions?(instructions)
|
68
|
+
valid_hash_instructions.include?(instructions.keys.first)
|
69
|
+
end
|
70
|
+
|
71
|
+
def valid_symbol_instructions
|
72
|
+
VALID_SYMBOL_INSTRUCTIONS
|
73
|
+
end
|
74
|
+
|
75
|
+
def valid_hash_instructions
|
76
|
+
VALID_HASH_INSTRUCTIONS
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Neo4j::Shared
|
2
|
+
module Identity
|
3
|
+
def ==(other)
|
4
|
+
other.class == self.class && other.id == id
|
5
|
+
end
|
6
|
+
alias_method :eql?, :==
|
7
|
+
|
8
|
+
# Returns an Enumerable of all (primary) key attributes
|
9
|
+
# or nil if model.persisted? is false
|
10
|
+
def to_key
|
11
|
+
_persisted_obj ? [id] : nil
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Integer, nil] the neo4j id of the node if persisted or nil
|
15
|
+
def neo_id
|
16
|
+
_persisted_obj ? _persisted_obj.neo_id : nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def id
|
20
|
+
id = neo_id
|
21
|
+
id.is_a?(Integer) ? id : nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def hash
|
25
|
+
id.hash
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Neo4j::Shared
|
2
|
+
module Initialize
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
# Implements the Neo4j::Node#wrapper and Neo4j::Relationship#wrapper method
|
6
|
+
# so that we don't have to care if the node is wrapped or not.
|
7
|
+
# @return self
|
8
|
+
def wrapper
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def convert_and_assign_attributes(properties)
|
15
|
+
@attributes ||= Hash[self.class.attributes_nil_hash]
|
16
|
+
stringify_attributes!(@attributes, properties)
|
17
|
+
self.default_properties = properties if respond_to?(:default_properties=)
|
18
|
+
self.class.declared_properties.convert_properties_to(self, :ruby, @attributes)
|
19
|
+
end
|
20
|
+
|
21
|
+
def stringify_attributes!(attr, properties)
|
22
|
+
properties.each_pair do |k, v|
|
23
|
+
key = self.class.declared_properties.string_key(k)
|
24
|
+
attr[key.freeze] = v
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Shared
|
3
|
+
module Marshal
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def marshal_dump
|
7
|
+
marshal_instance_variables.map(&method(:instance_variable_get))
|
8
|
+
end
|
9
|
+
|
10
|
+
def marshal_load(array)
|
11
|
+
marshal_instance_variables.zip(array).each do |var, value|
|
12
|
+
instance_variable_set(var, value)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def marshal_instance_variables
|
19
|
+
self.class::MARSHAL_INSTANCE_VARIABLES
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Neo4j::Shared
|
2
|
+
# MassAssignment allows you to bulk set and update attributes
|
3
|
+
#
|
4
|
+
# Including MassAssignment into your model gives it a set of mass assignment
|
5
|
+
# methods, similar to those found in ActiveRecord.
|
6
|
+
#
|
7
|
+
# @example Usage
|
8
|
+
# class Person
|
9
|
+
# include Neo4j::Shared::MassAssignment
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# Originally part of ActiveAttr, https://github.com/cgriego/active_attr
|
13
|
+
module MassAssignment
|
14
|
+
extend ActiveSupport::Concern
|
15
|
+
# Mass update a model's attributes
|
16
|
+
#
|
17
|
+
# @example Assigning a hash
|
18
|
+
# person.assign_attributes(:first_name => "Chris", :last_name => "Griego")
|
19
|
+
# person.first_name #=> "Chris"
|
20
|
+
# person.last_name #=> "Griego"
|
21
|
+
#
|
22
|
+
# @param [Hash{#to_s => Object}, #each] attributes Attributes used to
|
23
|
+
# populate the model
|
24
|
+
# @param [Hash, #[]] options Options that affect mass assignment
|
25
|
+
def assign_attributes(new_attributes = nil)
|
26
|
+
return unless new_attributes.present?
|
27
|
+
new_attributes.each do |name, value|
|
28
|
+
writer = :"#{name}="
|
29
|
+
send(writer, value) if respond_to?(writer)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Mass update a model's attributes
|
34
|
+
#
|
35
|
+
# @example Assigning a hash
|
36
|
+
# person.attributes = { :first_name => "Chris", :last_name => "Griego" }
|
37
|
+
# person.first_name #=> "Chris"
|
38
|
+
# person.last_name #=> "Griego"
|
39
|
+
#
|
40
|
+
# @param (see #assign_attributes)
|
41
|
+
def attributes=(new_attributes)
|
42
|
+
assign_attributes(new_attributes)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Initialize a model with a set of attributes
|
46
|
+
#
|
47
|
+
# @example Initializing with a hash
|
48
|
+
# person = Person.new(:first_name => "Chris", :last_name => "Griego")
|
49
|
+
# person.first_name #=> "Chris"
|
50
|
+
# person.last_name #=> "Griego"
|
51
|
+
#
|
52
|
+
# @param (see #assign_attributes)
|
53
|
+
def initialize(attributes = nil)
|
54
|
+
assign_attributes(attributes)
|
55
|
+
super()
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Neo4j::Shared
|
2
|
+
module PermittedAttributes
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
include ActiveModel::ForbiddenAttributesProtection
|
5
|
+
|
6
|
+
def process_attributes(attributes)
|
7
|
+
attributes = sanitize_input_parameters(attributes)
|
8
|
+
super(attributes)
|
9
|
+
end
|
10
|
+
|
11
|
+
def attributes=(attributes)
|
12
|
+
attributes = sanitize_input_parameters(attributes)
|
13
|
+
super(attributes)
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
# Check if an argument is a string or an ActionController::Parameters
|
19
|
+
def hash_or_parameter?(args)
|
20
|
+
args.is_a?(Hash) || args.respond_to?(:to_unsafe_h)
|
21
|
+
end
|
22
|
+
|
23
|
+
def sanitize_input_parameters(attributes)
|
24
|
+
attributes = sanitize_for_mass_assignment(attributes)
|
25
|
+
attributes.respond_to?(:symbolize_keys) ? attributes.symbolize_keys : attributes
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
module Neo4j::Shared
|
2
|
+
module Persistence
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
# @return [Hash] Given a node's state, will call the appropriate `props_for_{action}` method.
|
6
|
+
def props_for_persistence
|
7
|
+
_persisted_obj ? props_for_update : props_for_create
|
8
|
+
end
|
9
|
+
|
10
|
+
def update_model
|
11
|
+
return if !changed_attributes || changed_attributes.empty?
|
12
|
+
_persisted_obj.update_props(props_for_update)
|
13
|
+
changed_attributes.clear
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns a hash containing:
|
17
|
+
# * All properties and values for insertion in the database
|
18
|
+
# * A `uuid` (or equivalent) key and value
|
19
|
+
# * Timestamps, if the class is set to include them.
|
20
|
+
# Note that the UUID is added to the hash but is not set on the node.
|
21
|
+
# The timestamps, by comparison, are set on the node prior to addition in this hash.
|
22
|
+
# @return [Hash]
|
23
|
+
def props_for_create
|
24
|
+
inject_timestamps!
|
25
|
+
props_with_defaults = inject_defaults!(props)
|
26
|
+
converted_props = props_for_db(props_with_defaults)
|
27
|
+
return converted_props unless self.class.respond_to?(:default_property_values)
|
28
|
+
inject_primary_key!(converted_props)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Hash] Properties and values, type-converted and timestamped for the database.
|
32
|
+
def props_for_update
|
33
|
+
update_magic_properties
|
34
|
+
changed_props = attributes.select { |k, _| changed_attributes.include?(k) }
|
35
|
+
changed_props.symbolize_keys!
|
36
|
+
inject_defaults!(changed_props)
|
37
|
+
props_for_db(changed_props)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Increments a numeric attribute by a centain amount
|
41
|
+
# @param [Symbol, String] name of the attribute to increment
|
42
|
+
# @param [Integer, Float] amount to increment
|
43
|
+
def increment(attribute, by = 1)
|
44
|
+
self[attribute] ||= 0
|
45
|
+
self[attribute] += by
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
# Convenience method to increment numeric attribute and #save at the same time
|
50
|
+
# @param [Symbol, String] name of the attribute to increment
|
51
|
+
# @param [Integer, Float] amount to increment
|
52
|
+
def increment!(attribute, by = 1)
|
53
|
+
increment(attribute, by).update_attribute(attribute, self[attribute])
|
54
|
+
end
|
55
|
+
|
56
|
+
# Increments concurrently a numeric attribute by a centain amount
|
57
|
+
# @param [Symbol, String] name of the attribute to increment
|
58
|
+
# @param [Integer, Float] amount to increment
|
59
|
+
def concurrent_increment!(_attribute, _by = 1)
|
60
|
+
fail 'not_implemented'
|
61
|
+
end
|
62
|
+
|
63
|
+
# Convenience method to set attribute and #save at the same time
|
64
|
+
# @param [Symbol, String] attribute of the attribute to update
|
65
|
+
# @param [Object] value to set
|
66
|
+
def update_attribute(attribute, value)
|
67
|
+
send("#{attribute}=", value)
|
68
|
+
self.save
|
69
|
+
end
|
70
|
+
|
71
|
+
# Convenience method to set attribute and #save! at the same time
|
72
|
+
# @param [Symbol, String] attribute of the attribute to update
|
73
|
+
# @param [Object] value to set
|
74
|
+
def update_attribute!(attribute, value)
|
75
|
+
send("#{attribute}=", value)
|
76
|
+
self.save!
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_or_update
|
80
|
+
# since the same model can be created or updated twice from a relationship we have to have this guard
|
81
|
+
@_create_or_updating = true
|
82
|
+
apply_default_values
|
83
|
+
result = _persisted_obj ? update_model : create_model
|
84
|
+
if result == false
|
85
|
+
Neo4j::Transaction.current.failure if Neo4j::Transaction.current
|
86
|
+
false
|
87
|
+
else
|
88
|
+
true
|
89
|
+
end
|
90
|
+
rescue => e
|
91
|
+
Neo4j::Transaction.current.failure if Neo4j::Transaction.current
|
92
|
+
raise e
|
93
|
+
ensure
|
94
|
+
@_create_or_updating = nil
|
95
|
+
end
|
96
|
+
|
97
|
+
def apply_default_values
|
98
|
+
return if self.class.declared_property_defaults.empty?
|
99
|
+
self.class.declared_property_defaults.each_pair do |key, value|
|
100
|
+
self.send("#{key}=", value) if self.send(key).nil?
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def touch
|
105
|
+
fail 'Cannot touch on a new record object' unless persisted?
|
106
|
+
update_attribute!(:updated_at, Time.now) if respond_to?(:updated_at=)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns +true+ if the record is persisted, i.e. it's not a new record and it was not destroyed
|
110
|
+
def persisted?
|
111
|
+
!new_record? && !destroyed?
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns +true+ if the record hasn't been saved to Neo4j yet.
|
115
|
+
def new_record?
|
116
|
+
!_persisted_obj
|
117
|
+
end
|
118
|
+
|
119
|
+
alias_method :new?, :new_record?
|
120
|
+
|
121
|
+
def destroy
|
122
|
+
freeze
|
123
|
+
_persisted_obj && _persisted_obj.del
|
124
|
+
@_deleted = true
|
125
|
+
end
|
126
|
+
|
127
|
+
def exist?
|
128
|
+
_persisted_obj && _persisted_obj.exist?
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns +true+ if the object was destroyed.
|
132
|
+
def destroyed?
|
133
|
+
@_deleted
|
134
|
+
end
|
135
|
+
|
136
|
+
# @return [Hash] all defined and none nil properties
|
137
|
+
def props
|
138
|
+
attributes.reject { |_, v| v.nil? }.symbolize_keys
|
139
|
+
end
|
140
|
+
|
141
|
+
# @return true if the attributes hash has been frozen
|
142
|
+
def frozen?
|
143
|
+
@attributes.frozen?
|
144
|
+
end
|
145
|
+
|
146
|
+
def freeze
|
147
|
+
@attributes.freeze
|
148
|
+
self
|
149
|
+
end
|
150
|
+
|
151
|
+
def reload
|
152
|
+
return self if new_record?
|
153
|
+
association_proxy_cache.clear if respond_to?(:association_proxy_cache)
|
154
|
+
changed_attributes && changed_attributes.clear
|
155
|
+
unless reload_from_database
|
156
|
+
@_deleted = true
|
157
|
+
freeze
|
158
|
+
end
|
159
|
+
self
|
160
|
+
end
|
161
|
+
|
162
|
+
def reload_from_database
|
163
|
+
reloaded = self.class.load_entity(neo_id)
|
164
|
+
reloaded ? init_on_reload(reloaded._persisted_obj) : nil
|
165
|
+
end
|
166
|
+
|
167
|
+
# Updates this resource with all the attributes from the passed-in Hash and requests that the record be saved.
|
168
|
+
# If saving fails because the resource is invalid then false will be returned.
|
169
|
+
def update(attributes)
|
170
|
+
self.attributes = process_attributes(attributes)
|
171
|
+
save
|
172
|
+
end
|
173
|
+
alias_method :update_attributes, :update
|
174
|
+
|
175
|
+
# Same as {#update_attributes}, but raises an exception if saving fails.
|
176
|
+
def update!(attributes)
|
177
|
+
self.attributes = process_attributes(attributes)
|
178
|
+
save!
|
179
|
+
end
|
180
|
+
alias_method :update_attributes!, :update!
|
181
|
+
|
182
|
+
def cache_key
|
183
|
+
if self.new_record?
|
184
|
+
"#{model_cache_key}/new"
|
185
|
+
elsif self.respond_to?(:updated_at) && !self.updated_at.blank?
|
186
|
+
"#{model_cache_key}/#{neo_id}-#{self.updated_at.utc.to_s(:number)}"
|
187
|
+
else
|
188
|
+
"#{model_cache_key}/#{neo_id}"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
protected
|
193
|
+
|
194
|
+
def increment_by_query!(match_query, attribute, by, element_name = :n)
|
195
|
+
new_attribute = match_query.with(element_name)
|
196
|
+
.set("#{element_name}.`#{attribute}` = COALESCE(#{element_name}.`#{attribute}`, 0) + {by}")
|
197
|
+
.params(by: by).limit(1)
|
198
|
+
.pluck("#{element_name}.`#{attribute}`").first
|
199
|
+
return false unless new_attribute
|
200
|
+
self[attribute] = new_attribute
|
201
|
+
changed_attributes.delete(attribute)
|
202
|
+
true
|
203
|
+
end
|
204
|
+
|
205
|
+
private
|
206
|
+
|
207
|
+
def props_for_db(props_hash)
|
208
|
+
self.class.declared_properties.convert_properties_to(self, :db, props_hash)
|
209
|
+
end
|
210
|
+
|
211
|
+
def model_cache_key
|
212
|
+
self.class.model_name.cache_key
|
213
|
+
end
|
214
|
+
|
215
|
+
def update_magic_properties
|
216
|
+
self.updated_at = DateTime.now if respond_to?(:updated_at=) && (updated_at.nil? || (changed? && !updated_at_changed?))
|
217
|
+
end
|
218
|
+
|
219
|
+
def inject_timestamps!
|
220
|
+
now = DateTime.now
|
221
|
+
self.created_at ||= now if respond_to?(:created_at=)
|
222
|
+
self.updated_at ||= now if respond_to?(:updated_at=)
|
223
|
+
end
|
224
|
+
|
225
|
+
def set_timestamps
|
226
|
+
warning = 'This method has been replaced with `inject_timestamps!` and will be removed in a future version'.freeze
|
227
|
+
ActiveSupport::Deprecation.warn warning, caller
|
228
|
+
inject_timestamps!
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|