neo4j-wrapper 0.0.1-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.
Files changed (88) hide show
  1. data/Gemfile +10 -0
  2. data/README.rdoc +64 -0
  3. data/lib/db/active_tx_log +1 -0
  4. data/lib/db/index/lucene/node/Person_exact/_0.fdt +0 -0
  5. data/lib/db/index/lucene/node/Person_exact/_0.fdx +0 -0
  6. data/lib/db/index/lucene/node/Person_exact/_0.fnm +1 -0
  7. data/lib/db/index/lucene/node/Person_exact/_0.frq +0 -0
  8. data/lib/db/index/lucene/node/Person_exact/_0.nrm +1 -0
  9. data/lib/db/index/lucene/node/Person_exact/_0.prx +0 -0
  10. data/lib/db/index/lucene/node/Person_exact/_0.tii +0 -0
  11. data/lib/db/index/lucene/node/Person_exact/_0.tis +0 -0
  12. data/lib/db/index/lucene/node/Person_exact/_1.fdt +0 -0
  13. data/lib/db/index/lucene/node/Person_exact/_1.fdx +0 -0
  14. data/lib/db/index/lucene/node/Person_exact/_1.fnm +1 -0
  15. data/lib/db/index/lucene/node/Person_exact/_1.frq +0 -0
  16. data/lib/db/index/lucene/node/Person_exact/_1.nrm +1 -0
  17. data/lib/db/index/lucene/node/Person_exact/_1.prx +0 -0
  18. data/lib/db/index/lucene/node/Person_exact/_1.tii +0 -0
  19. data/lib/db/index/lucene/node/Person_exact/_1.tis +0 -0
  20. data/lib/db/index/lucene/node/Person_exact/_2.fdt +0 -0
  21. data/lib/db/index/lucene/node/Person_exact/_2.fdx +0 -0
  22. data/lib/db/index/lucene/node/Person_exact/_2.fnm +1 -0
  23. data/lib/db/index/lucene/node/Person_exact/_2.frq +0 -0
  24. data/lib/db/index/lucene/node/Person_exact/_2.nrm +1 -0
  25. data/lib/db/index/lucene/node/Person_exact/_2.prx +0 -0
  26. data/lib/db/index/lucene/node/Person_exact/_2.tii +0 -0
  27. data/lib/db/index/lucene/node/Person_exact/_2.tis +0 -0
  28. data/lib/db/index/lucene/node/Person_exact/segments.gen +0 -0
  29. data/lib/db/index/lucene/node/Person_exact/segments_3 +0 -0
  30. data/lib/db/index/lucene-store.db +0 -0
  31. data/lib/db/index/lucene.log.active +0 -0
  32. data/lib/db/index.db +0 -0
  33. data/lib/db/messages.log +826 -0
  34. data/lib/db/neostore +0 -0
  35. data/lib/db/neostore.id +0 -0
  36. data/lib/db/neostore.nodestore.db +0 -0
  37. data/lib/db/neostore.nodestore.db.id +0 -0
  38. data/lib/db/neostore.propertystore.db +0 -0
  39. data/lib/db/neostore.propertystore.db.arrays +0 -0
  40. data/lib/db/neostore.propertystore.db.arrays.id +0 -0
  41. data/lib/db/neostore.propertystore.db.id +0 -0
  42. data/lib/db/neostore.propertystore.db.index +0 -0
  43. data/lib/db/neostore.propertystore.db.index.id +0 -0
  44. data/lib/db/neostore.propertystore.db.index.keys +0 -0
  45. data/lib/db/neostore.propertystore.db.index.keys.id +0 -0
  46. data/lib/db/neostore.propertystore.db.strings +0 -0
  47. data/lib/db/neostore.propertystore.db.strings.id +0 -0
  48. data/lib/db/neostore.relationshipstore.db +0 -0
  49. data/lib/db/neostore.relationshipstore.db.id +0 -0
  50. data/lib/db/neostore.relationshiptypestore.db +0 -0
  51. data/lib/db/neostore.relationshiptypestore.db.id +0 -0
  52. data/lib/db/neostore.relationshiptypestore.db.names +0 -0
  53. data/lib/db/neostore.relationshiptypestore.db.names.id +0 -0
  54. data/lib/db/nioneo_logical.log.active +0 -0
  55. data/lib/db/tm_tx_log.1 +0 -0
  56. data/lib/example.rb +34 -0
  57. data/lib/neo4j/identity_map.rb +109 -0
  58. data/lib/neo4j/node_mixin.rb +74 -0
  59. data/lib/neo4j/relationship_mixin.rb +62 -0
  60. data/lib/neo4j/type_converters/type_converters.rb +333 -0
  61. data/lib/neo4j-wrapper/class_methods.rb +27 -0
  62. data/lib/neo4j-wrapper/find.rb +65 -0
  63. data/lib/neo4j-wrapper/has_n/class_methods.rb +131 -0
  64. data/lib/neo4j-wrapper/has_n/decl_rel.rb +261 -0
  65. data/lib/neo4j-wrapper/has_n/instance_methods.rb +12 -0
  66. data/lib/neo4j-wrapper/has_n/nodes.rb +83 -0
  67. data/lib/neo4j-wrapper/node_mixin/class_methods.rb +53 -0
  68. data/lib/neo4j-wrapper/node_mixin/delegates.rb +140 -0
  69. data/lib/neo4j-wrapper/node_mixin/initialize.rb +43 -0
  70. data/lib/neo4j-wrapper/properties/class_methods.rb +150 -0
  71. data/lib/neo4j-wrapper/relationship_mixin/class_methods.rb +30 -0
  72. data/lib/neo4j-wrapper/relationship_mixin/delegates.rb +110 -0
  73. data/lib/neo4j-wrapper/relationship_mixin/initialize.rb +35 -0
  74. data/lib/neo4j-wrapper/rule/class_methods.rb +204 -0
  75. data/lib/neo4j-wrapper/rule/event_listener.rb +66 -0
  76. data/lib/neo4j-wrapper/rule/functions/count.rb +45 -0
  77. data/lib/neo4j-wrapper/rule/functions/function.rb +76 -0
  78. data/lib/neo4j-wrapper/rule/functions/sum.rb +31 -0
  79. data/lib/neo4j-wrapper/rule/instance_methods.rb +16 -0
  80. data/lib/neo4j-wrapper/rule/neo4j_core_ext/traverser.rb +29 -0
  81. data/lib/neo4j-wrapper/rule/rule.rb +134 -0
  82. data/lib/neo4j-wrapper/rule/rule_node.rb +205 -0
  83. data/lib/neo4j-wrapper/version.rb +5 -0
  84. data/lib/neo4j-wrapper/wrapper.rb +38 -0
  85. data/lib/neo4j-wrapper.rb +35 -0
  86. data/lib/test.rb +61 -0
  87. data/neo4j-wrapper.gemspec +31 -0
  88. metadata +162 -0
