neo4j 1.0.0.beta.17 → 1.0.0.beta.18
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/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
|