neo4j 9.6.2 → 10.0.0.pre.alpha.1

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +0 -13
  3. data/CONTRIBUTORS +4 -0
  4. data/Gemfile +2 -33
  5. data/lib/neo4j.rb +6 -2
  6. data/lib/neo4j/active_base.rb +19 -22
  7. data/lib/neo4j/active_node/has_n.rb +1 -1
  8. data/lib/neo4j/active_node/labels.rb +1 -11
  9. data/lib/neo4j/active_node/node_wrapper.rb +1 -1
  10. data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +1 -1
  11. data/lib/neo4j/active_rel/rel_wrapper.rb +2 -2
  12. data/lib/neo4j/ansi.rb +14 -0
  13. data/lib/neo4j/core.rb +14 -0
  14. data/lib/neo4j/core/connection_failed_error.rb +6 -0
  15. data/lib/neo4j/core/cypher_error.rb +37 -0
  16. data/lib/neo4j/core/driver.rb +83 -0
  17. data/lib/neo4j/core/has_uri.rb +63 -0
  18. data/lib/neo4j/core/instrumentable.rb +36 -0
  19. data/lib/neo4j/core/label.rb +158 -0
  20. data/lib/neo4j/core/logging.rb +44 -0
  21. data/lib/neo4j/core/node.rb +23 -0
  22. data/lib/neo4j/core/querable.rb +88 -0
  23. data/lib/neo4j/core/query.rb +487 -0
  24. data/lib/neo4j/core/query_builder.rb +32 -0
  25. data/lib/neo4j/core/query_clauses.rb +727 -0
  26. data/lib/neo4j/core/query_find_in_batches.rb +49 -0
  27. data/lib/neo4j/core/relationship.rb +13 -0
  28. data/lib/neo4j/core/responses.rb +50 -0
  29. data/lib/neo4j/core/result.rb +33 -0
  30. data/lib/neo4j/core/schema.rb +30 -0
  31. data/lib/neo4j/core/schema_errors.rb +12 -0
  32. data/lib/neo4j/core/wrappable.rb +30 -0
  33. data/lib/neo4j/migration.rb +2 -2
  34. data/lib/neo4j/migrations/base.rb +1 -1
  35. data/lib/neo4j/model_schema.rb +2 -2
  36. data/lib/neo4j/railtie.rb +8 -52
  37. data/lib/neo4j/schema/operation.rb +1 -1
  38. data/lib/neo4j/shared.rb +1 -1
  39. data/lib/neo4j/shared/property.rb +1 -1
  40. data/lib/neo4j/tasks/migration.rake +5 -4
  41. data/lib/neo4j/transaction.rb +137 -0
  42. data/lib/neo4j/version.rb +1 -1
  43. data/neo4j.gemspec +5 -5
  44. metadata +59 -26
  45. data/bin/neo4j-jars +0 -33
  46. data/lib/neo4j/active_base/session_registry.rb +0 -12
  47. data/lib/neo4j/session_manager.rb +0 -78
