active_node 2.2.3 → 2.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/active_node/graph.rb +30 -11
- data/lib/active_node/graph/query_methods.rb +199 -9
- data/lib/active_node/version.rb +1 -1
- data/spec/functional/graph/query_methods_spec.rb +21 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f099f7600aea945cfd5006b737360b8d5403909c
|
4
|
+
data.tar.gz: b2505371bcbc5ed36a5692fc6720aab6bff55985
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52822ad900508188e43d78788179768eb0dcde8cbbb4e543fc28c63cb1d259913dbbaeb0f50a3e6af29e4977fb063f5b8f4f57864d00778e2d0ea94cf981314a
|
7
|
+
data.tar.gz: aa4ca077908e7d79375059bfe776291bb4d3267d1b088e80515c6127ca8293e86c94c49945c55a39e216a40543ac5e22d9fae22da335c8b06d8447b19ca164d2
|
data/lib/active_node/graph.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
module ActiveNode
|
2
2
|
class Graph
|
3
|
+
MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
|
4
|
+
:order, :joins, :where, :having, :bind, :references,
|
5
|
+
:extending, :unscope]
|
6
|
+
|
7
|
+
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering,
|
8
|
+
:reverse_order, :distinct, :create_with, :uniq]
|
9
|
+
|
10
|
+
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
|
11
|
+
|
3
12
|
include Neography::Rest::Helpers
|
4
13
|
include FinderMethods, QueryMethods, Delegation
|
5
14
|
|
@@ -15,6 +24,7 @@ module ActiveNode
|
|
15
24
|
@loaded_assoc_cache = {}
|
16
25
|
@where = {}
|
17
26
|
@includes = includes
|
27
|
+
@values = {}
|
18
28
|
@offsets = {}
|
19
29
|
end
|
20
30
|
|
@@ -36,11 +46,6 @@ module ActiveNode
|
|
36
46
|
self
|
37
47
|
end
|
38
48
|
|
39
|
-
def limit count
|
40
|
-
@limit = count
|
41
|
-
self
|
42
|
-
end
|
43
|
-
|
44
49
|
def build *objects
|
45
50
|
find objects.map { |o| o.is_a?(ActiveNode::Base) ? o.id.tap { |id| @object_cache[id]=o } : extract_id(o) }
|
46
51
|
end
|
@@ -138,7 +143,11 @@ module ActiveNode
|
|
138
143
|
end
|
139
144
|
|
140
145
|
def limit_cond
|
141
|
-
"limit #{
|
146
|
+
"limit #{limit_value}" if limit_value
|
147
|
+
end
|
148
|
+
|
149
|
+
def skip_cond
|
150
|
+
"skip #{offset_value}" if offset_value
|
142
151
|
end
|
143
152
|
|
144
153
|
def initial_match
|
@@ -154,7 +163,7 @@ module ActiveNode
|
|
154
163
|
end
|
155
164
|
|
156
165
|
def to_cypher
|
157
|
-
[initial_match, conditions, "with n0", limit_cond, query, 'return', list_with_rel(@reflections.size),
|
166
|
+
[initial_match, conditions, "with n0", order_list, skip_cond, limit_cond, query, 'return', list_with_rel(@reflections.size), order_list_with_defaults].compact.join ' '
|
158
167
|
end
|
159
168
|
|
160
169
|
def parse_results results
|
@@ -229,15 +238,23 @@ module ActiveNode
|
|
229
238
|
end
|
230
239
|
|
231
240
|
def list_with_rel num
|
232
|
-
comma_sep_list(num) { |i| [("r#{i}" if i>0), "n#{i}"] }
|
241
|
+
comma_sep_list(0, num) { |i| [("r#{i}" if i>0), "n#{i}"] }
|
233
242
|
end
|
234
243
|
|
235
|
-
def comma_sep_list num, &block
|
244
|
+
def comma_sep_list start, num, &block
|
236
245
|
(0..num).map(&block).flatten.compact.join(', ')
|
237
246
|
end
|
238
247
|
|
239
|
-
def
|
240
|
-
comma_sep_list(
|
248
|
+
def order_list_with_defaults
|
249
|
+
"#{order_list}, #{comma_sep_list(1, @reflections.size) { |i| "n#{i}.created_at" }}"
|
250
|
+
end
|
251
|
+
|
252
|
+
def order_list
|
253
|
+
if order_values.empty?
|
254
|
+
order(:created_at) if @klass.respond_to? :created_at
|
255
|
+
order(:id)
|
256
|
+
end
|
257
|
+
"order by #{build_order(:n0)}"
|
241
258
|
end
|
242
259
|
|
243
260
|
def extract_id(id)
|
@@ -250,5 +267,7 @@ module ActiveNode
|
|
250
267
|
get_id(id).to_i
|
251
268
|
end
|
252
269
|
end
|
270
|
+
|
271
|
+
|
253
272
|
end
|
254
273
|
end
|
@@ -1,28 +1,218 @@
|
|
1
1
|
module ActiveNode
|
2
2
|
module QueryMethods
|
3
|
-
|
3
|
+
Graph::MULTI_VALUE_METHODS.each do |name|
|
4
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
5
|
+
def #{name}_values # def select_values
|
6
|
+
@values[:#{name}] || [] # @values[:select] || []
|
7
|
+
end # end
|
8
|
+
#
|
9
|
+
def #{name}_values=(values) # def select_values=(values)
|
10
|
+
raise ImmutableGraph if @loaded # raise ImmutableGraph if @loaded
|
11
|
+
@values[:#{name}] = values # @values[:select] = values
|
12
|
+
end # end
|
13
|
+
CODE
|
14
|
+
end
|
15
|
+
|
16
|
+
(Graph::SINGLE_VALUE_METHODS - [:create_with]).each do |name|
|
17
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
18
|
+
def #{name}_value # def readonly_value
|
19
|
+
@values[:#{name}] # @values[:readonly]
|
20
|
+
end # end
|
21
|
+
CODE
|
22
|
+
end
|
23
|
+
|
24
|
+
Graph::SINGLE_VALUE_METHODS.each do |name|
|
25
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
26
|
+
def #{name}_value=(value) # def readonly_value=(value)
|
27
|
+
raise ImmutableGraph if @loaded # raise ImmutableGraph if @loaded
|
28
|
+
@values[:#{name}] = value # @values[:readonly] = value
|
29
|
+
end # end
|
30
|
+
CODE
|
31
|
+
end
|
32
|
+
|
33
|
+
# Specifies a limit for the number of records to retrieve.
|
34
|
+
#
|
35
|
+
# User.limit(10) # generated SQL has 'LIMIT 10'
|
36
|
+
#
|
37
|
+
# User.limit(10).limit(20) # generated SQL has 'LIMIT 20'
|
38
|
+
def limit(value)
|
39
|
+
spawn.limit!(value)
|
40
|
+
end
|
41
|
+
|
42
|
+
def limit!(value) # :nodoc:
|
43
|
+
self.limit_value = value
|
4
44
|
self
|
5
45
|
end
|
6
46
|
|
47
|
+
# Specifies the number of rows to skip before returning rows.
|
48
|
+
#
|
49
|
+
# User.offset(10) # generated SQL has "OFFSET 10"
|
50
|
+
#
|
51
|
+
# Should be used with order.
|
52
|
+
#
|
53
|
+
# User.offset(10).order("name ASC")
|
7
54
|
def offset(value)
|
55
|
+
spawn.offset!(value)
|
56
|
+
end
|
57
|
+
|
58
|
+
def offset!(value) # :nodoc:
|
59
|
+
self.offset_value = value
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
# Allows to specify an order attribute:
|
65
|
+
#
|
66
|
+
# User.order('name')
|
67
|
+
# => SELECT "users".* FROM "users" ORDER BY name
|
68
|
+
#
|
69
|
+
# User.order('name DESC')
|
70
|
+
# => SELECT "users".* FROM "users" ORDER BY name DESC
|
71
|
+
#
|
72
|
+
# User.order('name DESC, email')
|
73
|
+
# => SELECT "users".* FROM "users" ORDER BY name DESC, email
|
74
|
+
#
|
75
|
+
# User.order(:name)
|
76
|
+
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
|
77
|
+
#
|
78
|
+
# User.order(email: :desc)
|
79
|
+
# => SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
|
80
|
+
#
|
81
|
+
# User.order(:name, email: :desc)
|
82
|
+
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
|
83
|
+
def order(*args)
|
84
|
+
check_if_method_has_arguments!(:order, args)
|
85
|
+
spawn.order!(*args)
|
86
|
+
end
|
87
|
+
|
88
|
+
def order!(*args) # :nodoc:
|
89
|
+
preprocess_order_args(args)
|
90
|
+
|
91
|
+
self.order_values += args
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
# Replaces any existing order defined on the relation with the specified order.
|
96
|
+
#
|
97
|
+
# User.order('email DESC').reorder('id ASC') # generated SQL has 'ORDER BY id ASC'
|
98
|
+
#
|
99
|
+
# Subsequent calls to order on the same relation will be appended. For example:
|
100
|
+
#
|
101
|
+
# User.order('email DESC').reorder('id ASC').order('name ASC')
|
102
|
+
#
|
103
|
+
# generates a query with 'ORDER BY id ASC, name ASC'.
|
104
|
+
def reorder(*args)
|
105
|
+
check_if_method_has_arguments!(:reorder, args)
|
106
|
+
spawn.reorder!(*args)
|
107
|
+
end
|
108
|
+
|
109
|
+
def reorder!(*args) # :nodoc:
|
110
|
+
preprocess_order_args(args)
|
111
|
+
|
112
|
+
self.reordering_value = true
|
113
|
+
self.order_values = args
|
8
114
|
self
|
9
115
|
end
|
10
116
|
|
117
|
+
# Reverse the existing order clause on the relation.
|
118
|
+
#
|
119
|
+
# User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
|
11
120
|
def reverse_order
|
121
|
+
spawn.reverse_order!
|
122
|
+
end
|
123
|
+
|
124
|
+
def reverse_order! # :nodoc:
|
125
|
+
self.reverse_order_value = !reverse_order_value
|
12
126
|
self
|
13
127
|
end
|
14
128
|
|
15
|
-
|
16
|
-
|
17
|
-
|
129
|
+
private
|
130
|
+
|
131
|
+
def build_order(prefix)
|
132
|
+
orders = order_values.uniq
|
133
|
+
orders.reject!(&:blank?)
|
134
|
+
orders = reverse_sql_order(orders) if reverse_order_value
|
135
|
+
|
136
|
+
orders.map {|o| "#{prefix}.#{o}"}.join(', ')
|
137
|
+
end
|
138
|
+
|
139
|
+
def reverse_sql_order(order_query)
|
140
|
+
# order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
|
141
|
+
|
142
|
+
order_query.flat_map do |o|
|
143
|
+
case o
|
144
|
+
when String
|
145
|
+
split_order(o).map! do |s|
|
146
|
+
s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
|
147
|
+
end
|
148
|
+
else
|
149
|
+
o
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def spawn
|
155
|
+
clone
|
156
|
+
end
|
157
|
+
|
158
|
+
def split_order s
|
159
|
+
s.to_s.split(',').map! &:strip
|
160
|
+
end
|
161
|
+
|
162
|
+
def preprocess_order_args(order_args)
|
163
|
+
order_args.flatten!
|
164
|
+
validate_order_args(order_args)
|
165
|
+
|
166
|
+
# references = order_args.grep(String)
|
167
|
+
# references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
|
168
|
+
# references!(references) if references.any?
|
169
|
+
|
170
|
+
order_args.map! do |arg|
|
171
|
+
case arg
|
172
|
+
when Symbol
|
173
|
+
arg = klass.attribute_alias(arg) if klass.try :attribute_alias?, arg
|
174
|
+
arg.to_s
|
175
|
+
when Hash
|
176
|
+
arg.map { |field, dir|
|
177
|
+
field = klass.attribute_alias(field) if klass.try :attribute_alias?, arg
|
178
|
+
"#{field} #{dir}"
|
179
|
+
}
|
180
|
+
when String
|
181
|
+
split_order(arg)
|
182
|
+
else
|
183
|
+
arg
|
184
|
+
end
|
185
|
+
end.flatten!
|
18
186
|
end
|
19
187
|
|
20
|
-
|
21
|
-
|
188
|
+
# Checks to make sure that the arguments are not blank. Note that if some
|
189
|
+
# blank-like object were initially passed into the query method, then this
|
190
|
+
# method will not raise an error.
|
191
|
+
#
|
192
|
+
# Example:
|
193
|
+
#
|
194
|
+
# Post.references() # => raises an error
|
195
|
+
# Post.references([]) # => does not raise an error
|
196
|
+
#
|
197
|
+
# This particular method should be called with a method_name and the args
|
198
|
+
# passed into that method as an input. For example:
|
199
|
+
#
|
200
|
+
# def references(*args)
|
201
|
+
# check_if_method_has_arguments!("references", args)
|
202
|
+
# ...
|
203
|
+
# end
|
204
|
+
def check_if_method_has_arguments!(method_name, args)
|
205
|
+
if args.blank?
|
206
|
+
raise ArgumentError, "The method .#{method_name}() must contain arguments."
|
207
|
+
end
|
22
208
|
end
|
23
209
|
|
24
|
-
def
|
25
|
-
|
210
|
+
def validate_order_args(args)
|
211
|
+
args.grep(Hash) do |h|
|
212
|
+
unless (h.values - [:asc, :desc]).empty?
|
213
|
+
raise ArgumentError, 'Direction should be :asc or :desc'
|
214
|
+
end
|
215
|
+
end
|
26
216
|
end
|
27
217
|
end
|
28
|
-
end
|
218
|
+
end
|
data/lib/active_node/version.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveNode::QueryMethods do
|
4
|
+
describe "#order" do
|
5
|
+
it "should order" do
|
6
|
+
p1=Person.create!(name: 'abc')
|
7
|
+
p2=Person.create!(name: 'def')
|
8
|
+
Person.order(:name).should == [p1, p2]
|
9
|
+
Person.order(name: :desc).should == [p2, p1]
|
10
|
+
Person.order('name asc').reverse_order.should == [p2, p1]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#limit" do
|
15
|
+
it "should limt" do
|
16
|
+
p1=Person.create!(name: 'abc')
|
17
|
+
p2=Person.create!(name: 'def')
|
18
|
+
Person.limit(1).should == [p1]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_node
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Heinrich Klobuczek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: active_attr
|
@@ -182,6 +182,7 @@ files:
|
|
182
182
|
- lib/active_node/version.rb
|
183
183
|
- spec/functional/associations_spec.rb
|
184
184
|
- spec/functional/base_spec.rb
|
185
|
+
- spec/functional/graph/query_methods_spec.rb
|
185
186
|
- spec/functional/graph_spec.rb
|
186
187
|
- spec/functional/persistence_spec.rb
|
187
188
|
- spec/functional/validations_spec.rb
|
@@ -218,6 +219,7 @@ summary: ActiveRecord style Object Graph Mapping for neo4j
|
|
218
219
|
test_files:
|
219
220
|
- spec/functional/associations_spec.rb
|
220
221
|
- spec/functional/base_spec.rb
|
222
|
+
- spec/functional/graph/query_methods_spec.rb
|
221
223
|
- spec/functional/graph_spec.rb
|
222
224
|
- spec/functional/persistence_spec.rb
|
223
225
|
- spec/functional/validations_spec.rb
|