active_node 2.2.3 → 2.2.4
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.
- 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
|