neo4j 1.0.0.beta.27-java → 1.0.0.beta.28-java
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.
- data/CONTRIBUTORS +1 -0
- data/lib/neo4j.rb +38 -72
- data/lib/neo4j/{algo.rb → algo/algo.rb} +5 -1
- data/lib/neo4j/batch/batch.rb +2 -0
- data/lib/neo4j/batch/indexer.rb +108 -0
- data/lib/neo4j/batch/inserter.rb +168 -0
- data/lib/neo4j/database.rb +13 -8
- data/lib/neo4j/{mapping/class_methods/list.rb → has_list/class_methods.rb} +2 -4
- data/lib/neo4j/has_list/has_list.rb +3 -0
- data/lib/neo4j/{mapping/has_list.rb → has_list/mapping.rb} +2 -2
- data/lib/neo4j/{mapping/class_methods/relationship.rb → has_n/class_methods.rb} +42 -12
- data/lib/neo4j/has_n/decl_relationship_dsl.rb +216 -0
- data/lib/neo4j/has_n/has_n.rb +3 -0
- data/lib/neo4j/{mapping/has_n.rb → has_n/mapping.rb} +16 -7
- data/lib/neo4j/index/index.rb +5 -0
- data/lib/neo4j/index/indexer.rb +27 -22
- data/lib/neo4j/index/lucene_query.rb +3 -1
- data/lib/neo4j/jars/core/neo4j-graph-algo-0.8-1.3.M01.jar +0 -0
- data/lib/neo4j/jars/core/neo4j-index-1.3-1.3.M01.jar +0 -0
- data/lib/neo4j/jars/core/neo4j-kernel-1.3-1.3.M01.jar +0 -0
- data/lib/neo4j/jars/core/neo4j-lucene-index-0.5-1.3.M01.jar +0 -0
- data/lib/neo4j/jars/ha/{neo4j-ha-0.6-SNAPSHOT.jar → neo4j-ha-0.6-1.3.M01.jar} +0 -0
- data/lib/neo4j/jars/ha/neo4j-management-1.3-1.3.M01.jar +0 -0
- data/lib/neo4j/jars/ha/neo4j-shell-1.3-1.3.M01.jar +0 -0
- data/lib/neo4j/migrations/class_methods.rb +102 -0
- data/lib/neo4j/migrations/extensions.rb +10 -9
- data/lib/neo4j/migrations/lazy_node_mixin.rb +50 -0
- data/lib/neo4j/migrations/migration.rb +84 -81
- data/lib/neo4j/migrations/migrations.rb +6 -100
- data/lib/neo4j/migrations/node_mixin.rb +80 -0
- data/lib/neo4j/migrations/ref_node_wrapper.rb +32 -0
- data/lib/neo4j/neo4j.rb +11 -0
- data/lib/neo4j/node.rb +55 -25
- data/lib/neo4j/{mapping/class_methods/init_node.rb → node_mixin/class_methods.rb} +3 -3
- data/lib/neo4j/{mapping → node_mixin}/node_mixin.rb +35 -18
- data/lib/neo4j/{mapping/class_methods/property.rb → property/class_methods.rb} +5 -4
- data/lib/neo4j/{property.rb → property/property.rb} +2 -0
- data/lib/neo4j/rails/finders.rb +21 -7
- data/lib/neo4j/rails/rails.rb +19 -0
- data/lib/neo4j/rails/timestamps.rb +1 -1
- data/lib/neo4j/relationship.rb +7 -0
- data/lib/neo4j/{mapping/class_methods/init_rel.rb → relationship_mixin/class_methods.rb} +4 -4
- data/lib/neo4j/{mapping → relationship_mixin}/relationship_mixin.rb +23 -5
- data/lib/neo4j/rels/rels.rb +85 -0
- data/lib/neo4j/rels/traverser.rb +102 -0
- data/lib/neo4j/{mapping/class_methods/rule.rb → rule/class_methods.rb} +11 -11
- data/lib/neo4j/rule/functions/count.rb +37 -0
- data/lib/neo4j/rule/functions/function.rb +74 -0
- data/lib/neo4j/rule/functions/functions.rb +3 -0
- data/lib/neo4j/rule/functions/sum.rb +29 -0
- data/lib/neo4j/rule/rule.rb +5 -0
- data/lib/neo4j/rule/rule_event_listener.rb +162 -0
- data/lib/neo4j/rule/rule_node.rb +182 -0
- data/lib/neo4j/to_java.rb +0 -14
- data/lib/neo4j/traversal/filter_predicate.rb +25 -0
- data/lib/neo4j/traversal/prune_evaluator.rb +14 -0
- data/lib/neo4j/traversal/rel_expander.rb +31 -0
- data/lib/neo4j/traversal/traversal.rb +90 -0
- data/lib/neo4j/traversal/traverser.rb +173 -0
- data/lib/neo4j/{type_converters.rb → type_converters/type_converters.rb} +0 -0
- data/lib/neo4j/version.rb +1 -1
- data/lib/test.rb~ +2 -0
- data/neo4j.gemspec +11 -10
- metadata +48 -37
- data/lib/neo4j/functions/count.rb +0 -33
- data/lib/neo4j/functions/function.rb +0 -72
- data/lib/neo4j/functions/sum.rb +0 -27
- data/lib/neo4j/jars/core/neo4j-graph-algo-0.8-SNAPSHOT.jar +0 -0
- data/lib/neo4j/jars/core/neo4j-index-1.3-SNAPSHOT.jar +0 -0
- data/lib/neo4j/jars/core/neo4j-kernel-1.3-SNAPSHOT.jar +0 -0
- data/lib/neo4j/jars/core/neo4j-lucene-index-0.5-SNAPSHOT.jar +0 -0
- data/lib/neo4j/jars/ha/neo4j-management-1.3-SNAPSHOT.jar +0 -0
- data/lib/neo4j/jars/ha/neo4j-shell-1.3-SNAPSHOT.jar +0 -0
- data/lib/neo4j/mapping/decl_relationship_dsl.rb +0 -214
- data/lib/neo4j/mapping/rule.rb +0 -158
- data/lib/neo4j/mapping/rule_node.rb +0 -176
- data/lib/neo4j/migrations.rb +0 -12
- data/lib/neo4j/migrations/global_migration.rb +0 -29
- data/lib/neo4j/migrations/lazy_migration_mixin.rb +0 -47
- data/lib/neo4j/migrations/migration_mixin.rb +0 -78
- data/lib/neo4j/node_mixin.rb +0 -4
- data/lib/neo4j/node_relationship.rb +0 -161
- data/lib/neo4j/node_traverser.rb +0 -224
- data/lib/neo4j/relationship_mixin.rb +0 -4
- data/lib/neo4j/relationship_traverser.rb +0 -92
@@ -0,0 +1,37 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Rule
|
3
|
+
module Functions
|
4
|
+
|
5
|
+
# A function for counting number of nodes of a given class.
|
6
|
+
class Count < Function
|
7
|
+
def initialize
|
8
|
+
@property = '_classname'
|
9
|
+
end
|
10
|
+
|
11
|
+
def calculate?(changed_property)
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete(rule_name, rule_node, old_value)
|
16
|
+
key = rule_node_property(rule_name)
|
17
|
+
rule_node[key] ||= 0
|
18
|
+
rule_node[key] -= 1
|
19
|
+
end
|
20
|
+
|
21
|
+
def add(rule_name, rule_node, new_value)
|
22
|
+
key = rule_node_property(rule_name)
|
23
|
+
rule_node[key] ||= 0
|
24
|
+
rule_node[key] += 1
|
25
|
+
end
|
26
|
+
|
27
|
+
def update(*)
|
28
|
+
# we are only counting, not interested in property changes
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.function_name
|
32
|
+
:count
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Rule
|
3
|
+
module Functions
|
4
|
+
|
5
|
+
# The base class of rule functions.
|
6
|
+
#
|
7
|
+
# You are expected to at least implement two methods:
|
8
|
+
# * update :: update the rule node value of this function
|
9
|
+
# * function_name :: the name of this function, the name of the generated method - A class method !
|
10
|
+
#
|
11
|
+
class Function
|
12
|
+
|
13
|
+
# Initialize the the function with a property which is usually the same as the function identity.
|
14
|
+
# See the #calculate? method how this property is used.
|
15
|
+
#
|
16
|
+
def initialize(property)
|
17
|
+
@property = property.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
"Function #{self.class.function_name} function_id: #{function_id}"
|
22
|
+
end
|
23
|
+
|
24
|
+
# Decides if the function should be called are not
|
25
|
+
#
|
26
|
+
def calculate?(changed_property)
|
27
|
+
@property == changed_property
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
# The identity of the function.
|
32
|
+
# Used to identify function.
|
33
|
+
#
|
34
|
+
# ==== Example
|
35
|
+
# Person.sum(:young, :age)
|
36
|
+
#
|
37
|
+
# In the example above the property :age is the used to identify which function will be called
|
38
|
+
# since there could be several sum method. In the example we want use the sum method that uses the :age property.
|
39
|
+
#
|
40
|
+
def function_id
|
41
|
+
@property
|
42
|
+
end
|
43
|
+
|
44
|
+
# The value of the rule
|
45
|
+
def value(rule_node, rule_name)
|
46
|
+
key = rule_node_property(rule_name)
|
47
|
+
rule_node[key] || 0
|
48
|
+
end
|
49
|
+
|
50
|
+
# Called when a node is removed from a rule group
|
51
|
+
# Default is calling update method which is expected to be implemented in a subclass
|
52
|
+
def delete(rule_name, rule_node, old_value)
|
53
|
+
update(rule_name, rule_node, old_value, nil)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Called when a node is added to a rule group
|
57
|
+
# Default is calling update method which is expected to be implemented in a subclass
|
58
|
+
def add(rule_name, rule_node, new_value)
|
59
|
+
update(rule_name, rule_node, nil, new_value)
|
60
|
+
end
|
61
|
+
|
62
|
+
# the name of the property that holds the value of the function
|
63
|
+
def rule_node_property(rule_name)
|
64
|
+
self.class.rule_node_property(self.class.function_name, rule_name, @property)
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.rule_node_property(function_name, rule_name, prop)
|
68
|
+
"_#{function_name}_#{rule_name}_#{prop}"
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Rule
|
3
|
+
module Functions
|
4
|
+
|
5
|
+
class Sum < Function
|
6
|
+
# Updates the function's value.
|
7
|
+
# Called after the transactions commits and a property has been changed on a node.
|
8
|
+
#
|
9
|
+
# ==== Arguments
|
10
|
+
# * rule_name :: the name of the rule group
|
11
|
+
# * rule_node :: the node which contains the value of this function
|
12
|
+
# * old_value new value :: the changed value of the property (when the transaction commits)
|
13
|
+
def update(rule_name, rule_node, old_value, new_value)
|
14
|
+
key = rule_node_property(rule_name)
|
15
|
+
rule_node[key] ||= 0
|
16
|
+
old_value ||= 0
|
17
|
+
new_value ||= 0
|
18
|
+
rule_node[key] += new_value - old_value
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.function_name
|
22
|
+
:sum
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Rule
|
3
|
+
|
4
|
+
|
5
|
+
# Holds all defined rules and trigger them when an event is received.
|
6
|
+
#
|
7
|
+
# See Neo4j::Rule::ClassMethods
|
8
|
+
#
|
9
|
+
class RuleEventListener #:nodoc:
|
10
|
+
|
11
|
+
attr_reader :rule_name, :filter, :triggers, :functions
|
12
|
+
|
13
|
+
def initialize(rule_name, props, &block)
|
14
|
+
@rule_name = rule_name
|
15
|
+
@triggers = props[:triggers]
|
16
|
+
@functions = props[:functions]
|
17
|
+
@triggers = [@triggers] if @triggers && !@triggers.respond_to?(:each)
|
18
|
+
@functions = [@functions] if @functions && !@functions.respond_to?(:each)
|
19
|
+
@filter = block
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"RuleEventListener #{rule_name} props=#{props.inspect}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def find_function(function_name, function_id)
|
27
|
+
function_id = function_id.to_s
|
28
|
+
@functions && @functions.find { |f| f.function_id == function_id && f.class.function_name == function_name }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Reconstruct the properties given when created this rule
|
32
|
+
# Needed when inheriting a rule and we want to duplicate a rule
|
33
|
+
def props
|
34
|
+
props = {}
|
35
|
+
props[:triggers] = @triggers if @triggers
|
36
|
+
props[:functions] = @functions if @functions
|
37
|
+
props
|
38
|
+
end
|
39
|
+
|
40
|
+
def functions_for(property)
|
41
|
+
@functions && @functions.find_all { |f| f.calculate?(property) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def execute_filter(node)
|
45
|
+
if @filter.nil?
|
46
|
+
true
|
47
|
+
elsif @filter.arity != 1
|
48
|
+
node.wrapper.instance_eval(&@filter)
|
49
|
+
else
|
50
|
+
@filter.call(node)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# ------------------------------------------------------------------------------------------------------------------
|
55
|
+
# Class Methods
|
56
|
+
# ------------------------------------------------------------------------------------------------------------------
|
57
|
+
|
58
|
+
class << self
|
59
|
+
def add(clazz, rule_name, props, &block)
|
60
|
+
rule_node = rule_node_for(clazz.to_s)
|
61
|
+
rule_node.remove_rule(rule_name) # remove any previously inherited rules
|
62
|
+
rule = RuleEventListener.new(rule_name, props, &block)
|
63
|
+
rule_node.add_rule(rule)
|
64
|
+
rule
|
65
|
+
end
|
66
|
+
|
67
|
+
def rule_names_for(clazz)
|
68
|
+
rule_node = rule_node_for(clazz)
|
69
|
+
rule_node.rules.map { |rule| rule.rule_name }
|
70
|
+
end
|
71
|
+
|
72
|
+
def rule_node_for(clazz)
|
73
|
+
return nil if clazz.nil?
|
74
|
+
@rule_nodes ||= {}
|
75
|
+
@rule_nodes[clazz.to_s] ||= RuleNode.new(clazz)
|
76
|
+
end
|
77
|
+
|
78
|
+
def inherit(parent_class, subclass)
|
79
|
+
# copy all the rules
|
80
|
+
if rule_node = rule_node_for(parent_class)
|
81
|
+
rule_node.inherit(subclass)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def delete(clazz)
|
86
|
+
if rule_node = rule_node_for(clazz)
|
87
|
+
rule_node.delete_node
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def trigger?(node)
|
92
|
+
classname = node[:_classname]
|
93
|
+
@rule_nodes && classname && rule_node_for(classname)
|
94
|
+
end
|
95
|
+
|
96
|
+
def trigger_rules(node, *changes)
|
97
|
+
classname = node[:_classname]
|
98
|
+
return unless classname # there are no rules if there is not a :_classname property
|
99
|
+
rule_node = rule_node_for(classname)
|
100
|
+
rule_node.execute_rules(node, *changes)
|
101
|
+
|
102
|
+
# recursively add relationships for all the parent classes with rules that also pass for this node
|
103
|
+
if (clazz = eval("#{classname}.superclass")) && clazz.include?(Neo4j::NodeMixin)
|
104
|
+
rule_node = rule_node_for(clazz)
|
105
|
+
rule_node && rule_node.execute_rules(node, *changes)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
# ----------------------------------------------------------------------------------------------------------------
|
111
|
+
# Event handling methods
|
112
|
+
# ----------------------------------------------------------------------------------------------------------------
|
113
|
+
|
114
|
+
def on_relationship_created(rel, *)
|
115
|
+
trigger_start_node = trigger?(rel._start_node)
|
116
|
+
trigger_end_node = trigger?(rel._end_node)
|
117
|
+
trigger_rules(rel._start_node) if trigger_start_node
|
118
|
+
trigger_rules(rel._end_node) if trigger_end_node
|
119
|
+
end
|
120
|
+
|
121
|
+
def on_property_changed(node, *changes)
|
122
|
+
trigger_rules(node, *changes) if trigger?(node)
|
123
|
+
end
|
124
|
+
|
125
|
+
def on_node_deleted(node, old_properties, data)
|
126
|
+
# have we deleted a rule node ?
|
127
|
+
del_rule_node = @rule_nodes && @rule_nodes.values.find { |rn| rn.rule_node?(node) }
|
128
|
+
del_rule_node && del_rule_node.clear_rule_node
|
129
|
+
return if del_rule_node
|
130
|
+
|
131
|
+
# do we have prop_aggregations for this
|
132
|
+
clazz = old_properties['_classname']
|
133
|
+
rule_node = rule_node_for(clazz)
|
134
|
+
return if rule_node.nil?
|
135
|
+
|
136
|
+
id = node.getId
|
137
|
+
rule_node.rules.each do |rule|
|
138
|
+
next if rule.functions.nil?
|
139
|
+
rule_name = rule.rule_name.to_s
|
140
|
+
|
141
|
+
# is the rule node deleted ?
|
142
|
+
deleted_rule_node = data.deletedNodes.find { |n| n == rule_node.rule_node }
|
143
|
+
next if deleted_rule_node
|
144
|
+
|
145
|
+
rule.functions.each do |function|
|
146
|
+
next unless data.deletedRelationships.find do |r|
|
147
|
+
r.getEndNode().getId() == id && r.rel_type == rule_name
|
148
|
+
end
|
149
|
+
previous_value = old_properties[function.function_id]
|
150
|
+
function.delete(rule_name, rule_node.rule_node, previous_value) if previous_value
|
151
|
+
end if rule.functions
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def on_neo4j_started(*)
|
156
|
+
@rule_nodes.each_value { |rule_node| rule_node.on_neo4j_started } if @rule_nodes
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Rule
|
3
|
+
|
4
|
+
# This is the node that has relationships to all nodes of a given class.
|
5
|
+
# For example if the PersonNode has a rule then it will also have one RuleNode
|
6
|
+
# from where it will create relationships to each created node of type PersonNode.
|
7
|
+
# The RuleNode can also be used to hold properties for functions, like sum and count.
|
8
|
+
#
|
9
|
+
class RuleNode
|
10
|
+
attr_reader :rules
|
11
|
+
|
12
|
+
def initialize(clazz)
|
13
|
+
@clazz = clazz
|
14
|
+
@rules = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
"RuleNode #{@clazz}, node #{rule_node} #rules: #{@rules.size}"
|
19
|
+
end
|
20
|
+
|
21
|
+
# returns true if the rule node exist yet in the database
|
22
|
+
def node_exist?
|
23
|
+
!Neo4j.ref_node.rel?(@clazz)
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_node
|
27
|
+
Neo4j::Transaction.run do
|
28
|
+
node = Neo4j::Node.new
|
29
|
+
Neo4j.ref_node.outgoing(@clazz) << node
|
30
|
+
node
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def inherit(subclass)
|
35
|
+
@rules.each do |rule|
|
36
|
+
subclass.rule rule.rule_name, rule.props, &rule.filter
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete_node
|
41
|
+
if Neo4j.ref_node.rel?(@clazz)
|
42
|
+
Neo4j.ref_node.outgoing(@clazz).each { |n| n.del }
|
43
|
+
end
|
44
|
+
clear_rule_node
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_node
|
48
|
+
Neo4j.ref_node.rel?(@clazz.to_s) && Neo4j.ref_node._rel(:outgoing, @clazz.to_s)._end_node
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_neo4j_started
|
52
|
+
# initialize the rule node when neo4j starts
|
53
|
+
@rule_node = find_node || create_node
|
54
|
+
end
|
55
|
+
|
56
|
+
def rule_node
|
57
|
+
@rule_node ||= find_node || create_node
|
58
|
+
end
|
59
|
+
|
60
|
+
def rule_node?(node)
|
61
|
+
@rule_node == node
|
62
|
+
end
|
63
|
+
|
64
|
+
def clear_rule_node
|
65
|
+
@rule_node = nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def rule_names
|
69
|
+
@rules.map { |r| r.rule_name }
|
70
|
+
end
|
71
|
+
|
72
|
+
def find_rule(rule_name)
|
73
|
+
@rules.find { |rule| rule.rule_name == rule_name }
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_rule(rule)
|
77
|
+
@rules << rule
|
78
|
+
end
|
79
|
+
|
80
|
+
def remove_rule(rule_name)
|
81
|
+
r = find_rule(rule_name)
|
82
|
+
r && @rules.delete(r)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Return a traversal object with methods for each rule and function.
|
86
|
+
# E.g. Person.all.old or Person.all.sum(:age)
|
87
|
+
def traversal(rule_name)
|
88
|
+
# define method on the traversal
|
89
|
+
traversal = rule_node.outgoing(rule_name)
|
90
|
+
@rules.each do |rule|
|
91
|
+
traversal.filter_method(rule.rule_name) do |path|
|
92
|
+
path.end_node.rel?(rule.rule_name, :incoming)
|
93
|
+
end
|
94
|
+
rule.functions && rule.functions.each do |func|
|
95
|
+
traversal.functions_method(func, self, rule_name)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
traversal
|
99
|
+
end
|
100
|
+
|
101
|
+
def find_function(rule_name, function_name, function_id)
|
102
|
+
rule = find_rule(rule_name)
|
103
|
+
rule.find_function(function_name, function_id)
|
104
|
+
end
|
105
|
+
|
106
|
+
def execute_rules(node, *changes)
|
107
|
+
@rules.each do |rule|
|
108
|
+
execute_rule(rule, node, *changes)
|
109
|
+
execute_other_rules(rule, node)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def execute_other_rules(rule, node)
|
114
|
+
rule.triggers && rule.triggers.each do |rel_type|
|
115
|
+
node.incoming(rel_type).each { |n| n.trigger_rules }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def execute_rule(rule, node, *changes)
|
120
|
+
if rule.execute_filter(node)
|
121
|
+
if connected?(rule.rule_name, node)
|
122
|
+
# it was already connected - the node is in the same rule group but a property has changed
|
123
|
+
execute_update_functions(rule, *changes)
|
124
|
+
else
|
125
|
+
# the node has changed or is in a new rule group
|
126
|
+
connect(rule.rule_name, node)
|
127
|
+
execute_add_functions(rule, *changes)
|
128
|
+
end
|
129
|
+
else
|
130
|
+
if break_connection(rule.rule_name, node)
|
131
|
+
# the node has been removed from a rule group
|
132
|
+
execute_delete_functions(rule, *changes)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def execute_update_functions(rule, *changes)
|
138
|
+
if functions = find_functions_for_changes(rule, *changes)
|
139
|
+
functions && functions.each { |f| f.update(rule.rule_name, rule_node, changes[1], changes[2]) }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def execute_add_functions(rule, *changes)
|
144
|
+
if functions = find_functions_for_changes(rule, *changes)
|
145
|
+
functions && functions.each { |f| f.add(rule.rule_name, rule_node, changes[2]) }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def execute_delete_functions(rule, *changes)
|
150
|
+
if functions = find_functions_for_changes(rule, *changes)
|
151
|
+
functions.each { |f| f.delete(rule.rule_name, rule_node, changes[1]) }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def find_functions_for_changes(rule, *changes)
|
156
|
+
!changes.empty? && rule.functions_for(changes[0])
|
157
|
+
end
|
158
|
+
|
159
|
+
# work out if two nodes are connected by a particular relationship
|
160
|
+
# uses the end_node to start with because it's more likely to have less relationships to go through
|
161
|
+
# (just the number of superclasses it has really)
|
162
|
+
def connected?(rule_name, end_node)
|
163
|
+
end_node.incoming(rule_name).find { |n| n == rule_node }
|
164
|
+
end
|
165
|
+
|
166
|
+
def connect(rule_name, end_node)
|
167
|
+
rule_node.outgoing(rule_name) << end_node
|
168
|
+
end
|
169
|
+
|
170
|
+
# sever a direct one-to-one relationship if it exists
|
171
|
+
def break_connection(rule_name, end_node)
|
172
|
+
rel = end_node._rels(:incoming, rule_name).find { |r| r._start_node == rule_node }
|
173
|
+
rel && rel.del
|
174
|
+
!rel.nil?
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|