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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f1e0b745f51b5a1c58f9053bb5fe9b0d94b24bec
4
- data.tar.gz: f83ca71aed4249ee3f7eff3f9a69b00b5b6ad7e5
3
+ metadata.gz: f099f7600aea945cfd5006b737360b8d5403909c
4
+ data.tar.gz: b2505371bcbc5ed36a5692fc6720aab6bff55985
5
5
  SHA512:
6
- metadata.gz: ced0ba488ef4173804f1dbd6cc96ba567ef76f796ed7e3b093e901df80b0228309ba1dfbfb3da6fb58f9ccd73303f002642c693b353542f2f6085a64f58ae271
7
- data.tar.gz: 20b2b8337a2fc1577fa9f943a5dc1ab4476138b1e3bb87255ed67963085252cf2d505e5bcd68bf9f57584ea706e1e8da9c1a47c84b83f56ecea8f2e77689f319
6
+ metadata.gz: 52822ad900508188e43d78788179768eb0dcde8cbbb4e543fc28c63cb1d259913dbbaeb0f50a3e6af29e4977fb063f5b8f4f57864d00778e2d0ea94cf981314a
7
+ data.tar.gz: aa4ca077908e7d79375059bfe776291bb4d3267d1b088e80515c6127ca8293e86c94c49945c55a39e216a40543ac5e22d9fae22da335c8b06d8447b19ca164d2
@@ -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 #{@limit}" if @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), 'order by', created_at_list(@reflections.size)].compact.join ' '
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 created_at_list num
240
- comma_sep_list(num) { |i| "n#{i}.created_at" }
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
- def order(*args)
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
- #TODO temporary stubbing
16
- def offset_value
17
- 0
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
- def order_values
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 limit_value
25
- nil
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
@@ -1,3 +1,3 @@
1
1
  module ActiveNode
2
- VERSION = "2.2.3"
2
+ VERSION = "2.2.4"
3
3
  end
@@ -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.3
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-13 00:00:00.000000000 Z
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