neo4j 1.0.0.beta.17 → 1.0.0.beta.18
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -2
- data/README.rdoc +2 -2
- data/lib/generators/neo4j.rb +65 -0
- data/lib/generators/neo4j/model/model_generator.rb +39 -0
- data/lib/generators/neo4j/model/templates/model.rb +7 -0
- data/lib/neo4j.rb +2 -2
- data/lib/neo4j/config.rb +14 -12
- data/lib/neo4j/database.rb +1 -1
- data/lib/neo4j/equal.rb +3 -0
- data/lib/neo4j/event_handler.rb +25 -14
- data/lib/neo4j/index/class_methods.rb +3 -2
- data/lib/neo4j/index/indexer.rb +121 -21
- data/lib/neo4j/index/lucene_query.rb +156 -0
- data/lib/neo4j/load.rb +3 -2
- data/lib/neo4j/mapping/class_methods/property.rb +16 -0
- data/lib/neo4j/mapping/class_methods/relationship.rb +3 -1
- data/lib/neo4j/mapping/class_methods/root.rb +3 -0
- data/lib/neo4j/mapping/class_methods/rule.rb +141 -66
- data/lib/neo4j/mapping/decl_relationship_dsl.rb +8 -8
- data/lib/neo4j/mapping/has_n.rb +11 -1
- data/lib/neo4j/mapping/node_mixin.rb +20 -0
- data/lib/neo4j/neo4j.rb +24 -5
- data/lib/neo4j/node.rb +1 -1
- data/lib/neo4j/property.rb +1 -1
- data/lib/neo4j/rails/model.rb +40 -36
- data/lib/neo4j/rails/properties.rb +29 -0
- data/lib/neo4j/rails/value.rb +8 -29
- data/lib/neo4j/transaction.rb +0 -1
- data/lib/neo4j/version.rb +1 -1
- metadata +8 -5
- data/lib/neo4j/index/wrapped_query.rb +0 -59
- data/lib/neo4j/mapping/class_methods/index.rb +0 -17
@@ -0,0 +1,156 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Index
|
3
|
+
# == LuceneQuery
|
4
|
+
#
|
5
|
+
# This object is returned when you call the #find method on the Node, Relationship.
|
6
|
+
# The actual query is not executed until the first item is requested.
|
7
|
+
#
|
8
|
+
# You can perform a query in many different ways:
|
9
|
+
#
|
10
|
+
# ==== By Hash
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# Person.find(:name => 'foo', :age => 3)
|
14
|
+
#
|
15
|
+
# ==== By Range
|
16
|
+
#
|
17
|
+
# Example:
|
18
|
+
# Person.find(:age).between(15,35)
|
19
|
+
#
|
20
|
+
# ==== By Lucene Query Syntax
|
21
|
+
#
|
22
|
+
# Example
|
23
|
+
# Car.find('wheels:"4" AND colour: "blue")
|
24
|
+
#
|
25
|
+
# For more information about the syntax see http://lucene.apache.org/java/3_0_2/queryparsersyntax.html
|
26
|
+
#
|
27
|
+
# ==== By Compound Queries
|
28
|
+
#
|
29
|
+
# You can combine several queries by <tt>AND</tt>ing those together.
|
30
|
+
#
|
31
|
+
# Example:
|
32
|
+
# Vehicle.find(:weight).between(5.0, 100000.0).and(:name).between('a', 'd')
|
33
|
+
#
|
34
|
+
# === See Also
|
35
|
+
# * Neo4j::Index::Indexer#index
|
36
|
+
# * Neo4j::Index::Indexer#find - which returns an LuceneQuery
|
37
|
+
#
|
38
|
+
class LuceneQuery
|
39
|
+
include Enumerable
|
40
|
+
attr_accessor :left_and_query, :left_or_query
|
41
|
+
|
42
|
+
def initialize(index, decl_props, query)
|
43
|
+
@index = index
|
44
|
+
@query = query
|
45
|
+
@decl_props = decl_props
|
46
|
+
end
|
47
|
+
|
48
|
+
def each
|
49
|
+
hits.each { |n| yield n.wrapper }
|
50
|
+
end
|
51
|
+
|
52
|
+
def close
|
53
|
+
@hits.close if @hits
|
54
|
+
end
|
55
|
+
|
56
|
+
def empty?
|
57
|
+
hits.size == 0
|
58
|
+
end
|
59
|
+
|
60
|
+
def [](index)
|
61
|
+
each_with_index {|node,i| break node if index == i}
|
62
|
+
end
|
63
|
+
|
64
|
+
def size
|
65
|
+
hits.size
|
66
|
+
end
|
67
|
+
|
68
|
+
def hits
|
69
|
+
@hits ||= perform_query
|
70
|
+
end
|
71
|
+
|
72
|
+
def between(lower, upper)
|
73
|
+
raise "Expected a symbol. Syntax for range queries example: index(:weight).between(a,b)" unless Symbol === @query
|
74
|
+
raise "Can't only do range queries on Neo4j::NodeMixin, Neo4j::Model, Neo4j::RelationshipMixin" unless @decl_props
|
75
|
+
type = @decl_props[@query] && @decl_props[@query][:type]
|
76
|
+
raise "Missing type declaration of property #{@query}. E.g. property :#{@query}, :type => Float; index :#{@query}" unless type
|
77
|
+
if type != String
|
78
|
+
raise "find(#{@query}).between(#{lower}, #{upper}) to allowed since #{lower} is not a Float or Fixnum" if lower === Float or lower === Fixnum
|
79
|
+
raise "find(#{@query}).between(#{lower}, #{upper}) to allowed since #{upper} is not a Float or Fixnum" if upper === Float or upper === Fixnum
|
80
|
+
@query = org.apache.lucene.search.NumericRangeQuery.new_double_range(@query.to_s, lower, upper, false, false)
|
81
|
+
else
|
82
|
+
raise "find(#{@query}).between(#{lower}, #{upper}) to allowed since #{lower} is not a String" if lower === String
|
83
|
+
raise "find(#{@query}).between(#{lower}, #{upper}) to allowed since #{upper} is not a String" if upper === String
|
84
|
+
@query = org.apache.lucene.search.TermRangeQuery.new(@query.to_s, lower, upper, false, false)
|
85
|
+
end
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
def and(query2)
|
90
|
+
new_query = LuceneQuery.new(@index, @decl_props, query2)
|
91
|
+
new_query.left_and_query = self
|
92
|
+
new_query
|
93
|
+
end
|
94
|
+
|
95
|
+
def desc(*fields)
|
96
|
+
@order = fields.inject(@order || {}) { |memo, field| memo[field] = true; memo }
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def asc(*fields)
|
101
|
+
@order = fields.inject(@order || {}) { |memo, field| memo[field] = false; memo }
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
def build_and_query(query)
|
106
|
+
left_query = @left_and_query.build_query
|
107
|
+
and_query = org.apache.lucene.search.BooleanQuery.new
|
108
|
+
and_query.add(left_query, org.apache.lucene.search.BooleanClause::Occur::MUST)
|
109
|
+
and_query.add(query, org.apache.lucene.search.BooleanClause::Occur::MUST)
|
110
|
+
and_query
|
111
|
+
end
|
112
|
+
|
113
|
+
def build_sort_query(query)
|
114
|
+
java_sort_fields = @order.keys.inject([]) do |memo, field|
|
115
|
+
decl_type = @decl_props && @decl_props[field] && @decl_props[field][:type]
|
116
|
+
type = case
|
117
|
+
when Float == decl_type
|
118
|
+
org.apache.lucene.search.SortField::DOUBLE
|
119
|
+
when Fixnum == decl_type
|
120
|
+
org.apache.lucene.search.SortField::LONG
|
121
|
+
else
|
122
|
+
org.apache.lucene.search.SortField::STRING
|
123
|
+
end
|
124
|
+
memo << org.apache.lucene.search.SortField.new(field.to_s, type, @order[field])
|
125
|
+
end
|
126
|
+
sort = org.apache.lucene.search.Sort.new(*java_sort_fields)
|
127
|
+
org.neo4j.index.impl.lucene.QueryContext.new(query).sort(sort)
|
128
|
+
end
|
129
|
+
|
130
|
+
def build_hash_query(query)
|
131
|
+
and_query = org.apache.lucene.search.BooleanQuery.new
|
132
|
+
|
133
|
+
query.each_pair do |key, value|
|
134
|
+
raise "Only String values valid in find(hash) got :#{key} => #{value} which is not a String" if !value.is_a?(String) && @decl_props[key] && @decl_props[key][:type] != String
|
135
|
+
term = org.apache.lucene.index.Term.new(key.to_s, value.to_s)
|
136
|
+
term_query = org.apache.lucene.search.TermQuery.new(term)
|
137
|
+
and_query.add(term_query, org.apache.lucene.search.BooleanClause::Occur::MUST)
|
138
|
+
end
|
139
|
+
and_query
|
140
|
+
end
|
141
|
+
|
142
|
+
def build_query
|
143
|
+
query = @query
|
144
|
+
query = build_hash_query(query) if Hash === query
|
145
|
+
query = build_and_query(query) if @left_and_query
|
146
|
+
query = build_sort_query(query) if @order
|
147
|
+
query
|
148
|
+
end
|
149
|
+
|
150
|
+
def perform_query
|
151
|
+
@index.query(build_query)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
data/lib/neo4j/load.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Neo4j
|
2
2
|
|
3
|
-
#
|
3
|
+
# === Mixin responsible for loading Ruby wrappers for Neo4j Nodes and Relationship.
|
4
|
+
#
|
4
5
|
module Load
|
5
6
|
def wrapper(node) # :nodoc:
|
6
7
|
return node unless node.property?(:_classname)
|
@@ -11,7 +12,7 @@ module Neo4j
|
|
11
12
|
class_name.split("::").inject(Kernel) {|container, name| container.const_get(name.to_s) }
|
12
13
|
end
|
13
14
|
|
14
|
-
# Checks if the given node or
|
15
|
+
# Checks if the given entity (node/relationship) or entity id (#neo_id) exists in the database.
|
15
16
|
def exist?(node_or_node_id, db = Neo4j.started_db)
|
16
17
|
id = node_or_node_id.kind_of?(Fixnum) ? node_or_node_id : node_or_node_id.id
|
17
18
|
_load(id, db) != nil
|
@@ -6,7 +6,23 @@ module Neo4j::Mapping
|
|
6
6
|
# The generated accessor is a simple wrapper around the #[] and
|
7
7
|
# #[]= operators.
|
8
8
|
#
|
9
|
+
# ==== Types
|
9
10
|
# If a property is set to nil the property will be removed.
|
11
|
+
# A property can be of any primitive type (Boolean, String, Fixnum, Float) and does not
|
12
|
+
# even have to be the same.
|
13
|
+
#
|
14
|
+
# Example:
|
15
|
+
# class Foo
|
16
|
+
# include Neo4j::NodeMixin
|
17
|
+
# property :age
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# Example:
|
21
|
+
# foo = Foo.new
|
22
|
+
# foo.age = "hej" # first set it to string
|
23
|
+
# foo.age = 42 # change it to a Fixnum
|
24
|
+
#
|
25
|
+
# However, you can specify an type for the index, see Neo4j::Index::Indexer#index
|
10
26
|
#
|
11
27
|
# ==== Example
|
12
28
|
# class Baaz; end
|
@@ -6,6 +6,7 @@ module Neo4j::Mapping
|
|
6
6
|
|
7
7
|
# Specifies a relationship between two node classes.
|
8
8
|
# Generates assignment and accessor methods for the given relationship.
|
9
|
+
# Both incoming and outgoing relationships can be declared, see Neo4j::Mapping::DeclRelationshipDsl
|
9
10
|
#
|
10
11
|
# ==== Example
|
11
12
|
#
|
@@ -43,6 +44,7 @@ module Neo4j::Mapping
|
|
43
44
|
# Specifies a relationship between two node classes.
|
44
45
|
# Generates assignment and accessor methods for the given relationship
|
45
46
|
# Old relationship is deleted when a new relationship is assigned.
|
47
|
+
# Both incoming and outgoing relationships can be declared, see Neo4j::Mapping::DeclRelationshipDsl
|
46
48
|
#
|
47
49
|
# ==== Example
|
48
50
|
#
|
@@ -58,7 +60,7 @@ module Neo4j::Mapping
|
|
58
60
|
#
|
59
61
|
# ==== Returns
|
60
62
|
#
|
61
|
-
# Neo4j::
|
63
|
+
# Neo4j::Mapping::DeclRelationshipDsl
|
62
64
|
#
|
63
65
|
def has_one(rel_type, params = {})
|
64
66
|
clazz = self
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Neo4j::Mapping
|
2
2
|
module ClassMethods
|
3
|
+
# Used to hold information about which relationships and properties has been declared.
|
3
4
|
module Root
|
4
5
|
#attr_reader :_decl_rels, :_decl_props
|
5
6
|
|
@@ -13,11 +14,13 @@ module Neo4j::Mapping
|
|
13
14
|
end
|
14
15
|
|
15
16
|
|
17
|
+
# a hash of all relationships which has been declared with a has_n or has_one using Neo4j::Mapping::ClassMethods::Relationship
|
16
18
|
def _decl_rels
|
17
19
|
@@_all_decl_rels[self] ||= {}
|
18
20
|
@_decl_props = @@_all_decl_rels[self]
|
19
21
|
end
|
20
22
|
|
23
|
+
# a hash of all properties which has been declared with <tt>property</tt> using the Neo4j::Mapping::ClassMethods::Property
|
21
24
|
def _decl_props
|
22
25
|
@@_all_decl_props[self] ||= {}
|
23
26
|
@_decl_props = @@_all_decl_props[self]
|
@@ -1,39 +1,43 @@
|
|
1
1
|
module Neo4j::Mapping
|
2
2
|
module ClassMethods
|
3
|
+
# Holds all defined rules and trigger them when an event is received.
|
4
|
+
#
|
5
|
+
# See Rule
|
6
|
+
#
|
3
7
|
class Rules
|
4
8
|
class << self
|
5
9
|
def add(clazz, field, props, &block)
|
6
|
-
clazz
|
7
|
-
@rules
|
10
|
+
clazz = clazz.to_s
|
11
|
+
@rules ||= {}
|
8
12
|
# was there no ruls for this class AND is neo4j running ?
|
9
13
|
if !@rules.include?(clazz) && Neo4j.running?
|
10
14
|
# maybe Neo4j was started first and the rules was added later. Create rule nodes now
|
11
15
|
create_rule_node_for(clazz)
|
12
16
|
end
|
13
|
-
@rules[clazz]
|
14
|
-
filter
|
15
|
-
@rules[clazz][field]
|
16
|
-
@triggers
|
17
|
-
@triggers[clazz]
|
18
|
-
trigger
|
17
|
+
@rules[clazz] ||= {}
|
18
|
+
filter = block.nil? ? Proc.new { |*| true } : block
|
19
|
+
@rules[clazz][field] = filter
|
20
|
+
@triggers ||= {}
|
21
|
+
@triggers[clazz] ||= {}
|
22
|
+
trigger = props[:trigger].nil? ? [] : props[:trigger]
|
19
23
|
@triggers[clazz][field] = trigger.respond_to?(:each) ? trigger : [trigger]
|
20
24
|
end
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
|
26
|
+
def inherit(parent_class, subclass)
|
27
|
+
# copy all the rules
|
28
|
+
@rules[parent_class.to_s].each_pair do |field, filter|
|
29
|
+
subclass.rule field, &filter
|
30
|
+
end if @rules[parent_class.to_s]
|
31
|
+
end
|
28
32
|
|
29
33
|
def trigger_other_rules(node)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
clazz = node[:_classname]
|
35
|
+
@rules[clazz].keys.each do |field|
|
36
|
+
rel_types = @triggers[clazz][field]
|
37
|
+
rel_types.each do |rel_type|
|
38
|
+
node.incoming(rel_type).each { |n| n.trigger_rules }
|
39
|
+
end
|
40
|
+
end
|
37
41
|
end
|
38
42
|
|
39
43
|
def fields_for(clazz)
|
@@ -70,17 +74,17 @@ module Neo4j::Mapping
|
|
70
74
|
end
|
71
75
|
|
72
76
|
def rule_for(clazz)
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
77
|
+
if Neo4j.ref_node.rel?(clazz)
|
78
|
+
Neo4j.ref_node._rel(:outgoing, clazz)._end_node
|
79
|
+
else
|
80
|
+
# this should be called if the rule node gets deleted
|
81
|
+
create_rule_node_for(clazz)
|
82
|
+
end
|
79
83
|
end
|
80
84
|
|
81
85
|
|
82
86
|
def on_relationship_created(rel, *)
|
83
|
-
|
87
|
+
trigger_start_node = trigger?(rel._start_node)
|
84
88
|
trigger_end_node = trigger?(rel._end_node)
|
85
89
|
# end or start node must be triggered by this event
|
86
90
|
return unless trigger_start_node || trigger_end_node
|
@@ -89,52 +93,52 @@ module Neo4j::Mapping
|
|
89
93
|
|
90
94
|
|
91
95
|
def on_property_changed(node, *)
|
92
|
-
|
96
|
+
trigger_rules(node) if trigger?(node)
|
93
97
|
end
|
94
98
|
|
95
99
|
def trigger_rules(node)
|
96
100
|
trigger_rules_for_class(node, node[:_classname])
|
97
|
-
|
101
|
+
trigger_other_rules(node)
|
98
102
|
end
|
99
|
-
|
100
|
-
|
101
|
-
|
103
|
+
|
104
|
+
def trigger_rules_for_class(node, clazz)
|
105
|
+
return if @rules[clazz].nil?
|
102
106
|
|
103
107
|
agg_node = rule_for(clazz)
|
104
108
|
@rules[clazz].each_pair do |field, rule|
|
105
109
|
if run_rule(rule, node)
|
106
110
|
# is this node already included ?
|
107
|
-
|
111
|
+
unless connected?(field, agg_node, node)
|
108
112
|
agg_node.outgoing(field) << node
|
109
113
|
end
|
110
114
|
else
|
111
115
|
# remove old ?
|
112
|
-
|
116
|
+
break_connection(field, agg_node, node)
|
113
117
|
end
|
114
118
|
end
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
119
|
+
|
120
|
+
# recursively add relationships for all the parent classes with rules that also pass for this node
|
121
|
+
if clazz = eval("#{clazz}.superclass")
|
122
|
+
trigger_rules_for_class(node, clazz.to_s)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# work out if two nodes are connected by a particular relationship
|
127
|
+
# uses the end_node to start with because it's more likely to have less relationships to go through
|
128
|
+
# (just the number of superclasses it has really)
|
129
|
+
def connected?(relationship, start_node, end_node)
|
130
|
+
end_node.incoming(relationship).each do |n|
|
131
|
+
return true if n == start_node
|
132
|
+
end
|
133
|
+
false
|
134
|
+
end
|
135
|
+
|
136
|
+
# sever a direct one-to-one relationship if it exists
|
137
|
+
def break_connection(relationship, start_node, end_node)
|
138
|
+
end_node.rels(relationship).incoming.each do |r|
|
139
|
+
return r.del if r.start_node == start_node
|
140
|
+
end
|
141
|
+
end
|
138
142
|
|
139
143
|
def run_rule(rule, node)
|
140
144
|
if rule.arity != 1
|
@@ -147,6 +151,75 @@ module Neo4j::Mapping
|
|
147
151
|
end
|
148
152
|
|
149
153
|
|
154
|
+
# Allows you to group nodes by providing a rule.
|
155
|
+
#
|
156
|
+
# === Example, finding all nodes of a certain class
|
157
|
+
# Just add a rule without a code block, then all nodes of that class will be grouped under the given key (<tt>all</tt>
|
158
|
+
# for the example below).
|
159
|
+
#
|
160
|
+
# class Person
|
161
|
+
# include Neo4j::NodeMixin
|
162
|
+
# rule :all
|
163
|
+
# end
|
164
|
+
#
|
165
|
+
# Then you can get all the nodes of type Person (and siblings) by
|
166
|
+
# Person.all.each {|x| ...}
|
167
|
+
#
|
168
|
+
# === Example, finding all nodes with a given condition on a property
|
169
|
+
#
|
170
|
+
# class Person
|
171
|
+
# include Neo4j::NodeMixin
|
172
|
+
# property :age
|
173
|
+
# rule(:old) { age > 10 }
|
174
|
+
# end
|
175
|
+
#
|
176
|
+
# Now we can find all nodes with a property <tt>age</tt> above 10.
|
177
|
+
#
|
178
|
+
# === Chain Rules
|
179
|
+
#
|
180
|
+
# class NewsStory
|
181
|
+
# include Neo4j::NodeMixin
|
182
|
+
# has_n :readers
|
183
|
+
# rule(:featured) { |node| node[:featured] == true }
|
184
|
+
# rule(:young_readers) { !readers.find{|user| !user.young?}}
|
185
|
+
# end
|
186
|
+
#
|
187
|
+
# You can combine two rules. Let say you want to find all stories which are featured and has young readers:
|
188
|
+
# NewsStory.featured.young_readers.each {...}
|
189
|
+
#
|
190
|
+
# === Trigger Other Rules
|
191
|
+
# You can let one rule trigger another rule.
|
192
|
+
# Let say you have readers of some magazine and want to know if the magazine has old or young readers.
|
193
|
+
# So when a reader change from young to old you want to trigger all the magazine that he reads (a but stupid example)
|
194
|
+
#
|
195
|
+
# Example
|
196
|
+
# class Reader
|
197
|
+
# include Neo4j::NodeMixin
|
198
|
+
# property :age
|
199
|
+
# rule(:young, :trigger => :readers) { age < 15 }
|
200
|
+
# end
|
201
|
+
#
|
202
|
+
# class NewsStory
|
203
|
+
# include Neo4j::NodeMixin
|
204
|
+
# has_n :readers
|
205
|
+
# rule(:young_readers) { !readers.find{|user| !user.young?}}
|
206
|
+
# end
|
207
|
+
#
|
208
|
+
# === Performance Considerations
|
209
|
+
# If you have many rules and many updates this can be a bit slow.
|
210
|
+
# In order to speed it up somewhat you can use the raw java node object instead by providing an argument in your block.
|
211
|
+
#
|
212
|
+
# Example:
|
213
|
+
#
|
214
|
+
# class Person
|
215
|
+
# include Neo4j::NodeMixin
|
216
|
+
# property :age
|
217
|
+
# rule(:old) {|node| node[:age] > 10 }
|
218
|
+
# end
|
219
|
+
#
|
220
|
+
# === Thread Safe ?
|
221
|
+
# Not sure...
|
222
|
+
#
|
150
223
|
module Rule
|
151
224
|
|
152
225
|
# Creates an rule node attached to the Neo4j.ref_node
|
@@ -168,13 +241,13 @@ module Neo4j::Mapping
|
|
168
241
|
# p1.young? # => true
|
169
242
|
#
|
170
243
|
def rule(name, props = {}, &block)
|
171
|
-
|
244
|
+
singelton = class << self;
|
172
245
|
self;
|
173
246
|
end
|
174
|
-
|
247
|
+
|
175
248
|
# define class methods
|
176
249
|
singelton.send(:define_method, name) do
|
177
|
-
agg_node
|
250
|
+
agg_node = Rules.rule_for(self)
|
178
251
|
raise "no rule node for #{name} on #{self}" if agg_node.nil?
|
179
252
|
traversal = agg_node.outgoing(name) # TODO possible to cache this object
|
180
253
|
Rules.fields_for(self).each do |filter_name|
|
@@ -188,15 +261,15 @@ module Neo4j::Mapping
|
|
188
261
|
# define instance methods
|
189
262
|
self.send(:define_method, "#{name}?") do
|
190
263
|
instance_eval &block
|
191
|
-
|
264
|
+
end
|
192
265
|
|
193
266
|
Rules.add(self, name, props, &block)
|
194
267
|
end
|
195
|
-
|
268
|
+
|
196
269
|
def inherit_rules_from(clazz)
|
197
|
-
|
270
|
+
Rules.inherit(clazz, self)
|
198
271
|
end
|
199
|
-
|
272
|
+
|
200
273
|
# This is typically used for RSpecs to clean up rule nodes created by the #rule method.
|
201
274
|
# It also remove the given class method.
|
202
275
|
def delete_rules
|
@@ -209,6 +282,8 @@ module Neo4j::Mapping
|
|
209
282
|
Rules.delete(self)
|
210
283
|
end
|
211
284
|
|
285
|
+
# Force to trigger the rules.
|
286
|
+
# You don't normally need that since it will be done automatically.
|
212
287
|
def trigger_rules(node)
|
213
288
|
Rules.trigger_rules(node)
|
214
289
|
end
|