@@ -0,0 +1,32 @@
1
+ module Neo4j
2
+ module Core
3
+ class QueryBuilder
4
+ attr_reader :queries
5
+
6
+ Query = Struct.new(:cypher, :parameters, :pretty_cypher, :context)
7
+
8
+ def initialize
9
+ @queries = []
10
+ end
11
+
12
+ def append(*args)
13
+ query = case args.map(&:class)
14
+ when [String], [String, Hash]
15
+ Query.new(args[0], args[1] || {})
16
+ when [::Neo4j::Core::Query]
17
+ args[0]
18
+ else
19
+ fail ArgumentError, "Could not determine query from arguments: #{args.inspect}"
20
+ end
21
+
22
+ @queries << query
23
+ end
24
+
25
+ def query
26
+ # `nil` sessions are just a workaround until
27
+ # we phase out `Query` objects containing sessions
28
+ Neo4j::Core::Query.new(session: nil)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,727 @@
1
+ module Neo4j
2
+ module Core
3
+ module QueryClauses
4
+ class ArgError < StandardError
5
+ attr_reader :arg_part
6
+ def initialize(arg_part = nil)
7
+ super
8
+ @arg_part = arg_part
9
+ end
10
+ end
11
+
12
+ class Clause
13
+ UNDERSCORE = '_'
14
+ COMMA_SPACE = ', '
15
+ AND = ' AND '
16
+ PRETTY_NEW_LINE = "\n "
17
+
18
+ attr_accessor :params, :arg
19
+ attr_reader :options, :param_vars_added
20
+
21
+ def initialize(arg, params, options = {})
22
+ @arg = arg
23
+ @options = options
24
+ @params = params
25
+ @param_vars_added = []
26
+ end
27
+
28
+ def value
29
+ return @value if @value
30
+
31
+ [String, Symbol, Integer, Hash, NilClass].each do |arg_class|
32
+ from_method = "from_#{arg_class.name.downcase}"
33
+ return @value = send(from_method, @arg) if @arg.is_a?(arg_class) && respond_to?(from_method)
34
+ end
35
+
36
+ fail ArgError
37
+ rescue ArgError => arg_error
38
+ message = "Invalid argument for #{self.class.keyword}. Full arguments: #{@arg.inspect}"
39
+ message += " | Invalid part: #{arg_error.arg_part.inspect}" if arg_error.arg_part
40
+
41
+ raise ArgumentError, message
42
+ end
43
+
44
+ def from_hash(value)
45
+ fail ArgError if !respond_to?(:from_key_and_value)
46
+
47
+ value.map do |k, v|
48
+ from_key_and_value k, v
49
+ end
50
+ end
51
+
52
+ def from_string(value)
53
+ value
54
+ end
55
+
56
+ def node_from_key_and_value(key, value, options = {})
57
+ prefer = options[:prefer] || :var
58
+ var = var_from_key_and_value(key, value, prefer)
59
+ label = label_from_key_and_value(key, value, prefer)
60
+
61
+ attributes = attributes_from_key_and_value(key, value)
62
+
63
+ prefix_value = value
64
+ if value.is_a?(Hash)
65
+ prefix_value = (value.keys.join(UNDERSCORE) if value.values.any? { |v| v.is_a?(Hash) })
66
+ end
67
+
68
+ prefix_array = [key, prefix_value].tap(&:compact!).join(UNDERSCORE)
69
+ formatted_attributes = attributes_string(attributes, "#{prefix_array}#{UNDERSCORE}")
70
+ "(#{var}#{format_label(label)}#{formatted_attributes})"
71
+ end
72
+
73
+ def var_from_key_and_value(key, value, prefer = :var)
74
+ case value
75
+ when String, Symbol, Class, Module, NilClass, Array then key
76
+ when Hash
77
+ key if _use_key_for_var?(value, prefer)
78
+ else
79
+ fail ArgError, value
80
+ end
81
+ end
82
+
83
+ def label_from_key_and_value(key, value, prefer = :var)
84
+ case value
85
+ when String, Symbol, Array, NilClass then value
86
+ when Class, Module then value.name
87
+ when Hash
88
+ if value.values.map(&:class) == [Hash]
89
+ value.first.first
90
+ elsif !_use_key_for_var?(value, prefer)
91
+ key
92
+ end
93
+ else
94
+ fail ArgError, value
95
+ end
96
+ end
97
+
98
+ def _use_key_for_var?(value, prefer)
99
+ _nested_value_hash?(value) || prefer == :var
100
+ end
101
+
102
+ def _nested_value_hash?(value)
103
+ value.values.any? { |v| v.is_a?(Hash) }
104
+ end
105
+
106
+ def attributes_from_key_and_value(_key, value)
107
+ return nil unless value.is_a?(Hash)
108
+
109
+ value.values.map(&:class) == [Hash] ? value.first[1] : value
110
+ end
111
+
112
+ class << self
113
+ def keyword
114
+ self::KEYWORD
115
+ end
116
+
117
+ def keyword_downcase
118
+ keyword.downcase
119
+ end
120
+
121
+ def from_args(args, params, options = {})
122
+ args.flatten!
123
+ args.map { |arg| from_arg(arg, params, options) }.tap(&:compact!)
124
+ end
125
+
126
+ def from_arg(arg, params, options = {})
127
+ new(arg, params, options) if !arg.respond_to?(:empty?) || !arg.empty?
128
+ end
129
+
130
+ def to_cypher(clauses, pretty = false)
131
+ string = clause_string(clauses, pretty)
132
+
133
+ final_keyword = if pretty
134
+ "#{clause_color}#{keyword}#{ANSI::CLEAR}"
135
+ else
136
+ keyword
137
+ end
138
+
139
+ "#{final_keyword} #{string}" if !string.empty?
140
+ end
141
+
142
+ def clause_string(clauses, pretty)
143
+ join_string = pretty ? clause_join + PRETTY_NEW_LINE : clause_join
144
+
145
+ strings = clause_strings(clauses)
146
+ stripped_string = strings.join(join_string).strip
147
+ pretty && strings.size > 1 ? PRETTY_NEW_LINE + stripped_string : stripped_string
148
+ end
149
+
150
+ def clause_join
151
+ ''
152
+ end
153
+
154
+ def clause_color
155
+ ANSI::CYAN
156
+ end
157
+
158
+ def from_key_and_single_value(key, value)
159
+ value.to_sym == :neo_id ? "ID(#{key})" : "#{key}.#{value}"
160
+ end
161
+ end
162
+
163
+ def self.paramaterize_key!(key)
164
+ key.tr_s!('^a-zA-Z0-9', UNDERSCORE)
165
+ key.gsub!(/^_+|_+$/, '')
166
+ end
167
+
168
+ def add_param(key, value)
169
+ @param_vars_added << key
170
+ @params.add_param(key, value)
171
+ end
172
+
173
+ def add_params(keys_and_values)
174
+ @param_vars_added += keys_and_values.keys
175
+ @params.add_params(keys_and_values)
176
+ end
177
+
178
+ private
179
+
180
+ def key_value_string(key, value, previous_keys = [], is_set = false)
181
+ param = (previous_keys << key).join(UNDERSCORE)
182
+ self.class.paramaterize_key!(param)
183
+
184
+ if value.is_a?(Range)
185
+ range_key_value_string(key, value, previous_keys, param)
186
+ else
187
+ value = value.first if array_value?(value, is_set) && value.size == 1
188
+
189
+ param = add_param(param, value)
190
+
191
+ "#{key} #{array_value?(value, is_set) ? 'IN' : '='} {#{param}}"
192
+ end
193
+ end
194
+
195
+ def range_key_value_string(key, value, previous_keys, param)
196
+ begin_param, end_param = add_params("#{param}_range_min" => value.begin, "#{param}_range_max" => value.end)
197
+ "#{key} >= {#{begin_param}} AND #{previous_keys[-2]}.#{key} <#{'=' unless value.exclude_end?} {#{end_param}}"
198
+ end
199
+
200
+ def array_value?(value, is_set)
201
+ value.is_a?(Array) && !is_set
202
+ end
203
+
204
+ def format_label(label_arg)
205
+ return label_arg.map { |arg| format_label(arg) }.join if label_arg.is_a?(Array)
206
+
207
+ label_arg = label_arg.to_s.strip
208
+ if !label_arg.empty? && label_arg[0] != ':'
209
+ label_arg = "`#{label_arg}`" unless label_arg[' ']
210
+ label_arg = ":#{label_arg}"
211
+ end
212
+ label_arg
213
+ end
214
+
215
+ def attributes_string(attributes, prefix = '')
216
+ return '' if not attributes
217
+
218
+ attributes_string = attributes.map do |key, value|
219
+ if value.to_s =~ /^{.+}$/
220
+ "#{key}: #{value}"
221
+ else
222
+ param_key = "#{prefix}#{key}".gsub(/:+/, '_')
223
+ param_key = add_param(param_key, value)
224
+ "#{key}: {#{param_key}}"
225
+ end
226
+ end.join(Clause::COMMA_SPACE)
227
+
228
+ " {#{attributes_string}}"
229
+ end
230
+ end
231
+
232
+ class StartClause < Clause
233
+ KEYWORD = 'START'
234
+
235
+ def from_symbol(value)
236
+ from_string(value.to_s)
237
+ end
238
+
239
+ def from_key_and_value(key, value)
240
+ case value
241
+ when String, Symbol
242
+ "#{key} = #{value}"
243
+ else
244
+ fail ArgError, value
245
+ end
246
+ end
247
+
248
+ class << self
249
+ def clause_strings(clauses)
250
+ clauses.map!(&:value)
251
+ end
252
+
253
+ def clause_join
254
+ Clause::COMMA_SPACE
255
+ end
256
+ end
257
+ end
258
+
259
+ class WhereClause < Clause
260
+ KEYWORD = 'WHERE'
261
+
262
+ PAREN_SURROUND_REGEX = /^\s*\(.+\)\s*$/
263
+
264
+ def from_key_and_value(key, value, previous_keys = [])
265
+ case value
266
+ when Hash then hash_key_value_string(key, value, previous_keys)
267
+ when NilClass then "#{key} IS NULL"
268
+ when Regexp then regexp_key_value_string(key, value, previous_keys)
269
+ else
270
+ key_value_string(key, value, previous_keys)
271
+ end
272
+ end
273
+
274
+ class << self
275
+ def clause_strings(clauses)
276
+ clauses.flat_map do |clause|
277
+ Array(clause.value).map do |v|
278
+ (clause.options[:not] ? 'NOT' : '') + (v.to_s.match(PAREN_SURROUND_REGEX) ? v.to_s : "(#{v})")
279
+ end
280
+ end
281
+ end
282
+
283
+ def clause_join
284
+ Clause::AND
285
+ end
286
+ end
287
+
288
+ private
289
+
290
+ def hash_key_value_string(key, value, previous_keys)
291
+ value.map do |k, v|
292
+ if k.to_sym == :neo_id
293
+ v = Array(v).map { |item| (item.respond_to?(:neo_id) ? item.neo_id : item).to_i }
294
+ key_value_string("ID(#{key})", v)
295
+ else
296
+ "#{key}.#{from_key_and_value(k, v, previous_keys + [key])}"
297
+ end
298
+ end.join(AND)
299
+ end
300
+
301
+ def regexp_key_value_string(key, value, previous_keys)
302
+ pattern = (value.casefold? ? '(?i)' : '') + value.source
303
+
304
+ param = [previous_keys + [key]].join(UNDERSCORE)
305
+ self.class.paramaterize_key!(param)
306
+
307
+ param = add_param(param, pattern)
308
+
309
+ "#{key} =~ {#{param}}"
310
+ end
311
+
312
+ class << self
313
+ ARG_HAS_QUESTION_MARK_REGEX = /(^|\(|\s)\?(\s|\)|$)/
314
+
315
+ def from_args(args, params, options = {})
316
+ query_string, params_arg = args
317
+
318
+ if query_string.is_a?(String) && (query_string.match(ARG_HAS_QUESTION_MARK_REGEX) || params_arg.is_a?(Hash))
319
+ if params_arg.is_a?(Hash)
320
+ params.add_params(params_arg)
321
+ else
322
+ param_var = params.add_params(question_mark_param: params_arg)[0]
323
+ query_string = query_string.gsub(ARG_HAS_QUESTION_MARK_REGEX, "\\1{#{param_var}}\\2")
324
+ end
325
+
326
+ [from_arg(query_string, params, options)]
327
+ else
328
+ super
329
+ end
330
+ end
331
+ end
332
+ end
333
+
334
+ class CallClause < Clause
335
+ KEYWORD = 'CALL'
336
+
337
+ def from_string(value)
338
+ value
339
+ end
340
+
341
+ class << self
342
+ def clause_strings(clauses)
343
+ clauses.map!(&:value)
344
+ end
345
+
346
+ def clause_join
347
+ " #{KEYWORD} "
348
+ end
349
+ end
350
+ end
351
+
352
+ class MatchClause < Clause
353
+ KEYWORD = 'MATCH'
354
+
355
+ def from_symbol(value)
356
+ '(' + from_string(value.to_s) + ')'
357
+ end
358
+
359
+ def from_key_and_value(key, value)
360
+ node_from_key_and_value(key, value)
361
+ end
362
+
363
+ class << self
364
+ def clause_strings(clauses)
365
+ clauses.map!(&:value)
366
+ end
367
+
368
+ def clause_join
369
+ Clause::COMMA_SPACE
370
+ end
371
+ end
372
+ end
373
+
374
+ class OptionalMatchClause < MatchClause
375
+ KEYWORD = 'OPTIONAL MATCH'
376
+ end
377
+
378
+ class WithClause < Clause
379
+ KEYWORD = 'WITH'
380
+
381
+ def from_symbol(value)
382
+ from_string(value.to_s)
383
+ end
384
+
385
+ def from_key_and_value(key, value)
386
+ "#{value} AS #{key}"
387
+ end
388
+
389
+ class << self
390
+ def clause_strings(clauses)
391
+ clauses.map!(&:value)
392
+ end
393
+
394
+ def clause_join
395
+ Clause::COMMA_SPACE
396
+ end
397
+ end
398
+ end
399
+
400
+ class WithDistinctClause < WithClause
401
+ KEYWORD = 'WITH DISTINCT'
402
+ end
403
+
404
+ class UsingClause < Clause
405
+ KEYWORD = 'USING'
406
+
407
+ class << self
408
+ def clause_strings(clauses)
409
+ clauses.map!(&:value)
410
+ end
411
+
412
+ def clause_join
413
+ " #{keyword} "
414
+ end
415
+ end
416
+ end
417
+
418
+ class CreateClause < Clause
419
+ KEYWORD = 'CREATE'
420
+
421
+ def from_string(value)
422
+ value
423
+ end
424
+
425
+ def from_symbol(value)
426
+ "(:#{value})"
427
+ end
428
+
429
+ def from_hash(hash)
430
+ if hash.values.any? { |value| value.is_a?(Hash) }
431
+ hash.map do |key, value|
432
+ from_key_and_value(key, value)
433
+ end
434
+ else
435
+ "(#{attributes_string(hash)})"
436
+ end
437
+ end
438
+
439
+ def from_key_and_value(key, value)
440
+ node_from_key_and_value(key, value, prefer: :label)
441
+ end
442
+
443
+ class << self
444
+ def clause_strings(clauses)
445
+ clauses.map!(&:value)
446
+ end
447
+
448
+ def clause_join
449
+ ', '
450
+ end
451
+
452
+ def clause_color
453
+ ANSI::GREEN
454
+ end
455
+ end
456
+ end
457
+
458
+ class CreateUniqueClause < CreateClause
459
+ KEYWORD = 'CREATE UNIQUE'
460
+ end
461
+
462
+ class MergeClause < CreateClause
463
+ KEYWORD = 'MERGE'
464
+
465
+ class << self
466
+ def clause_color
467
+ ANSI::MAGENTA
468
+ end
469
+
470
+ def clause_join
471
+ ' MERGE '
472
+ end
473
+ end
474
+ end
475
+
476
+ class DeleteClause < Clause
477
+ KEYWORD = 'DELETE'
478
+
479
+ def from_symbol(value)
480
+ from_string(value.to_s)
481
+ end
482
+
483
+ class << self
484
+ def clause_strings(clauses)
485
+ clauses.map!(&:value)
486
+ end
487
+
488
+ def clause_join
489
+ Clause::COMMA_SPACE
490
+ end
491
+
492
+ def clause_color
493
+ ANSI::RED
494
+ end
495
+ end
496
+ end
497
+
498
+ class DetachDeleteClause < DeleteClause
499
+ KEYWORD = 'DETACH DELETE'
500
+ end
501
+
502
+ class OrderClause < Clause
503
+ KEYWORD = 'ORDER BY'
504
+
505
+ def from_symbol(value)
506
+ from_string(value.to_s)
507
+ end
508
+
509
+ def from_key_and_value(key, value)
510
+ case value
511
+ when String, Symbol
512
+ self.class.from_key_and_single_value(key, value)
513
+ when Array
514
+ value.map do |v|
515
+ v.is_a?(Hash) ? from_key_and_value(key, v) : self.class.from_key_and_single_value(key, v)
516
+ end
517
+ when Hash
518
+ value.map { |k, v| "#{self.class.from_key_and_single_value(key, k)} #{v.upcase}" }
519
+ end
520
+ end
521
+
522
+ class << self
523
+ def clause_strings(clauses)
524
+ clauses.map!(&:value)
525
+ end
526
+
527
+ def clause_join
528
+ Clause::COMMA_SPACE
529
+ end
530
+ end
531
+ end
532
+
533
+ class LimitClause < Clause
534
+ KEYWORD = 'LIMIT'
535
+
536
+ def from_string(value)
537
+ param_var = "#{self.class.keyword_downcase}_#{value}"
538
+ param_var = add_param(param_var, value.to_i)
539
+ "{#{param_var}}"
540
+ end
541
+
542
+ def from_integer(value)
543
+ from_string(value)
544
+ end
545
+
546
+ def from_nilclass(_value)
547
+ ''
548
+ end
549
+
550
+ class << self
551
+ def clause_strings(clauses)
552
+ result_clause = clauses.last
553
+
554
+ clauses[0..-2].map(&:param_vars_added).flatten.grep(/^limit_\d+$/).each do |var|
555
+ result_clause.params.remove_param(var)
556
+ end
557
+
558
+ [result_clause.value]
559
+ end
560
+ end
561
+ end
562
+
563
+ class SkipClause < Clause
564
+ KEYWORD = 'SKIP'
565
+
566
+ def from_string(value)
567
+ clause_id = "#{self.class.keyword_downcase}_#{value}"
568
+ clause_id = add_param(clause_id, value.to_i)
569
+ "{#{clause_id}}"
570
+ end
571
+
572
+ def from_integer(value)
573
+ clause_id = "#{self.class.keyword_downcase}_#{value}"
574
+ clause_id = add_param(clause_id, value)
575
+ "{#{clause_id}}"
576
+ end
577
+
578
+ class << self
579
+ def clause_strings(clauses)
580
+ result_clause = clauses.last
581
+
582
+ clauses[0..-2].map(&:param_vars_added).flatten.grep(/^skip_\d+$/).each do |var|
583
+ result_clause.params.remove_param(var)
584
+ end
585
+
586
+ [result_clause.value]
587
+ end
588
+ end
589
+ end
590
+
591
+ class SetClause < Clause
592
+ KEYWORD = 'SET'
593
+
594
+ def from_key_and_value(key, value)
595
+ case value
596
+ when String, Symbol then "#{key}:`#{value}`"
597
+ when Hash
598
+ if @options[:set_props]
599
+ param = add_param("#{key}_set_props", value)
600
+ "#{key} = {#{param}}"
601
+ else
602
+ value.map { |k, v| key_value_string("#{key}.`#{k}`", v, ['setter'], true) }
603
+ end
604
+ when Array then value.map { |v| from_key_and_value(key, v) }
605
+ when NilClass then []
606
+ else
607
+ fail ArgError, value
608
+ end
609
+ end
610
+
611
+ class << self
612
+ def clause_strings(clauses)
613
+ clauses.map!(&:value)
614
+ end
615
+
616
+ def clause_join
617
+ Clause::COMMA_SPACE
618
+ end
619
+
620
+ def clause_color
621
+ ANSI::YELLOW
622
+ end
623
+ end
624
+ end
625
+
626
+ class OnCreateSetClause < SetClause
627
+ KEYWORD = 'ON CREATE SET'
628
+
629
+ def initialize(*args)
630
+ super
631
+ @options[:set_props] = false
632
+ end
633
+ end
634
+
635
+ class OnMatchSetClause < OnCreateSetClause
636
+ KEYWORD = 'ON MATCH SET'
637
+ end
638
+
639
+ class RemoveClause < Clause
640
+ KEYWORD = 'REMOVE'
641
+
642
+ def from_key_and_value(key, value)
643
+ case value
644
+ when /^:/
645
+ "#{key}:`#{value[1..-1]}`"
646
+ when String
647
+ "#{key}.#{value}"
648
+ when Symbol
649
+ "#{key}:`#{value}`"
650
+ when Array
651
+ value.map do |v|
652
+ from_key_and_value(key, v)
653
+ end
654
+ else
655
+ fail ArgError, value
656
+ end
657
+ end
658
+
659
+ class << self
660
+ def clause_strings(clauses)
661
+ clauses.map!(&:value)
662
+ end
663
+
664
+ def clause_join
665
+ Clause::COMMA_SPACE
666
+ end
667
+ end
668
+ end
669
+
670
+ class UnwindClause < Clause
671
+ KEYWORD = 'UNWIND'
672
+
673
+ def from_key_and_value(key, value)
674
+ case value
675
+ when String, Symbol
676
+ "#{value} AS #{key}"
677
+ when Array
678
+ "#{value.inspect} AS #{key}"
679
+ else
680
+ fail ArgError, value
681
+ end
682
+ end
683
+
684
+ class << self
685
+ def clause_strings(clauses)
686
+ clauses.map!(&:value)
687
+ end
688
+
689
+ def clause_join
690
+ ' UNWIND '
691
+ end
692
+ end
693
+ end
694
+
695
+ class ReturnClause < Clause
696
+ KEYWORD = 'RETURN'
697
+
698
+ def from_symbol(value)
699
+ from_string(value.to_s)
700
+ end
701
+
702
+ def from_key_and_value(key, value)
703
+ case value
704
+ when Array
705
+ value.map do |v|
706
+ from_key_and_value(key, v)
707
+ end.join(Clause::COMMA_SPACE)
708
+ when String, Symbol
709
+ self.class.from_key_and_single_value(key, value)
710
+ else
711
+ fail ArgError, value
712
+ end
713
+ end
714
+
715
+ class << self
716
+ def clause_strings(clauses)
717
+ clauses.map!(&:value)
718
+ end
719
+
720
+ def clause_join
721
+ Clause::COMMA_SPACE
722
+ end
723
+ end
724
+ end
725
+ end
726
+ end
727
+ end