neo4j 3.0.0.rc.3 → 3.0.0.rc.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +10 -0
- data/Gemfile +5 -4
- data/README.md +1 -1
- data/config/neo4j/add_classnames.yml +1 -0
- data/lib/neo4j.rb +2 -0
- data/lib/neo4j/active_node.rb +4 -1
- data/lib/neo4j/active_node/has_n.rb +75 -9
- data/lib/neo4j/active_node/has_n/association.rb +5 -4
- data/lib/neo4j/active_node/id_property.rb +50 -5
- data/lib/neo4j/active_node/initialize.rb +1 -0
- data/lib/neo4j/active_node/labels.rb +15 -3
- data/lib/neo4j/active_node/orm_adapter.rb +1 -1
- data/lib/neo4j/active_node/persistence.rb +39 -0
- data/lib/neo4j/active_node/query/query_proxy.rb +29 -6
- data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +20 -0
- data/lib/neo4j/active_node/query/query_proxy_methods.rb +24 -12
- data/lib/neo4j/active_node/query_methods.rb +21 -10
- data/lib/neo4j/active_node/reflection.rb +85 -0
- data/lib/neo4j/active_rel/callbacks.rb +3 -1
- data/lib/neo4j/active_rel/initialize.rb +2 -2
- data/lib/neo4j/active_rel/persistence.rb +23 -14
- data/lib/neo4j/active_rel/property.rb +9 -10
- data/lib/neo4j/active_rel/query.rb +51 -29
- data/lib/neo4j/active_rel/related_node.rb +2 -6
- data/lib/neo4j/migration.rb +185 -0
- data/lib/neo4j/paginated.rb +2 -1
- data/lib/neo4j/railtie.rb +13 -8
- data/lib/neo4j/shared/persistence.rb +10 -56
- data/lib/neo4j/shared/property.rb +15 -6
- data/lib/neo4j/tasks/migration.rake +23 -0
- data/lib/neo4j/version.rb +1 -1
- metadata +7 -2
@@ -1,8 +1,46 @@
|
|
1
1
|
module Neo4j::ActiveNode
|
2
2
|
module Persistence
|
3
|
+
|
4
|
+
class RecordInvalidError < RuntimeError
|
5
|
+
attr_reader :record
|
6
|
+
|
7
|
+
def initialize(record)
|
8
|
+
@record = record
|
9
|
+
super(@record.errors.full_messages.join(", "))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
3
13
|
extend ActiveSupport::Concern
|
4
14
|
include Neo4j::Shared::Persistence
|
5
15
|
|
16
|
+
# Saves the model.
|
17
|
+
#
|
18
|
+
# If the model is new a record gets created in the database, otherwise the existing record gets updated.
|
19
|
+
# If perform_validation is true validations run.
|
20
|
+
# If any of them fail the action is cancelled and save returns false. If the flag is false validations are bypassed altogether. See ActiveRecord::Validations for more information.
|
21
|
+
# There’s a series of callbacks associated with save. If any of the before_* callbacks return false the action is cancelled and save returns false.
|
22
|
+
def save(*)
|
23
|
+
update_magic_properties
|
24
|
+
clear_association_cache
|
25
|
+
create_or_update
|
26
|
+
end
|
27
|
+
|
28
|
+
# Persist the object to the database. Validations and Callbacks are included
|
29
|
+
# by default but validation can be disabled by passing :validate => false
|
30
|
+
# to #save! Creates a new transaction.
|
31
|
+
#
|
32
|
+
# @raise a RecordInvalidError if there is a problem during save.
|
33
|
+
# @param (see Neo4j::Rails::Validations#save)
|
34
|
+
# @return nil
|
35
|
+
# @see #save
|
36
|
+
# @see Neo4j::Rails::Validations Neo4j::Rails::Validations - for the :validate parameter
|
37
|
+
# @see Neo4j::Rails::Callbacks Neo4j::Rails::Callbacks - for callbacks
|
38
|
+
def save!(*args)
|
39
|
+
unless save(*args)
|
40
|
+
raise RecordInvalidError.new(self)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
6
44
|
# Creates a model with values matching those of the instance attributes and returns its id.
|
7
45
|
# @private
|
8
46
|
# @return true
|
@@ -60,6 +98,7 @@ module Neo4j::ActiveNode
|
|
60
98
|
def load_entity(id)
|
61
99
|
Neo4j::Node.load(id)
|
62
100
|
end
|
101
|
+
|
63
102
|
end
|
64
103
|
|
65
104
|
private
|
@@ -5,6 +5,11 @@ module Neo4j
|
|
5
5
|
|
6
6
|
include Enumerable
|
7
7
|
include Neo4j::ActiveNode::Query::QueryProxyMethods
|
8
|
+
include Neo4j::ActiveNode::Query::QueryProxyFindInBatches
|
9
|
+
|
10
|
+
# The most recent node to start a QueryProxy chain.
|
11
|
+
# Will be nil when using QueryProxy chains on class methods.
|
12
|
+
attr_reader :caller
|
8
13
|
|
9
14
|
def initialize(model, association = nil, options = {})
|
10
15
|
@model = model
|
@@ -14,6 +19,7 @@ module Neo4j
|
|
14
19
|
@node_var = options[:node]
|
15
20
|
@rel_var = options[:rel] || _rel_chain_var
|
16
21
|
@session = options[:session]
|
22
|
+
@caller = options[:caller]
|
17
23
|
@chain = []
|
18
24
|
@params = options[:query_proxy] ? options[:query_proxy].instance_variable_get('@params') : {}
|
19
25
|
end
|
@@ -22,16 +28,24 @@ module Neo4j
|
|
22
28
|
@node_var || :result
|
23
29
|
end
|
24
30
|
|
31
|
+
def enumerable_query(node, rel = nil)
|
32
|
+
pluck_this = rel.nil? ? [node] : [node, rel]
|
33
|
+
return self.pluck(*pluck_this) if @association.nil? || caller.nil?
|
34
|
+
cypher_string = self.to_cypher_with_params(pluck_this)
|
35
|
+
association_collection = caller.association_instance_get(cypher_string, @association)
|
36
|
+
if association_collection.nil?
|
37
|
+
association_collection = self.pluck(*pluck_this)
|
38
|
+
caller.association_instance_set(cypher_string, association_collection, @association) unless association_collection.empty?
|
39
|
+
end
|
40
|
+
association_collection
|
41
|
+
end
|
42
|
+
|
25
43
|
def each(node = true, rel = nil, &block)
|
26
44
|
if node && rel
|
27
|
-
|
28
|
-
yield obj, rel
|
29
|
-
end
|
45
|
+
enumerable_query(identity, @rel_var).each { |obj, rel| yield obj, rel }
|
30
46
|
else
|
31
47
|
pluck_this = !rel ? identity : @rel_var
|
32
|
-
|
33
|
-
yield obj
|
34
|
-
end
|
48
|
+
enumerable_query(pluck_this).each { |obj| yield obj }
|
35
49
|
end
|
36
50
|
end
|
37
51
|
|
@@ -97,6 +111,14 @@ module Neo4j
|
|
97
111
|
query.to_cypher
|
98
112
|
end
|
99
113
|
|
114
|
+
# Returns a string of the cypher query with return objects and params
|
115
|
+
# @param [Array] columns array containing symbols of identifiers used in the query
|
116
|
+
# @return [String]
|
117
|
+
def to_cypher_with_params(columns = [:result])
|
118
|
+
final_query = query.return_query(columns)
|
119
|
+
"#{final_query.to_cypher} | params: #{final_query.send(:merge_params)}"
|
120
|
+
end
|
121
|
+
|
100
122
|
# To add a relationship for the node for the association on this QueryProxy
|
101
123
|
def <<(other_node)
|
102
124
|
create(other_node, {})
|
@@ -131,6 +153,7 @@ module Neo4j
|
|
131
153
|
return false if @association.perform_callback(@options[:start_object], other_node, :before) == false
|
132
154
|
|
133
155
|
start_object = @options[:start_object]
|
156
|
+
start_object.clear_association_cache
|
134
157
|
_session.query(context: @options[:context])
|
135
158
|
.start(start: "node(#{start_object.neo_id})", end: "node(#{other_node.neo_id})")
|
136
159
|
.create("start#{_association_arrow(properties, true)}end").exec
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module ActiveNode
|
3
|
+
module Query
|
4
|
+
module QueryProxyFindInBatches
|
5
|
+
def find_in_batches(options = {})
|
6
|
+
query.return(identity).find_in_batches(identity, @model.primary_key, options) do |batch|
|
7
|
+
yield batch
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def find_each(options = {})
|
12
|
+
query.return(identity).find_each(identity, @model.primary_key, options) do |result|
|
13
|
+
yield result
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -4,11 +4,6 @@ module Neo4j
|
|
4
4
|
module QueryProxyMethods
|
5
5
|
class InvalidParameterError < StandardError; end
|
6
6
|
|
7
|
-
def query_with_target(target, &block)
|
8
|
-
target = target.nil? ? identity : target
|
9
|
-
block.yield(target)
|
10
|
-
end
|
11
|
-
|
12
7
|
def first(target=nil)
|
13
8
|
query_with_target(target) { |target| first_and_last("ID(#{target})", target) }
|
14
9
|
end
|
@@ -42,19 +37,36 @@ module Neo4j
|
|
42
37
|
def include?(other, target=nil)
|
43
38
|
raise(InvalidParameterError, ':include? only accepts nodes') unless other.respond_to?(:neo_id)
|
44
39
|
query_with_target(target) do |target|
|
45
|
-
self.where(
|
40
|
+
self.query.where(target => {@model.primary_key => other.id}).return("count(#{target}) AS count").first.count > 0
|
46
41
|
end
|
47
42
|
end
|
48
43
|
|
49
|
-
def exists?(
|
50
|
-
raise(InvalidParameterError, ':exists? only accepts neo_ids') unless
|
44
|
+
def exists?(node_condition=nil, target=nil)
|
45
|
+
raise(InvalidParameterError, ':exists? only accepts neo_ids') unless node_condition.is_a?(Fixnum) || node_condition.is_a?(Hash) || node_condition.nil?
|
51
46
|
query_with_target(target) do |target|
|
52
|
-
start_q = self
|
53
|
-
|
54
|
-
|
47
|
+
start_q = exists_query_start(self, node_condition, target)
|
48
|
+
start_q.query.return("COUNT(#{target}) AS count").first.count > 0
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def query_with_target(target, &block)
|
55
|
+
target = target.nil? ? identity : target
|
56
|
+
block.yield(target)
|
57
|
+
end
|
58
|
+
|
59
|
+
def exists_query_start(origin, condition, target)
|
60
|
+
case
|
61
|
+
when condition.class == Fixnum
|
62
|
+
self.where("ID(#{target}) = #{condition}")
|
63
|
+
when condition.class == Hash
|
64
|
+
self.where(condition.keys.first => condition.values.first)
|
65
|
+
else
|
66
|
+
self
|
55
67
|
end
|
56
68
|
end
|
57
69
|
end
|
58
70
|
end
|
59
71
|
end
|
60
|
-
end
|
72
|
+
end
|
@@ -3,21 +3,21 @@ module Neo4j
|
|
3
3
|
module QueryMethods
|
4
4
|
class InvalidParameterError < StandardError; end
|
5
5
|
|
6
|
-
def exists?(
|
7
|
-
raise(InvalidParameterError, ':exists? only accepts
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
def exists?(node_condition=nil)
|
7
|
+
raise(InvalidParameterError, ':exists? only accepts ids or conditions') unless node_condition.is_a?(Fixnum) || node_condition.is_a?(Hash) || node_condition.nil?
|
8
|
+
query_start = exists_query_start(node_condition)
|
9
|
+
start_q = query_start.respond_to?(:query_as) ? query_start.query_as(:n) : query_start
|
10
|
+
start_q.return("COUNT(n) AS count").first.count > 0
|
11
11
|
end
|
12
12
|
|
13
13
|
# Returns the first node of this class, sorted by ID. Note that this may not be the first node created since Neo4j recycles IDs.
|
14
14
|
def first
|
15
|
-
self.query_as(:n).limit(1).order(
|
15
|
+
self.query_as(:n).limit(1).order(n: primary_key).pluck(:n).first
|
16
16
|
end
|
17
17
|
|
18
18
|
# Returns the last node of this class, sorted by ID. Note that this may not be the first node created since Neo4j recycles IDs.
|
19
19
|
def last
|
20
|
-
self.query_as(:n).limit(1).order(
|
20
|
+
self.query_as(:n).limit(1).order(n: {primary_key => :desc}).pluck(:n).first
|
21
21
|
end
|
22
22
|
|
23
23
|
# @return [Fixnum] number of nodes of this class
|
@@ -38,10 +38,21 @@ module Neo4j
|
|
38
38
|
|
39
39
|
def include?(other)
|
40
40
|
raise(InvalidParameterError, ':include? only accepts nodes') unless other.respond_to?(:neo_id)
|
41
|
-
self.query_as(:n).where(
|
41
|
+
self.query_as(:n).where(n: {primary_key => other.id}).return("count(n) AS count").first.count > 0
|
42
42
|
end
|
43
43
|
|
44
|
-
|
44
|
+
private
|
45
|
+
|
46
|
+
def exists_query_start(node_condition)
|
47
|
+
case
|
48
|
+
when node_condition.class == Fixnum
|
49
|
+
self.query_as(:n).where("ID(n) = #{node_condition}")
|
50
|
+
when node_condition.class == Hash
|
51
|
+
self.where(node_condition.keys.first => node_condition.values.first)
|
52
|
+
else
|
53
|
+
self.query_as(:n)
|
54
|
+
end
|
55
|
+
end
|
45
56
|
end
|
46
57
|
end
|
47
|
-
end
|
58
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Neo4j::ActiveNode
|
2
|
+
# A reflection contains information about an association.
|
3
|
+
# They are often used in connection with form builders to determine associated classes.
|
4
|
+
# This module contains methods related to the creation and retrieval of reflections.
|
5
|
+
module Reflection
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
class_attribute :reflections
|
10
|
+
self.reflections = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Adds methods to the class related to creating and retrieving reflections.
|
14
|
+
module ClassMethods
|
15
|
+
# @param macro [Symbol] the association type, :has_many or :has_one
|
16
|
+
# @param name [Symbol] the association name
|
17
|
+
# @param association_object [Neo4j::ActiveNode::HasN::Association] the association object created in the course of creating this reflection
|
18
|
+
def create_reflection(macro, name, association_object)
|
19
|
+
self.reflections = self.reflections.merge(name => AssociationReflection.new(macro, name, association_object))
|
20
|
+
end
|
21
|
+
|
22
|
+
private :create_reflection
|
23
|
+
# @param [Symbol] an association declared on the model
|
24
|
+
# @return [Neo4j::ActiveNode::Reflection::AssociationReflection] of the given association
|
25
|
+
def reflect_on_association(association)
|
26
|
+
reflections[association.to_sym]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns an array containing one reflection for each association declared in the model.
|
30
|
+
def reflect_on_all_associations(macro = nil)
|
31
|
+
association_reflections = reflections.values
|
32
|
+
macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# The actual reflection object that contains information about the given association.
|
37
|
+
# These should never need to be created manually, they will always be created by declaring a :has_many or :has_one association on a model.
|
38
|
+
class AssociationReflection
|
39
|
+
# The name of the association
|
40
|
+
attr_reader :name
|
41
|
+
|
42
|
+
# The type of association
|
43
|
+
attr_reader :macro
|
44
|
+
|
45
|
+
# The association object referenced by this reflection
|
46
|
+
attr_reader :association
|
47
|
+
|
48
|
+
def initialize(macro, name, association)
|
49
|
+
@macro = macro
|
50
|
+
@name = name
|
51
|
+
@association = association
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the target model
|
55
|
+
def klass
|
56
|
+
@klass ||= class_name.constantize
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the name of the target model
|
60
|
+
def class_name
|
61
|
+
@class_name ||= association.target_class.name
|
62
|
+
end
|
63
|
+
|
64
|
+
def rel_klass
|
65
|
+
@rel_klass ||= rel_class_name.constantize
|
66
|
+
end
|
67
|
+
|
68
|
+
def rel_class_name
|
69
|
+
@rel_class_name ||= association.relationship_class.name.to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
def type
|
73
|
+
@type ||= association.relationship_type
|
74
|
+
end
|
75
|
+
|
76
|
+
def collection?
|
77
|
+
macro == :has_many
|
78
|
+
end
|
79
|
+
|
80
|
+
def validate?
|
81
|
+
true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -5,7 +5,9 @@ module Neo4j
|
|
5
5
|
include Neo4j::Shared::Callbacks
|
6
6
|
|
7
7
|
def save(*args)
|
8
|
-
|
8
|
+
unless self.persisted? || (from_node.respond_to?(:neo_id) && to_node.respond_to?(:neo_id))
|
9
|
+
raise Neo4j::ActiveRel::Persistence::RelInvalidError, 'from_node and to_node must be node objects'
|
10
|
+
end
|
9
11
|
super(*args)
|
10
12
|
end
|
11
13
|
end
|
@@ -10,12 +10,12 @@ module Neo4j::ActiveRel
|
|
10
10
|
# @param [Neo4j::Relationship] start_node the starting node in the relationship.
|
11
11
|
# @param [Neo4j::Relationship] end_node the ending node in the relationship
|
12
12
|
# @param [String] type the relationship type
|
13
|
-
def init_on_load(persisted_rel,
|
13
|
+
def init_on_load(persisted_rel, from_node_id, to_node_id, type)
|
14
14
|
@_persisted_obj = persisted_rel
|
15
15
|
@rel_type = type
|
16
16
|
changed_attributes && changed_attributes.clear
|
17
17
|
@attributes = attributes.merge(persisted_rel.props.stringify_keys)
|
18
|
-
load_nodes(
|
18
|
+
load_nodes(from_node_id, to_node_id)
|
19
19
|
self.default_properties = persisted_rel.props
|
20
20
|
@attributes = convert_properties_to :ruby, @attributes
|
21
21
|
end
|
@@ -3,14 +3,7 @@ module Neo4j::ActiveRel
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
include Neo4j::Shared::Persistence
|
5
5
|
|
6
|
-
class RelInvalidError < RuntimeError
|
7
|
-
attr_reader :record
|
8
|
-
|
9
|
-
def initialize(record)
|
10
|
-
@record = record
|
11
|
-
super(@record.errors.full_messages.join(", "))
|
12
|
-
end
|
13
|
-
end
|
6
|
+
class RelInvalidError < RuntimeError; end
|
14
7
|
|
15
8
|
class ModelClassInvalidError < RuntimeError; end
|
16
9
|
|
@@ -31,7 +24,7 @@ module Neo4j::ActiveRel
|
|
31
24
|
set_timestamps
|
32
25
|
properties = convert_properties_to :db, props
|
33
26
|
rel = _create_rel(from_node, to_node, properties)
|
34
|
-
init_on_load(rel,
|
27
|
+
init_on_load(rel, from_node, to_node, @rel_type)
|
35
28
|
true
|
36
29
|
end
|
37
30
|
|
@@ -61,10 +54,10 @@ module Neo4j::ActiveRel
|
|
61
54
|
|
62
55
|
def confirm_node_classes
|
63
56
|
[from_node, to_node].each do |node|
|
64
|
-
|
65
|
-
next if
|
66
|
-
unless
|
67
|
-
raise ModelClassInvalidError, "Node class was #{node.class}, expected #{self.class.send(
|
57
|
+
type = from_node == node ? :_from_class : :_to_class
|
58
|
+
next if allows_any_class?(type)
|
59
|
+
unless class_as_constant(type) == node.class
|
60
|
+
raise ModelClassInvalidError, "Node class was #{node.class}, expected #{self.class.send(type)}"
|
68
61
|
end
|
69
62
|
end
|
70
63
|
end
|
@@ -75,5 +68,21 @@ module Neo4j::ActiveRel
|
|
75
68
|
set_classname(props)
|
76
69
|
from_node.create_rel(type, to_node, props)
|
77
70
|
end
|
71
|
+
|
72
|
+
def class_as_constant(type)
|
73
|
+
given_class = self.class.send(type)
|
74
|
+
case
|
75
|
+
when given_class.is_a?(String)
|
76
|
+
given_class.constantize
|
77
|
+
when given_class.is_a?(Symbol)
|
78
|
+
given_class.to_s.constantize
|
79
|
+
else
|
80
|
+
given_class
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def allows_any_class?(type)
|
85
|
+
self.class.send(type) == :any || self.class.send(type) == false
|
86
|
+
end
|
78
87
|
end
|
79
|
-
end
|
88
|
+
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
module Neo4j::ActiveRel
|
3
2
|
module Property
|
4
3
|
extend ActiveSupport::Concern
|
@@ -7,7 +6,7 @@ module Neo4j::ActiveRel
|
|
7
6
|
%w[to_node from_node].each do |direction|
|
8
7
|
define_method("#{direction}") { instance_variable_get("@#{direction}") }
|
9
8
|
define_method("#{direction}=") do |argument|
|
10
|
-
raise FrozenRelError,
|
9
|
+
raise FrozenRelError, 'Relationship start/end nodes cannot be changed once persisted' if self.persisted?
|
11
10
|
instance_variable_set("@#{direction}", argument)
|
12
11
|
end
|
13
12
|
end
|
@@ -15,18 +14,18 @@ module Neo4j::ActiveRel
|
|
15
14
|
alias_method :start_node, :from_node
|
16
15
|
alias_method :end_node, :to_node
|
17
16
|
|
17
|
+
# @return [String] a string representing the relationship type that will be created
|
18
18
|
def type
|
19
19
|
self.class._type
|
20
20
|
end
|
21
21
|
|
22
|
-
def initialize(attributes={}, options={})
|
22
|
+
def initialize(attributes = {}, options = {})
|
23
23
|
super(attributes, options)
|
24
24
|
|
25
25
|
send_props(@relationship_props) unless @relationship_props.nil?
|
26
26
|
end
|
27
27
|
|
28
28
|
module ClassMethods
|
29
|
-
|
30
29
|
# Extracts keys from attributes hash which are relationships of the model
|
31
30
|
# TODO: Validate separately that relationships are getting the right values? Perhaps also store the values and persist relationships on save?
|
32
31
|
def extract_association_attributes!(attributes)
|
@@ -43,10 +42,12 @@ module Neo4j::ActiveRel
|
|
43
42
|
alias_method :start_class, :from_class
|
44
43
|
alias_method :end_class, :to_class
|
45
44
|
|
45
|
+
# @param type [String] sets the relationship type when creating relationships via this class
|
46
46
|
def type(type = nil)
|
47
47
|
@rel_type = type
|
48
48
|
end
|
49
49
|
|
50
|
+
# @return [String] a string representing the relationship type that will be created
|
50
51
|
def _type
|
51
52
|
@rel_type
|
52
53
|
end
|
@@ -54,15 +55,13 @@ module Neo4j::ActiveRel
|
|
54
55
|
def load_entity(id)
|
55
56
|
Neo4j::Node.load(id)
|
56
57
|
end
|
57
|
-
|
58
58
|
end
|
59
59
|
|
60
60
|
private
|
61
61
|
|
62
|
-
def load_nodes(
|
63
|
-
@from_node = RelatedNode.new(
|
64
|
-
@to_node = RelatedNode.new(
|
62
|
+
def load_nodes(from_node = nil, to_node = nil)
|
63
|
+
@from_node = RelatedNode.new(from_node)
|
64
|
+
@to_node = RelatedNode.new(to_node)
|
65
65
|
end
|
66
|
-
|
67
66
|
end
|
68
|
-
end
|
67
|
+
end
|