neo4j 3.0.0.alpha.9 → 3.0.0.alpha.10
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 +3 -0
- data/Gemfile +2 -1
- data/lib/neo4j.rb +21 -4
- data/lib/neo4j/active_node.rb +5 -35
- data/lib/neo4j/active_node/callbacks.rb +1 -34
- data/lib/neo4j/active_node/has_n/association.rb +35 -16
- data/lib/neo4j/active_node/id_property.rb +1 -1
- data/lib/neo4j/active_node/initialize.rb +2 -2
- data/lib/neo4j/active_node/labels.rb +3 -3
- data/lib/neo4j/active_node/node_wrapper.rb +50 -0
- data/lib/neo4j/active_node/persistence.rb +2 -205
- data/lib/neo4j/active_node/property.rb +1 -194
- data/lib/neo4j/active_node/rels.rb +2 -2
- data/lib/neo4j/active_node/validations.rb +9 -41
- data/lib/neo4j/active_rel.rb +34 -0
- data/lib/neo4j/active_rel/callbacks.rb +13 -0
- data/lib/neo4j/active_rel/initialize.rb +30 -0
- data/lib/neo4j/active_rel/persistence.rb +80 -0
- data/lib/neo4j/active_rel/property.rb +64 -0
- data/lib/neo4j/active_rel/query.rb +66 -0
- data/lib/neo4j/active_rel/rel_wrapper.rb +18 -0
- data/lib/neo4j/active_rel/related_node.rb +42 -0
- data/lib/neo4j/active_rel/validations.rb +9 -0
- data/lib/neo4j/shared.rb +34 -0
- data/lib/neo4j/shared/callbacks.rb +40 -0
- data/lib/neo4j/{active_node → shared}/identity.rb +6 -2
- data/lib/neo4j/shared/persistence.rb +214 -0
- data/lib/neo4j/shared/property.rb +220 -0
- data/lib/neo4j/shared/validations.rb +48 -0
- data/lib/neo4j/version.rb +1 -1
- data/lib/neo4j/wrapper.rb +2 -50
- metadata +18 -5
- data/lib/neo4j/active_node/has_n/nodes.rb +0 -90
- data/lib/neo4j/active_node/quick_query.rb +0 -254
@@ -0,0 +1,220 @@
|
|
1
|
+
module Neo4j::Shared
|
2
|
+
module Property
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
include ActiveAttr::Attributes
|
6
|
+
include ActiveAttr::MassAssignment
|
7
|
+
include ActiveAttr::TypecastedAttributes
|
8
|
+
include ActiveAttr::AttributeDefaults
|
9
|
+
include ActiveAttr::QueryAttributes
|
10
|
+
include ActiveModel::Dirty
|
11
|
+
|
12
|
+
class UndefinedPropertyError < RuntimeError; end
|
13
|
+
class MultiparameterAssignmentError < StandardError; end
|
14
|
+
class IllegalPropertyError < StandardError; end
|
15
|
+
|
16
|
+
ILLEGAL_PROPS = %w[from_node to_node start_node end_node]
|
17
|
+
|
18
|
+
def initialize(attributes={}, options={})
|
19
|
+
attributes = process_attributes(attributes)
|
20
|
+
relationship_props = self.class.extract_association_attributes!(attributes)
|
21
|
+
writer_method_props = extract_writer_methods!(attributes)
|
22
|
+
validate_attributes!(attributes)
|
23
|
+
send_props(writer_method_props) unless writer_method_props.nil?
|
24
|
+
send_props(relationship_props) unless relationship_props.nil?
|
25
|
+
|
26
|
+
super(attributes, options)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returning nil when we get ActiveAttr::UnknownAttributeError from ActiveAttr
|
30
|
+
def read_attribute(name)
|
31
|
+
super(name)
|
32
|
+
rescue ActiveAttr::UnknownAttributeError
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
alias_method :[], :read_attribute
|
36
|
+
|
37
|
+
def default_properties=(properties)
|
38
|
+
keys = self.class.default_properties.keys
|
39
|
+
@default_properties = properties.reject{|key| !keys.include?(key)}
|
40
|
+
end
|
41
|
+
|
42
|
+
def default_property(key)
|
43
|
+
keys = self.class.default_properties.keys
|
44
|
+
keys.include?(key.to_sym) ? default_properties[key.to_sym] : nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def default_properties
|
48
|
+
@default_properties ||= {}
|
49
|
+
# keys = self.class.default_properties.keys
|
50
|
+
# _persisted_obj.props.reject{|key| !keys.include?(key)}
|
51
|
+
end
|
52
|
+
|
53
|
+
def send_props(hash)
|
54
|
+
hash.each do |key, value|
|
55
|
+
self.send("#{key}=", value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# Changes attributes hash to remove relationship keys
|
62
|
+
# Raises an error if there are any keys left which haven't been defined as properties on the model
|
63
|
+
def validate_attributes!(attributes)
|
64
|
+
invalid_properties = attributes.keys.map(&:to_s) - self.attributes.keys
|
65
|
+
raise UndefinedPropertyError, "Undefined properties: #{invalid_properties.join(',')}" if invalid_properties.size > 0
|
66
|
+
end
|
67
|
+
|
68
|
+
def extract_writer_methods!(attributes)
|
69
|
+
attributes.keys.inject({}) do |writer_method_props, key|
|
70
|
+
writer_method_props[key] = attributes.delete(key) if self.respond_to?("#{key}=")
|
71
|
+
|
72
|
+
writer_method_props
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Gives support for Rails date_select, datetime_select, time_select helpers.
|
77
|
+
def process_attributes(attributes = nil)
|
78
|
+
multi_parameter_attributes = {}
|
79
|
+
new_attributes = {}
|
80
|
+
attributes.each_pair do |key, value|
|
81
|
+
if key =~ /\A([^\(]+)\((\d+)([if])\)$/
|
82
|
+
found_key, index = $1, $2.to_i
|
83
|
+
(multi_parameter_attributes[found_key] ||= {})[index] = value.empty? ? nil : value.send("to_#{$3}")
|
84
|
+
else
|
85
|
+
new_attributes[key] = value
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
multi_parameter_attributes.empty? ? new_attributes : process_multiparameter_attributes(multi_parameter_attributes, new_attributes)
|
90
|
+
end
|
91
|
+
|
92
|
+
def process_multiparameter_attributes(multi_parameter_attributes, new_attributes)
|
93
|
+
multi_parameter_attributes.each_pair do |key, values|
|
94
|
+
begin
|
95
|
+
values = (values.keys.min..values.keys.max).map { |i| values[i] }
|
96
|
+
field = self.class.attributes[key.to_sym]
|
97
|
+
new_attributes[key] = instantiate_object(field, values)
|
98
|
+
rescue => e
|
99
|
+
raise MultiparameterAssignmentError, "error on assignment #{values.inspect} to #{key}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
new_attributes
|
103
|
+
end
|
104
|
+
|
105
|
+
def instantiate_object(field, values_with_empty_parameters)
|
106
|
+
return nil if values_with_empty_parameters.all? { |v| v.nil? }
|
107
|
+
values = values_with_empty_parameters.collect { |v| v.nil? ? 1 : v }
|
108
|
+
klass = field[:type]
|
109
|
+
if klass
|
110
|
+
klass.new(*values)
|
111
|
+
else
|
112
|
+
values
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
module ClassMethods
|
117
|
+
|
118
|
+
# Defines a property on the class
|
119
|
+
#
|
120
|
+
# See active_attr gem for allowed options, e.g which type
|
121
|
+
# Notice, in Neo4j you don't have to declare properties before using them, see the neo4j-core api.
|
122
|
+
#
|
123
|
+
# @example Without type
|
124
|
+
# class Person
|
125
|
+
# # declare a property which can have any value
|
126
|
+
# property :name
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
# @example With type and a default value
|
130
|
+
# class Person
|
131
|
+
# # declare a property which can have any value
|
132
|
+
# property :score, type: Integer, default: 0
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# @example With an index
|
136
|
+
# class Person
|
137
|
+
# # declare a property which can have any value
|
138
|
+
# property :name, index: :exact
|
139
|
+
# end
|
140
|
+
#
|
141
|
+
# @example With a constraint
|
142
|
+
# class Person
|
143
|
+
# # declare a property which can have any value
|
144
|
+
# property :name, constraint: :unique
|
145
|
+
# end
|
146
|
+
def property(name, options={})
|
147
|
+
check_illegal_prop(name)
|
148
|
+
magic_properties(name, options)
|
149
|
+
attribute(name, options)
|
150
|
+
constraint_or_index(name, options)
|
151
|
+
end
|
152
|
+
|
153
|
+
def default_property(name, &block)
|
154
|
+
default_properties[name] = block
|
155
|
+
end
|
156
|
+
|
157
|
+
# @return [Hash<Symbol,Proc>]
|
158
|
+
def default_properties
|
159
|
+
@default_property ||= {}
|
160
|
+
end
|
161
|
+
|
162
|
+
def default_property_values(instance)
|
163
|
+
default_properties.inject({}) do |result,pair|
|
164
|
+
result.tap{|obj| obj[pair[0]] = pair[1].call(instance)}
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def attribute!(name, options={})
|
169
|
+
super(name, options)
|
170
|
+
define_method("#{name}=") do |value|
|
171
|
+
typecast_value = typecast_attribute(typecaster_for(self.class._attribute_type(name)), value)
|
172
|
+
send("#{name}_will_change!") unless typecast_value == read_attribute(name)
|
173
|
+
super(value)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def cached_class?
|
178
|
+
!!Neo4j::Config[:cache_class_names]
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
def constraint_or_index(name, options)
|
184
|
+
# either constraint or index, do not set both
|
185
|
+
if options[:constraint]
|
186
|
+
raise "unknown constraint type #{options[:constraint]}, only :unique supported" if options[:constraint] != :unique
|
187
|
+
constraint(name, type: :unique)
|
188
|
+
elsif options[:index]
|
189
|
+
raise "unknown index type #{options[:index]}, only :exact supported" if options[:index] != :exact
|
190
|
+
index(name, options) if options[:index] == :exact
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def check_illegal_prop(name)
|
195
|
+
if ILLEGAL_PROPS.include?(name.to_s)
|
196
|
+
raise IllegalPropertyError, "#{name} is an illegal property"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Tweaks properties
|
201
|
+
def magic_properties(name, options)
|
202
|
+
set_stamp_type(name, options)
|
203
|
+
set_time_as_datetime(options)
|
204
|
+
end
|
205
|
+
|
206
|
+
def set_stamp_type(name, options)
|
207
|
+
options[:type] = DateTime if (name.to_sym == :created_at || name.to_sym == :updated_at)
|
208
|
+
end
|
209
|
+
|
210
|
+
# ActiveAttr does not handle "Time", Rails and Neo4j.rb 2.3 did
|
211
|
+
# Convert it to DateTime in the interest of consistency
|
212
|
+
def set_time_as_datetime(options)
|
213
|
+
options[:type] = DateTime if options[:type] == Time
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Shared
|
3
|
+
module Validations
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
include ActiveModel::Validations
|
6
|
+
# Implements the ActiveModel::Validation hook method.
|
7
|
+
# @see http://rubydoc.info/docs/rails/ActiveModel/Validations:read_attribute_for_validation
|
8
|
+
def read_attribute_for_validation(key)
|
9
|
+
respond_to?(key) ? send(key) : self[key]
|
10
|
+
end
|
11
|
+
|
12
|
+
# The validation process on save can be skipped by passing false. The regular Model#save method is
|
13
|
+
# replaced with this when the validations module is mixed in, which it is by default.
|
14
|
+
# @param [Hash] options the options to create a message with.
|
15
|
+
# @option options [true, false] :validate if false no validation will take place
|
16
|
+
# @return [Boolean] true if it saved it successfully
|
17
|
+
def save(options={})
|
18
|
+
result = perform_validations(options) ? super : false
|
19
|
+
if !result
|
20
|
+
Neo4j::Transaction.current.failure if Neo4j::Transaction.current
|
21
|
+
end
|
22
|
+
result
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Boolean] true if valid
|
26
|
+
def valid?(context = nil)
|
27
|
+
context ||= (new_record? ? :create : :update)
|
28
|
+
super(context)
|
29
|
+
errors.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def perform_validations(options={})
|
35
|
+
perform_validation = case options
|
36
|
+
when Hash
|
37
|
+
options[:validate] != false
|
38
|
+
end
|
39
|
+
|
40
|
+
if perform_validation
|
41
|
+
valid?(options.is_a?(Hash) ? options[:context] : nil)
|
42
|
+
else
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/neo4j/version.rb
CHANGED
data/lib/neo4j/wrapper.rb
CHANGED
@@ -1,52 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Wrapper
|
4
|
-
|
5
|
-
# this is a plugin in the neo4j-core so that the Ruby wrapper will be wrapped around the Neo4j::Node objects
|
6
|
-
def wrapper
|
7
|
-
most_concrete_class = sorted_wrapper_classes
|
8
|
-
wrapped_node = most_concrete_class.new
|
9
|
-
wrapped_node.init_on_load(self, self.props)
|
10
|
-
wrapped_node
|
11
|
-
end
|
12
|
-
|
13
|
-
def checked_labels_set
|
14
|
-
@@_checked_labels_set ||= Set.new
|
15
|
-
end
|
16
|
-
|
17
|
-
def check_label(label_name)
|
18
|
-
unless checked_labels_set.include?(label_name)
|
19
|
-
load_class_from_label(label_name)
|
20
|
-
# do this only once
|
21
|
-
checked_labels_set.add(label_name)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def sorted_wrapper_classes
|
26
|
-
if self.props.is_a?(Hash) && self.props.has_key?(Neo4j::Config.class_name_property)
|
27
|
-
self.props[Neo4j::Config.class_name_property].constantize
|
28
|
-
else
|
29
|
-
wrappers = _class_wrappers
|
30
|
-
return self if wrappers.nil?
|
31
|
-
wrapper_classes = wrappers.map{|w| Neo4j::ActiveNode::Labels._wrapped_labels[w]}
|
32
|
-
wrapper_classes.sort.first
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def load_class_from_label(label_name)
|
37
|
-
begin
|
38
|
-
label_name.to_s.split("::").inject(Kernel) {|container, name| container.const_get(name.to_s) }
|
39
|
-
rescue NameError
|
40
|
-
nil
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def _class_wrappers
|
45
|
-
labels.find_all do |label_name|
|
46
|
-
check_label(label_name)
|
47
|
-
Neo4j::ActiveNode::Labels._wrapped_labels[label_name].class == Class
|
48
|
-
end
|
49
|
-
end
|
1
|
+
module Neo4j
|
2
|
+
module ClassWrapper
|
50
3
|
end
|
51
|
-
|
52
4
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: neo4j
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0.alpha.
|
4
|
+
version: 3.0.0.alpha.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Ronge
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: orm_adapter
|
@@ -119,23 +119,36 @@ files:
|
|
119
119
|
- lib/neo4j/active_node/callbacks.rb
|
120
120
|
- lib/neo4j/active_node/has_n.rb
|
121
121
|
- lib/neo4j/active_node/has_n/association.rb
|
122
|
-
- lib/neo4j/active_node/has_n/nodes.rb
|
123
122
|
- lib/neo4j/active_node/id_property.rb
|
124
|
-
- lib/neo4j/active_node/identity.rb
|
125
123
|
- lib/neo4j/active_node/initialize.rb
|
126
124
|
- lib/neo4j/active_node/labels.rb
|
125
|
+
- lib/neo4j/active_node/node_wrapper.rb
|
127
126
|
- lib/neo4j/active_node/orm_adapter.rb
|
128
127
|
- lib/neo4j/active_node/persistence.rb
|
129
128
|
- lib/neo4j/active_node/property.rb
|
130
129
|
- lib/neo4j/active_node/query.rb
|
131
130
|
- lib/neo4j/active_node/query/query_proxy.rb
|
132
|
-
- lib/neo4j/active_node/quick_query.rb
|
133
131
|
- lib/neo4j/active_node/rels.rb
|
134
132
|
- lib/neo4j/active_node/serialized_properties.rb
|
135
133
|
- lib/neo4j/active_node/validations.rb
|
134
|
+
- lib/neo4j/active_rel.rb
|
135
|
+
- lib/neo4j/active_rel/callbacks.rb
|
136
|
+
- lib/neo4j/active_rel/initialize.rb
|
137
|
+
- lib/neo4j/active_rel/persistence.rb
|
138
|
+
- lib/neo4j/active_rel/property.rb
|
139
|
+
- lib/neo4j/active_rel/query.rb
|
140
|
+
- lib/neo4j/active_rel/rel_wrapper.rb
|
141
|
+
- lib/neo4j/active_rel/related_node.rb
|
142
|
+
- lib/neo4j/active_rel/validations.rb
|
136
143
|
- lib/neo4j/config.rb
|
137
144
|
- lib/neo4j/paginated.rb
|
138
145
|
- lib/neo4j/railtie.rb
|
146
|
+
- lib/neo4j/shared.rb
|
147
|
+
- lib/neo4j/shared/callbacks.rb
|
148
|
+
- lib/neo4j/shared/identity.rb
|
149
|
+
- lib/neo4j/shared/persistence.rb
|
150
|
+
- lib/neo4j/shared/property.rb
|
151
|
+
- lib/neo4j/shared/validations.rb
|
139
152
|
- lib/neo4j/type_converters.rb
|
140
153
|
- lib/neo4j/version.rb
|
141
154
|
- lib/neo4j/wrapper.rb
|
@@ -1,90 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module ActiveNode
|
3
|
-
module HasN
|
4
|
-
|
5
|
-
# The object created by a has_n or has_one Neo4j::NodeMixin class method which enables creating and traversal of nodes.
|
6
|
-
#
|
7
|
-
# @see Neo4j::ActiveNode::HasN::ClassMethods
|
8
|
-
class Nodes
|
9
|
-
include Enumerable
|
10
|
-
|
11
|
-
def initialize(node, decl_rel) # :nodoc:
|
12
|
-
@node = node
|
13
|
-
@decl_rel = decl_rel
|
14
|
-
end
|
15
|
-
|
16
|
-
def to_s
|
17
|
-
"[#{@decl_rel.dir}, from: #{@node.neo_id} type: #{@decl_rel.rel_type} has_one #{@decl_rel.has_one?}]"
|
18
|
-
end
|
19
|
-
|
20
|
-
def inspect
|
21
|
-
to_s
|
22
|
-
end
|
23
|
-
|
24
|
-
# Traverse the relationship till the index position
|
25
|
-
# @return [Neo4j::ActiveMixin,Neo4j::Node,nil] the node at the given position
|
26
|
-
def [](index)
|
27
|
-
i = 0
|
28
|
-
each { |x| return x if i == index; i += 1 }
|
29
|
-
nil # out of index
|
30
|
-
end
|
31
|
-
|
32
|
-
# Pretend we are an array - this is necessarily for Rails actionpack/actionview/formhelper to work with this
|
33
|
-
def is_a?(type)
|
34
|
-
# ActionView requires this for nested attributes to work
|
35
|
-
return true if Array == type
|
36
|
-
super
|
37
|
-
end
|
38
|
-
|
39
|
-
def ==(other)
|
40
|
-
self.to_a == other.to_a
|
41
|
-
end
|
42
|
-
alias_method :eql?, :==
|
43
|
-
|
44
|
-
# Required by the Enumerable mixin.
|
45
|
-
def each
|
46
|
-
@decl_rel.each_node(@node) { |n| yield n } # Should use yield here as passing &block through doesn't always work (why?)
|
47
|
-
end
|
48
|
-
|
49
|
-
# returns none wrapped nodes, you may get better performance using this method
|
50
|
-
def _each
|
51
|
-
@decl_rel._each_node(@node) { |n| yield n }
|
52
|
-
end
|
53
|
-
|
54
|
-
# Returns an real ruby array.
|
55
|
-
def to_ary
|
56
|
-
self.to_a
|
57
|
-
end
|
58
|
-
|
59
|
-
# Returns true if there are no node in this type of relationship
|
60
|
-
def empty?
|
61
|
-
first == nil
|
62
|
-
end
|
63
|
-
|
64
|
-
|
65
|
-
# Creates a relationship instance between this and the other node.
|
66
|
-
# Returns the relationship object
|
67
|
-
def create(other, relationship_props = {})
|
68
|
-
@decl_rel.create_relationship_to(@node, other, relationship_props)
|
69
|
-
end
|
70
|
-
|
71
|
-
|
72
|
-
# Creates a relationship between this and the other node.
|
73
|
-
#
|
74
|
-
# @example Person includes the Neo4j::NodeMixin and declares a has_n :friends
|
75
|
-
#
|
76
|
-
# p = Person.new # Node has declared having a friend type of relationship
|
77
|
-
# n1 = Node.new
|
78
|
-
# n2 = Node.new
|
79
|
-
#
|
80
|
-
# p.friends << n2 << n3
|
81
|
-
#
|
82
|
-
# @return self
|
83
|
-
def <<(other)
|
84
|
-
!@decl_rel.create_relationship_to(@node, other) ? false : self
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|