@@ -0,0 +1,204 @@
1
+ module Neo4j
2
+ module Wrapper
3
+ module Rule
4
+
5
+
6
+ # Allows you to group nodes by providing a rule.
7
+ #
8
+ # === Example, finding all nodes of a certain class
9
+ # Just add a rule without a code block, then all nodes of that class will be grouped under the given key (<tt>all</tt>
10
+ # for the example below).
11
+ #
12
+ # class Person
13
+ # include Neo4j::NodeMixin
14
+ # rule :all
15
+ # end
16
+ #
17
+ # Then you can get all the nodes of type Person (and siblings) by
18
+ # Person.all.each {|x| ...}
19
+ #
20
+ # === Example, finding all nodes with a given condition on a property
21
+ #
22
+ # class Person
23
+ # include Neo4j::NodeMixin
24
+ # property :age
25
+ # rule(:old) { age > 10 }
26
+ # end
27
+ #
28
+ # Now we can find all nodes with a property <tt>age</tt> above 10.
29
+ #
30
+ # === Chain Rules
31
+ #
32
+ # class NewsStory
33
+ # include Neo4j::NodeMixin
34
+ # has_n :readers
35
+ # rule(:featured) { |node| node[:featured] == true }
36
+ # rule(:young_readers) { !readers.find{|user| !user.young?}}
37
+ # end
38
+ #
39
+ # You can combine two rules. Let say you want to find all stories which are featured and has young readers:
40
+ # NewsStory.featured.young_readers.each {...}
41
+ #
42
+ # === Trigger Other Rules
43
+ # You can let one rule trigger another rule.
44
+ # Let say you have readers of some magazine and want to know if the magazine has old or young readers.
45
+ # So when a reader change from young to old you want to trigger all the magazine that he reads (a but stupid example)
46
+ #
47
+ # Example
48
+ # class Reader
49
+ # include Neo4j::NodeMixin
50
+ # property :age
51
+ # rule(:young, :triggers => :readers) { age < 15 }
52
+ # end
53
+ #
54
+ # class NewsStory
55
+ # include Neo4j::NodeMixin
56
+ # has_n :readers
57
+ # rule(:young_readers) { !readers.find{|user| !user.young?}}
58
+ # end
59
+ #
60
+ # === Performance Considerations
61
+ # If you have many rules and many updates this can be a bit slow.
62
+ # In order to speed it up somewhat you can use the raw java node object instead by providing an argument in your block.
63
+ #
64
+ # Example:
65
+ #
66
+ # class Person
67
+ # include Neo4j::NodeMixin
68
+ # property :age
69
+ # rule(:old) {|node| node[:age] > 10 }
70
+ # end
71
+ #
72
+ # === Thread Safe ?
73
+ # Yes, since operations are performed in an transaction. However you may get a deadlock exception:
74
+ # @see http://docs.neo4j.org/chunked/stable/transactions-deadlocks.html Transaction Deadlock
75
+ #
76
+ module ClassMethods
77
+
78
+ # Creates an rule node attached to the Neo4j.ref_node
79
+ # Can be used to rule all instances of a specific Ruby class.
80
+ #
81
+ # Example of usage:
82
+ # class Person
83
+ # include Neo4j::NodeMixin
84
+ # property :age
85
+ # rule :all
86
+ # rule :young { self[:age] < 10 }
87
+ # rule(:old, :functions => [Sum.new[:age]) { age > 20 }
88
+ # end
89
+ #
90
+ # p1 = Person.new :age => 5
91
+ # p2 = Person.new :age => 7
92
+ # p3 = Person.new :age => 12
93
+ # Neo4j::Transaction.finish
94
+ # Person.all # => [p1,p2,p3]
95
+ # Person.young # => [p1,p2]
96
+ # p1.young? # => true
97
+ # p1.sum(old, :age) # the some of the old people's age
98
+ #
99
+ def rule(rule_name, props = {}, &block)
100
+ singleton = class << self;
101
+ self;
102
+ end
103
+
104
+ # define class methods
105
+ singleton.send(:define_method, rule_name) do
106
+ rule_node = Rule.rule_node_for(self)
107
+ rule_node.traversal(rule_name)
108
+ end unless respond_to?(rule_name)
109
+
110
+ # define instance methods
111
+ self.send(:define_method, "#{rule_name}?") do
112
+ instance_eval &block
113
+ end
114
+
115
+ rule = Rule.add(self, rule_name, props, &block)
116
+
117
+ rule.functions && rule.functions.each do |func|
118
+ singleton.send(:define_method, func.class.function_name) do |r_name, *args|
119
+ rule_node = Rule.rule_node_for(self)
120
+ function_id = args.empty? ? "_classname" : args[0]
121
+ function = rule_node.find_function(r_name, func.class.function_name, function_id)
122
+ function.value(rule_node.rule_node, r_name)
123
+ end unless respond_to?(func.class.function_name)
124
+ end
125
+ end
126
+
127
+ def ref_node_for_class
128
+ Neo4j.ref_node #The reference node for a type falls back to the threadlocal ref node by default.
129
+ end
130
+
131
+ # Assigns the reference node for a class via a supplied block.
132
+ # Example of usage:
133
+ # class Person
134
+ # include Neo4j::NodeMixin
135
+ # ref_node { Neo4j.default_ref_node }
136
+ # end
137
+ #
138
+ def ref_node(&block)
139
+ singleton = class << self;
140
+ self;
141
+ end
142
+ singleton.send(:define_method, :ref_node_for_class) { block.call }
143
+ end
144
+
145
+ def inherit_rules_from(clazz)
146
+ Rule.inherit(clazz, self)
147
+ end
148
+
149
+ # This is typically used for RSpecs to clean up rule nodes created by the #rule method.
150
+ # It also remove all the rule class methods.
151
+ def delete_rules
152
+ singelton = class << self;
153
+ self;
154
+ end
155
+ rule_node = Rule.rule_node_for(self)
156
+
157
+ rule_node.rule_names.each { |rule_name| singelton.send(:remove_method, rule_name) }
158
+ rule_node.rules.clear
159
+ end
160
+
161
+ # Force to trigger the rules.
162
+ # You don't normally need that since it will be done automatically.
163
+ # This can be useful if you need to trigger rules on already existing nodes in the database.
164
+ # Can also be called from an migration.
165
+ #
166
+ def trigger_rules(node, *changes)
167
+ Rule.trigger_rules(node, *changes)
168
+ end
169
+
170
+ # Returns a proc that will call add method on the given function
171
+ # Can be used in migration to trigger rules on already existing nodes.
172
+ # Parameter function_id is default to '_classname' which means that
173
+ # the function 'reacts' on changes on the property '_classname'.
174
+ # That property changes only when a node is created or deleted.
175
+ # Function using function_id '_classname' is typically used for counting number of nodes of a class.
176
+ def add_function_for(rule_name, function_name_or_class, function_id = '_classname')
177
+ function_for(:add, rule_name, function_name_or_class, function_id)
178
+ end
179
+
180
+ # See #add_function_for
181
+ # Calls the delete method on the function.
182
+ def delete_function_for(rule_name, function_name_or_class, function_id = '_classname')
183
+ function_for(:delete, rule_name, function_name_or_class, function_id)
184
+ end
185
+
186
+ # Returns a proc that calls the given method on the given function.
187
+ def function_for(method, rule_name, function_name_or_class, function_id = '_classname')
188
+ function_name = function_name_or_class.is_a?(Symbol) ? function_name_or_class : function_name_or_class.function_name
189
+ rule_node = Rule.rule_node_for(self)
190
+ rule = rule_node.find_rule(rule_name)
191
+ rule_node_raw = rule_node.rule_node
192
+
193
+ function = rule_node.find_function(rule_name, function_name, function_id)
194
+ lambda do |node|
195
+ new_value = node[function_id]
196
+ function.send(method, rule.rule_name, rule_node_raw, new_value)
197
+ end
198
+ end
199
+ end
200
+
201
+ end
202
+ end
203
+
204
+ end
@@ -0,0 +1,66 @@
1
+ module Neo4j
2
+ module Wrapper
3
+ module Rule
4
+ class EventListener
5
+ class << self
6
+ # ----------------------------------------------------------------------------------------------------------------
7
+ # Event handling methods
8
+ # ----------------------------------------------------------------------------------------------------------------
9
+
10
+ def on_relationship_created(rel, *)
11
+ Rule.trigger_rules(rel._start_node) if Rule.trigger?(rel._start_node)
12
+ Rule.trigger_rules(rel._end_node) if Rule.trigger?(rel._end_node)
13
+ end
14
+
15
+ def on_property_changed(node, *changes)
16
+ Rule.trigger_rules(node, *changes) if Rule.trigger?(node)
17
+ end
18
+
19
+ def on_node_deleted(node, old_properties, deleted_relationship_set, deleted_identity_map)
20
+ # have we deleted a rule node ?
21
+ del_rule_node = Rule.find_rule_node(node)
22
+ del_rule_node && del_rule_node.clear_rule_node
23
+ return if del_rule_node
24
+
25
+ # do we have prop_aggregations for this
26
+ clazz = old_properties['_classname']
27
+ rule_node = Rule.rule_node_for(clazz)
28
+ return if rule_node.nil?
29
+
30
+ id = node.neo_id
31
+ rule_node.rules.each do |rule|
32
+ next if rule.functions.nil? || rule.bulk_update?
33
+ rule_name = rule.rule_name.to_s
34
+
35
+ # is the rule node deleted ?
36
+ deleted_rule_node = deleted_identity_map.get(rule_node.rule_node.neo_id)
37
+ next if deleted_rule_node
38
+
39
+ rule.functions.each do |function|
40
+ next unless deleted_relationship_set.contains?(id, rule_name)
41
+ previous_value = old_properties[function.function_id]
42
+ function.delete(rule_name, rule_node.rule_node, previous_value) if previous_value
43
+ end if rule.functions
44
+ end
45
+ end
46
+
47
+ def classes_changed(changed_class_map)
48
+ changed_class_map.each_pair do |clazz, class_change|
49
+ Rule.bulk_trigger_rules(clazz, class_change, changed_class_map)
50
+ end
51
+ end
52
+
53
+ def on_neo4j_started(db)
54
+ if not Neo4j::Config[:enable_rules]
55
+ db.event_handler.remove(self)
56
+ end
57
+ end
58
+ end
59
+
60
+
61
+ end
62
+ Neo4j.unstarted_db.event_handler.add(EventListener) unless Neo4j.read_only?
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,45 @@
1
+ module Neo4j
2
+ module Wrapper
3
+ module Rule
4
+ module Functions
5
+
6
+ # A function for counting number of nodes of a given class.
7
+ class Count < Function
8
+ def initialize
9
+ @property = '_classname'
10
+ end
11
+
12
+ def calculate?(changed_property)
13
+ true
14
+ end
15
+
16
+ def delete(rule_name, rule_node, _)
17
+ key = rule_node_property(rule_name)
18
+ rule_node[key] ||= 0
19
+ rule_node[key] -= 1
20
+ end
21
+
22
+ def add(rule_name, rule_node, _)
23
+ key = rule_node_property(rule_name)
24
+ rule_node[key] ||= 0
25
+ rule_node[key] += 1
26
+ end
27
+
28
+ def update(*)
29
+ # we are only counting, not interested in property changes
30
+ end
31
+
32
+ def classes_changed(rule_name, rule_node, class_change)
33
+ key = rule_node_property(rule_name)
34
+ rule_node[key] ||= 0
35
+ rule_node[key] += class_change.net_change
36
+ end
37
+
38
+ def self.function_name
39
+ :count
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,76 @@
1
+ module Neo4j
2
+ module Wrapper
3
+ module Rule
4
+ module Functions
5
+
6
+ # The base class of rule functions.
7
+ #
8
+ # You are expected to at least implement two methods:
9
+ # * update :: update the rule node value of this function
10
+ # * function_name :: the name of this function, the name of the generated method - A class method !
11
+ #
12
+ class Function
13
+
14
+ # Initialize the the function with a property which is usually the same as the function identity.
15
+ # See the #calculate? method how this property is used.
16
+ #
17
+ def initialize(property)
18
+ @property = property.to_s
19
+ end
20
+
21
+ def to_s
22
+ "Function #{self.class.function_name} function_id: #{function_id}"
23
+ end
24
+
25
+ # Decides if the function should be called are not
26
+ #
27
+ def calculate?(changed_property)
28
+ @property == changed_property
29
+ end
30
+
31
+
32
+ # The identity of the function.
33
+ # Used to identify function.
34
+ #
35
+ # ==== Example
36
+ # Person.sum(:young, :age)
37
+ #
38
+ # In the example above the property :age is the used to identify which function will be called
39
+ # since there could be several sum method. In the example we want use the sum method that uses the :age property.
40
+ #
41
+ def function_id
42
+ @property
43
+ end
44
+
45
+ # The value of the rule
46
+ def value(rule_node, rule_name)
47
+ key = rule_node_property(rule_name)
48
+ rule_node[key] || 0
49
+ end
50
+
51
+ # Called when a node is removed from a rule group
52
+ # Default is calling update method which is expected to be implemented in a subclass
53
+ def delete(rule_name, rule_node, old_value)
54
+ update(rule_name, rule_node, old_value, nil)
55
+ end
56
+
57
+ # Called when a node is added to a rule group
58
+ # Default is calling update method which is expected to be implemented in a subclass
59
+ def add(rule_name, rule_node, new_value)
60
+ update(rule_name, rule_node, nil, new_value)
61
+ end
62
+
63
+ # the name of the property that holds the value of the function
64
+ def rule_node_property(rule_name)
65
+ self.class.rule_node_property(self.class.function_name, rule_name, @property)
66
+ end
67
+
68
+ def self.rule_node_property(function_name, rule_name, prop)
69
+ "_#{function_name}_#{rule_name}_#{prop}"
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,31 @@
1
+ module Neo4j
2
+ module Wrapper
3
+ module Rule
4
+ module Functions
5
+
6
+ class Sum < Function
7
+ # Updates the function's value.
8
+ # Called after the transactions commits and a property has been changed on a node.
9
+ #
10
+ # ==== Arguments
11
+ # * rule_name :: the name of the rule group
12
+ # * rule_node :: the node which contains the value of this function
13
+ # * old_value new value :: the changed value of the property (when the transaction commits)
14
+ def update(rule_name, rule_node, old_value, new_value)
15
+ key = rule_node_property(rule_name)
16
+ rule_node[key] ||= 0
17
+ old_value ||= 0
18
+ new_value ||= 0
19
+ rule_node[key] += new_value - old_value
20
+ end
21
+
22
+ def self.function_name
23
+ :sum
24
+ end
25
+ end
26
+
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ module Neo4j
2
+ module Wrapper
3
+ module Rule
4
+ module InstanceMethods
5
+ # Trigger rules.
6
+ # You don't normally need to call this method (except in Migration) since
7
+ # it will be triggered automatically by the Neo4j::Wrapper::Rule::Rule
8
+ # @see Neo4j::Wrapper::Rule::ClassMethods
9
+ def trigger_rules
10
+ self.class.trigger_rules(self)
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,29 @@
1
+ module Neo4j
2
+ module Core
3
+ module Traversal
4
+ # Extends the Neo4j::Core Traverser in order to add rule traversal methods.
5
+ class Traverser
6
+ def filter_method(name, &proc)
7
+ # add method name
8
+ singelton = class << self;
9
+ self;
10
+ end
11
+ singelton.send(:define_method, name) { filter &proc }
12
+ self
13
+ end
14
+
15
+ def functions_method(func, rule_node, rule_name)
16
+ singelton = class << self;
17
+ self;
18
+ end
19
+ singelton.send(:define_method, func.class.function_name) do |*args|
20
+ function_id = args.empty? ? "_classname" : args[0]
21
+ function = rule_node.find_function(rule_name, func.class.function_name, function_id)
22
+ function.value(rule_node.rule_node, rule_name)
23
+ end
24
+ self
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,134 @@
1
+ module Neo4j
2
+ module Wrapper
3
+ module Rule
4
+
5
+
6
+ # Holds all defined rules added by the Neo4j::Rule::ClassMethods#rule method.
7
+ #
8
+ # @see Neo4j::Wrapper::Rule::ClassMethods
9
+ class Rule
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
+ @bulk_update = !@functions.nil? && @functions.size == 1 && @functions.first.class.function_name == :count && @filter.nil?
21
+ end
22
+
23
+ def to_s
24
+ "Rule #{rule_name} props=#{props.inspect}"
25
+ end
26
+
27
+ def find_function(function_name, function_id)
28
+ function_id = function_id.to_s
29
+ @functions && @functions.find { |f| f.function_id == function_id && f.class.function_name == function_name }
30
+ end
31
+
32
+ # Reconstruct the properties given when created this rule
33
+ # Needed when inheriting a rule and we want to duplicate a rule
34
+ def props
35
+ props = {}
36
+ props[:triggers] = @triggers if @triggers
37
+ props[:functions] = @functions if @functions
38
+ props
39
+ end
40
+
41
+ def functions_for(property)
42
+ @functions && @functions.find_all { |f| f.calculate?(property) }
43
+ end
44
+
45
+ def execute_filter(node)
46
+ if @filter.nil?
47
+ true
48
+ elsif @filter.arity != 1
49
+ node.wrapper.instance_eval(&@filter)
50
+ else
51
+ @filter.call(node)
52
+ end
53
+ end
54
+
55
+ def bulk_update?
56
+ @bulk_update
57
+ end
58
+
59
+ # ------------------------------------------------------------------------------------------------------------------
60
+ # Class Methods
61
+ # ------------------------------------------------------------------------------------------------------------------
62
+
63
+ @rule_nodes = {}
64
+
65
+ class << self
66
+
67
+ def add(clazz, rule_name, props, &block)
68
+ rule_node = rule_node_for(clazz.to_s)
69
+ rule_node.remove_rule(rule_name) # remove any previously inherited rules
70
+ rule = Rule.new(rule_name, props, &block)
71
+ rule_node.add_rule(rule)
72
+ rule
73
+ end
74
+
75
+ def rule_names_for(clazz)
76
+ rule_node = rule_node_for(clazz)
77
+ rule_node.rules.map { |rule| rule.rule_name }
78
+ end
79
+
80
+ def rule_node_for(clazz)
81
+ return nil if clazz.nil?
82
+ @rule_nodes[clazz.to_s] ||= RuleNode.new(clazz)
83
+ end
84
+
85
+ def find_rule_node(node)
86
+ @rule_nodes && @rule_nodes.values.find { |rn| rn.rule_node?(node) }
87
+ end
88
+
89
+ def inherit(parent_class, subclass)
90
+ # copy all the rules
91
+ if rule_node = rule_node_for(parent_class)
92
+ rule_node.inherit(subclass)
93
+ end
94
+ end
95
+
96
+ def trigger?(node)
97
+ classname = node[:_classname]
98
+ @rule_nodes && classname && rule_node_for(classname) && !rule_node_for(classname).bulk_update?
99
+ end
100
+
101
+ def trigger_rules(node, *changes)
102
+ classname = node[:_classname]
103
+ return unless classname # there are no rules if there is not a :_classname property
104
+ rule_node = rule_node_for(classname)
105
+ rule_node.execute_rules(node, *changes)
106
+
107
+ # recursively add relationships for all the parent classes with rules that also pass for this node
108
+ recursive(node, rule_node.model_class, *changes)
109
+ end
110
+
111
+ def bulk_trigger_rules(classname, class_change, map)
112
+ rule_node = rule_node_for(classname)
113
+ rule_node.classes_changed(class_change)
114
+ if (clazz = rule_node.model_class.superclass) && clazz.include?(Neo4j::NodeMixin)
115
+ bulk_trigger_rules(clazz.name, class_change, map) if clazz.to_s != "Neo4j::Rails::Model"
116
+ end
117
+ end
118
+
119
+ private
120
+
121
+ def recursive(node, model_class, *changes)
122
+ if (clazz = model_class.superclass) && clazz.include?(Neo4j::NodeMixin)
123
+ if clazz.to_s != "Neo4j::Rails::Model"
124
+ rule_node = rule_node_for(clazz)
125
+ rule_node && rule_node.execute_rules(node, *changes)
126
+ recursive(node, clazz, *changes)
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end