pacer 2.0.10.pre-java → 2.0.12-java

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 (71) hide show
  1. checksums.yaml +5 -13
  2. data/.autotest +0 -4
  3. data/.gitignore +1 -0
  4. data/.travis.yml +2 -2
  5. data/Rakefile +25 -0
  6. data/blog/detach_benchmarks.txt +56 -0
  7. data/ext/.classpath +26 -0
  8. data/ext/.gitignore +44 -0
  9. data/ext/.project +23 -0
  10. data/ext/pom.xml +76 -0
  11. data/ext/src/main/java/com/xnlogic/pacer/pipes/BlackboxPipeline.java +56 -0
  12. data/ext/src/main/java/com/xnlogic/pacer/pipes/CollectionFilterPipe.java +18 -0
  13. data/ext/src/main/java/com/xnlogic/pacer/pipes/EdgesPipe.java +26 -0
  14. data/ext/src/main/java/com/xnlogic/pacer/pipes/ExpandablePipe.java +93 -0
  15. data/ext/src/main/java/com/xnlogic/pacer/pipes/IdCollectionFilterPipe.java +50 -0
  16. data/ext/src/main/java/com/xnlogic/pacer/pipes/IsEmptyPipe.java +35 -0
  17. data/ext/src/main/java/com/xnlogic/pacer/pipes/IsUniquePipe.java +58 -0
  18. data/ext/src/main/java/com/xnlogic/pacer/pipes/LabelCollectionFilterPipe.java +32 -0
  19. data/ext/src/main/java/com/xnlogic/pacer/pipes/LabelPrefixPipe.java +23 -0
  20. data/ext/src/main/java/com/xnlogic/pacer/pipes/NeverPipe.java +10 -0
  21. data/ext/src/main/java/com/xnlogic/pacer/pipes/VerticesPipe.java +26 -0
  22. data/ext/src/test/java/com/xnlogic/pacer/pipes/BlackboxPipelineTest.java +33 -0
  23. data/ext/src/test/java/com/xnlogic/pacer/pipes/CollectionFilterPipeTest.java +50 -0
  24. data/ext/src/test/java/com/xnlogic/pacer/pipes/EdgesPipeTest.java +103 -0
  25. data/ext/src/test/java/com/xnlogic/pacer/pipes/ExpandablePipeTest.java +66 -0
  26. data/ext/src/test/java/com/xnlogic/pacer/pipes/IdCollectionFilterPipeTest.java +85 -0
  27. data/ext/src/test/java/com/xnlogic/pacer/pipes/IsUniquePipeTest.java +62 -0
  28. data/ext/src/test/java/com/xnlogic/pacer/pipes/LabelCollectionFilterPipeTest.java +97 -0
  29. data/ext/src/test/java/com/xnlogic/pacer/pipes/LabelPrefixPipeTest.java +69 -0
  30. data/ext/src/test/java/com/xnlogic/pacer/pipes/NeverPipeTest.java +17 -0
  31. data/ext/src/test/java/com/xnlogic/pacer/pipes/VerticesPipeTest.java +97 -0
  32. data/lib/pacer/core/graph/element_route.rb +2 -2
  33. data/lib/pacer/core/graph/path_route.rb +2 -2
  34. data/lib/pacer/core/route.rb +77 -1
  35. data/lib/pacer/filter/property_filter/edge_filters.rb +5 -1
  36. data/lib/pacer/filter/property_filter/filters.rb +23 -7
  37. data/lib/pacer/pipe/path_wrapping_pipe.rb +14 -3
  38. data/lib/pacer/pipe/wrapping_pipe.rb +15 -4
  39. data/lib/pacer/pipes.rb +15 -13
  40. data/lib/pacer/route.rb +1 -0
  41. data/lib/pacer/side_effect/as_var.rb +1 -1
  42. data/lib/pacer/side_effect/is_unique.rb +2 -2
  43. data/lib/pacer/transform/branch.rb +1 -1
  44. data/lib/pacer/transform/lookup_ids.rb +1 -1
  45. data/lib/pacer/transform/path_tree.rb +1 -1
  46. data/lib/pacer/transform/reduce.rb +1 -1
  47. data/lib/pacer/transform/sort_section.rb +17 -9
  48. data/lib/pacer/transform/stream_sort.rb +1 -0
  49. data/lib/pacer/transform/stream_uniq.rb +1 -0
  50. data/lib/pacer/transform/wrapped_path.rb +1 -1
  51. data/lib/pacer/version.rb +1 -1
  52. data/lib/pacer/visitors/section.rb +10 -3
  53. data/lib/pacer/visitors/visits_section.rb +4 -4
  54. data/lib/pacer-ext.jar +0 -0
  55. data/lib/pacer.rb +1 -0
  56. data/pacer.gemspec +2 -1
  57. data/spec/pacer/filter/property_filter_spec.rb +17 -0
  58. data/spec/pacer/transform/path_spec.rb +10 -0
  59. data/spec/pacer/transform/sort_section_spec.rb +8 -1
  60. metadata +59 -30
  61. data/lib/pacer/pipe/blackbox_pipeline.rb +0 -55
  62. data/lib/pacer/pipe/collection_filter_pipe.rb +0 -12
  63. data/lib/pacer/pipe/edges_pipe.rb +0 -22
  64. data/lib/pacer/pipe/expandable_pipe.rb +0 -51
  65. data/lib/pacer/pipe/id_collection_filter_pipe.rb +0 -37
  66. data/lib/pacer/pipe/is_empty_pipe.rb +0 -23
  67. data/lib/pacer/pipe/is_unique_pipe.rb +0 -51
  68. data/lib/pacer/pipe/label_collection_filter_pipe.rb +0 -15
  69. data/lib/pacer/pipe/label_prefix_pipe.rb +0 -15
  70. data/lib/pacer/pipe/never_pipe.rb +0 -9
  71. data/lib/pacer/pipe/vertices_pipe.rb +0 -22
@@ -0,0 +1,97 @@
1
+ package com.xnlogic.pacer.pipes;
2
+
3
+ import static org.junit.Assert.*;
4
+ import org.junit.Test;
5
+ import org.junit.Before;
6
+ import org.junit.After;
7
+ import com.tinkerpop.blueprints.impls.tg.TinkerGraph;
8
+ import com.tinkerpop.blueprints.Graph;
9
+ import com.tinkerpop.blueprints.Vertex;
10
+ import java.util.Collection;
11
+ import java.util.Arrays;
12
+ import java.util.ArrayList;
13
+ import com.xnlogic.pacer.pipes.VerticesPipe;
14
+
15
+ public class VerticesPipeTest {
16
+ private TinkerGraph graph = null;
17
+ private Collection<Graph> graphs;
18
+ private Collection<Vertex> verticesThatCount;
19
+
20
+ @Before
21
+ public void setup() throws Exception {
22
+ this.graph = new TinkerGraph();
23
+ this.graphs = Arrays.asList((Graph)this.graph);
24
+ }
25
+
26
+ private void createVertices() {
27
+ Vertex v1 = this.graph.addVertex(1);
28
+ Vertex v2 = this.graph.addVertex(2);
29
+ Vertex v3 = this.graph.addVertex(3);
30
+
31
+ this.verticesThatCount = Arrays.asList(v1, v2, v3);
32
+ }
33
+
34
+ @After
35
+ public void teardown() throws Exception {
36
+ this.graph.shutdown();
37
+ this.graph = null;
38
+ }
39
+
40
+ @Test
41
+ public void getVerticesFromGraphTest() {
42
+ this.createVertices();
43
+
44
+ VerticesPipe verticesPipe = new VerticesPipe();
45
+ verticesPipe.setStarts(this.graphs);
46
+
47
+ Collection<Vertex> vertices = new ArrayList<Vertex>();
48
+
49
+ while (verticesPipe.hasNext()) {
50
+ vertices.add(verticesPipe.next());
51
+ }
52
+
53
+ assertEquals(3, vertices.size());
54
+ assertTrue(vertices.containsAll(this.verticesThatCount));
55
+ }
56
+
57
+ @Test
58
+ public void getVerticesFromGraphAfterResetTest() {
59
+ this.createVertices();
60
+
61
+ VerticesPipe verticesPipe = new VerticesPipe();
62
+ verticesPipe.setStarts(this.graphs);
63
+
64
+ Collection<Vertex> vertices = new ArrayList<Vertex>();
65
+
66
+ while (verticesPipe.hasNext()) {
67
+ vertices.add(verticesPipe.next());
68
+ }
69
+
70
+ assertEquals(3, vertices.size());
71
+ assertTrue(vertices.containsAll(this.verticesThatCount));
72
+
73
+ verticesPipe.reset();
74
+ vertices.clear();
75
+
76
+ while (verticesPipe.hasNext()) {
77
+ vertices.add(verticesPipe.next());
78
+ }
79
+
80
+ assertEquals(3, vertices.size());
81
+ assertTrue(vertices.containsAll(this.verticesThatCount));
82
+ }
83
+
84
+ @Test
85
+ public void getNoVerticesFromGraphTest() {
86
+ VerticesPipe verticesPipe = new VerticesPipe();
87
+ verticesPipe.setStarts(this.graphs);
88
+
89
+ Collection<Vertex> vertices = new ArrayList<Vertex>();
90
+
91
+ while (verticesPipe.hasNext()) {
92
+ vertices.add(verticesPipe.next());
93
+ }
94
+
95
+ assertEquals(0, vertices.size());
96
+ }
97
+ }
@@ -166,8 +166,8 @@ module Pacer::Core::Graph
166
166
 
167
167
  protected
168
168
 
169
- def configure_iterator(iter)
170
- pipe = Pacer::Pipes::WrappingPipe.new graph, element_type, extensions
169
+ def configure_iterator(iter = nil, g = nil)
170
+ pipe = Pacer::Pipes::WrappingPipe.new((g || graph), element_type, extensions)
171
171
  pipe.wrapper = wrapper if wrapper
172
172
  pipe.setStarts iter
173
173
  pipe
@@ -96,9 +96,9 @@ HELP
96
96
  end
97
97
  protected
98
98
 
99
- def configure_iterator(iter)
99
+ def configure_iterator(iter = nil, g = nil)
100
100
  if respond_to? :graph
101
- pipe = Pacer::Pipes::PathWrappingPipe.new(graph)
101
+ pipe = Pacer::Pipes::PathWrappingPipe.new(g || graph)
102
102
  pipe.setStarts iter
103
103
  pipe
104
104
  else
@@ -380,6 +380,25 @@ HELP
380
380
  chain_route(:extensions => [], :wrapper => nil)
381
381
  end
382
382
 
383
+ # Returns an outer proc that returns an inner proc that will execute the given route for a given input data or element.
384
+ #
385
+ # The outer proc contains the already-compiled route (the most expensive part of the process). When you call it, it returns
386
+ # the inner proc. If you call it with no arguments, it will prepare the proc in the context of the original route used to create
387
+ # it. In a multi-tenant or multi-graph system, call the outer proc with the current graph to prepare so that it correctly wraps
388
+ # returned elements.
389
+ #
390
+ # The inner proc contains a non-thread-safe data pipeline compiled from the route. Call it with an item of input data for the detached
391
+ # pipe and it will execute the pipeline for that piece of data and return the result or results.
392
+ #
393
+ # A detached route can be configured with or without gather enabled. If enabled (default), the result will be an ArrayList of
394
+ # all result data produced by the detached route. If not, the result will be either the first thing produced by the route or null if
395
+ # nothing was produced.
396
+ def detach
397
+ route = yield Pacer::Route.empty(self)
398
+ DetachedRoute.new route
399
+ end
400
+
401
+
383
402
  # Change the source of this route.
384
403
  #
385
404
  # @note all routes derived from any route in the chain will be
@@ -421,7 +440,7 @@ HELP
421
440
 
422
441
  # Overridden to extend the iterator to apply mixins
423
442
  # or wrap elements
424
- def configure_iterator(iter)
443
+ def configure_iterator(iter = nil, g = nil)
425
444
  iter
426
445
  end
427
446
 
@@ -617,6 +636,63 @@ HELP
617
636
  end
618
637
  end
619
638
 
639
+
640
+ class DetachedRoute
641
+ attr_reader :route, :preconfig
642
+
643
+ def initialize(route)
644
+ @route = route
645
+ w = route.send(:configure_iterator)
646
+ if w.respond_to? :instance
647
+ @preconfig = w
648
+ end
649
+ end
650
+
651
+ def build(graph, gather = true)
652
+ if route.is_a? Pacer::Filter::EmptyFilter
653
+ pipe = Pacer::Pipes::IdentityPipe.new
654
+ else
655
+ pipe = Pacer::Route.pipeline route
656
+ end
657
+ expando = Pacer::Pipes::ExpandablePipe.new
658
+ expando.enablePath true
659
+ expando.setStarts(Pacer::Pipes::EmptyIterator::INSTANCE)
660
+ pipe.setStarts expando
661
+ if preconfig
662
+ pipe = preconfig.instance pipe, graph
663
+ else
664
+ pipe = route.send(:configure_iterator, pipe, graph)
665
+ end
666
+ pipe = yield pipe if block_given?
667
+ if gather
668
+ g = Pacer::Pipes::GatherPipe.new
669
+ g.setStarts pipe
670
+ DetachedPipe.new(expando, g, true)
671
+ else
672
+ DetachedPipe.new(expando, pipe, false)
673
+ end
674
+ end
675
+ end
676
+
677
+ class DetachedPipe
678
+ attr_reader :expando, :pipe, :collection
679
+
680
+ def initialize(expando, pipe, collection)
681
+ @expando = expando
682
+ @pipe = pipe
683
+ @collection = collection
684
+ end
685
+
686
+ def read(element)
687
+ pipe.reset unless collection
688
+ expando.add element
689
+ if pipe.hasNext
690
+ pipe.next
691
+ elsif collection
692
+ []
693
+ end
694
+ end
695
+ end
620
696
  end
621
697
  end
622
698
  end
@@ -31,7 +31,7 @@ module Pacer
31
31
  def build_pipeline(route, start_pipe = nil, pipe = nil)
32
32
  pipe ||= start_pipe
33
33
  if labels.any?
34
- label_pipe = Pacer::Pipes::LabelCollectionFilterPipe.new labels
34
+ label_pipe = Pacer::Pipes::LabelCollectionFilterPipe.new labels.map(&:to_s)
35
35
  label_pipe.set_starts pipe if pipe
36
36
  Pacer.debug_pipes << { :name => labels.inspect, :start => pipe, :end => block_pipe } if Pacer.debug_pipes
37
37
  pipe = label_pipe
@@ -44,6 +44,10 @@ module Pacer
44
44
  labels.any? or super
45
45
  end
46
46
 
47
+ def extensions_only?
48
+ labels.empty? and super
49
+ end
50
+
47
51
  def to_s
48
52
  if labels.any?
49
53
  [labels.map { |l| l.to_sym.inspect }.join(', '), super].reject { |s| s == '' }.join ', '
@@ -134,15 +134,30 @@ module Pacer
134
134
  end
135
135
  end
136
136
 
137
+ # Not used for regular filtering, but useful to test an element against
138
+ # a complex set of conditions without having to build a route.
139
+ #
140
+ # Returns a proc that can be called with an element to test and returns
141
+ # true if the element matches the conditions.
142
+ def to_predicate
143
+ expando, pipe = build_pipeline(nil, com.tinkerpop.pipes.util.iterators.ExpandableIterator.new)
144
+ proc do |e|
145
+ expando.add e
146
+ pipe.next if pipe.hasNext
147
+ end
148
+ end
149
+
137
150
  def build_pipeline(route, start_pipe, pipe = nil)
138
- self.graph = route.graph
139
151
  pipe ||= start_pipe
140
- route_modules.each do |mod|
141
- extension_route = mod.route(Pacer::Route.empty(route))
142
- s, e = extension_route.send :build_pipeline
143
- s.setStarts(pipe) if pipe
144
- start_pipe ||= s
145
- pipe = e
152
+ if route
153
+ self.graph = route.graph
154
+ route_modules.each do |mod|
155
+ extension_route = mod.route(Pacer::Route.empty(route))
156
+ s, e = extension_route.send :build_pipeline
157
+ s.setStarts(pipe) if pipe
158
+ start_pipe ||= s
159
+ pipe = e
160
+ end
146
161
  end
147
162
  encoded_properties.each do |key, value|
148
163
  case value
@@ -161,6 +176,7 @@ module Pacer
161
176
  end
162
177
  end
163
178
  blocks.each do |block|
179
+ # Will work if route is nil.
164
180
  block_pipe = Pacer::Pipes::BlockFilterPipe.new(route, block)
165
181
  block_pipe.set_starts pipe if pipe
166
182
  Pacer.debug_pipes << { :name => 'block', :start => pipe, :end => block_pipe } if Pacer.debug_pipes
@@ -6,9 +6,20 @@ module Pacer
6
6
 
7
7
  def initialize(graph, vertex_extensions = [], edge_extensions = [])
8
8
  super()
9
- @graph = graph
10
- @vertex_wrapper = Pacer::Wrappers::WrapperSelector.build graph, :vertex, vertex_extensions || Set[]
11
- @edge_wrapper = Pacer::Wrappers::WrapperSelector.build graph, :edge, edge_extensions || Set[]
9
+ if graph.is_a? Array
10
+ @graph, @vertex_wrapper, @edge_wrapper = graph
11
+ else
12
+ @graph = graph
13
+ @vertex_wrapper = Pacer::Wrappers::WrapperSelector.build graph, :vertex, vertex_extensions || Set[]
14
+ @edge_wrapper = Pacer::Wrappers::WrapperSelector.build graph, :edge, edge_extensions || Set[]
15
+ end
16
+ end
17
+
18
+ def instance(pipe, g)
19
+ g ||= graph
20
+ p = PathWrappingPipe.new [g, vertex_wrapper, edge_wrapper]
21
+ p.setStarts pipe
22
+ p
12
23
  end
13
24
 
14
25
  def getCurrentPath
@@ -5,10 +5,21 @@ module Pacer
5
5
 
6
6
  def initialize(graph, element_type = nil, extensions = [])
7
7
  super()
8
- @graph = graph
9
- @element_type = element_type
10
- @extensions = extensions || []
11
- @wrapper = Pacer::Wrappers::WrapperSelector.build graph, element_type, @extensions
8
+ if graph.is_a? Array
9
+ @graph, @wrapper = graph
10
+ else
11
+ @graph = graph
12
+ @element_type = element_type
13
+ @extensions = extensions || []
14
+ @wrapper = Pacer::Wrappers::WrapperSelector.build graph, element_type, @extensions
15
+ end
16
+ end
17
+
18
+ def instance(pipe, g)
19
+ g ||= graph
20
+ p = WrappingPipe.new [g, wrapper]
21
+ p.setStarts pipe
22
+ p
12
23
  end
13
24
 
14
25
  def getSideEffect
data/lib/pacer/pipes.rb CHANGED
@@ -15,6 +15,21 @@ module Pacer
15
15
  import com.tinkerpop.pipes.transform.PropertyPipe
16
16
  import com.tinkerpop.pipes.transform.PropertyMapPipe
17
17
 
18
+ import com.xnlogic.pacer.pipes.BlackboxPipeline
19
+ import com.xnlogic.pacer.pipes.EdgesPipe
20
+ import com.xnlogic.pacer.pipes.CollectionFilterPipe
21
+ import com.xnlogic.pacer.pipes.ExpandablePipe
22
+ SimpleExpandablePipe = com.tinkerpop.pipes.util.ExpandablePipe
23
+ import com.xnlogic.pacer.pipes.IdCollectionFilterPipe
24
+ import com.xnlogic.pacer.pipes.IsEmptyPipe
25
+ import com.xnlogic.pacer.pipes.LabelCollectionFilterPipe
26
+ import com.xnlogic.pacer.pipes.IsUniquePipe
27
+ import com.xnlogic.pacer.pipes.VerticesPipe
28
+ import com.xnlogic.pacer.pipes.LabelPrefixPipe
29
+ import com.xnlogic.pacer.pipes.NeverPipe
30
+ import com.tinkerpop.pipes.util.iterators.EmptyIterator
31
+ import com.tinkerpop.pipes.transform.GatherPipe
32
+
18
33
  IN = com.tinkerpop.blueprints.Direction::IN
19
34
  OUT = com.tinkerpop.blueprints.Direction::OUT
20
35
  BOTH = com.tinkerpop.blueprints.Direction::BOTH
@@ -33,32 +48,19 @@ end
33
48
 
34
49
  require 'pacer/pipe/ruby_pipe'
35
50
 
36
- require 'pacer/pipe/vertices_pipe'
37
- require 'pacer/pipe/edges_pipe'
38
-
39
- require 'pacer/pipe/never_pipe'
40
51
  require 'pacer/pipe/multi_pipe'
41
52
  require 'pacer/pipe/block_filter_pipe'
42
- require 'pacer/pipe/collection_filter_pipe'
43
53
  require 'pacer/pipe/enumerable_pipe'
44
- require 'pacer/pipe/expandable_pipe'
45
54
  require 'pacer/pipe/loop_pipe'
46
55
  require 'pacer/pipe/process_pipe'
47
56
  require 'pacer/pipe/visitor_pipe'
48
57
  require 'pacer/pipe/simple_visitor_pipe'
49
- require 'pacer/pipe/is_unique_pipe'
50
- require 'pacer/pipe/is_empty_pipe'
51
58
  require 'pacer/pipe/stream_sort_pipe'
52
59
  require 'pacer/pipe/stream_uniq_pipe'
53
60
  require 'pacer/pipe/type_filter_pipe'
54
- require 'pacer/pipe/label_collection_filter_pipe'
55
- require 'pacer/pipe/id_collection_filter_pipe'
56
- require 'pacer/pipe/label_prefix_pipe'
57
61
 
58
62
  require 'pacer/pipe/property_comparison_pipe'
59
63
 
60
- require 'pacer/pipe/blackbox_pipeline'
61
-
62
64
  require 'pacer/pipe/unary_transform_pipe'
63
65
  require 'pacer/pipe/cross_product_transform_pipe'
64
66
 
data/lib/pacer/route.rb CHANGED
@@ -18,6 +18,7 @@ module Pacer
18
18
  # @return [Pacer::Pipes::BlackboxPipeline] an instantiated pipeline
19
19
  def pipeline(route)
20
20
  s, e = route.send(:build_pipeline)
21
+ e = yield e if block_given?
21
22
  if s.equal?(e)
22
23
  s
23
24
  else
@@ -44,7 +44,7 @@ module Pacer
44
44
  def attach_pipe(pipe)
45
45
  if element_type == :vertex or element_type == :edge or element_type == :mixed
46
46
  wrapped = Pacer::Pipes::WrappingPipe.new graph, element_type, extensions
47
- wrapped.setStarts pipe
47
+ wrapped.setStarts pipe if pipe
48
48
  as_pipe = AsPipe.new(wrapped, vars, variable_name)
49
49
  unwrapped = Pacer::Pipes::UnwrappingPipe.new
50
50
  unwrapped.setStarts as_pipe
@@ -14,7 +14,7 @@ module Pacer
14
14
  def unique?
15
15
  check = nil
16
16
  is_unique { |pipe| check = pipe }.each do
17
- return false unless check.unique?
17
+ return false unless check.isUnique
18
18
  end
19
19
  true
20
20
  end
@@ -30,7 +30,7 @@ module Pacer
30
30
 
31
31
  def attach_pipe(end_pipe)
32
32
  checked = Pacer::Pipes::IsUniquePipe.new
33
- checked.setStarts end_pipe
33
+ checked.setStarts end_pipe if end_pipe
34
34
  on_build_pipe.call checked if on_build_pipe
35
35
  checked
36
36
  end
@@ -44,7 +44,7 @@ module Pacer
44
44
  def clone(conf = {})
45
45
  back.chain_route({transform: Branch,
46
46
  branches: branches.clone,
47
- element_type: back.element_type,
47
+ element_type: element_type,
48
48
  conds: conds.clone,
49
49
  extensions: extensions.clone,
50
50
  split_pipe_class: split_pipe_class,
@@ -28,7 +28,7 @@ module Pacer
28
28
  else
29
29
  fail ClientError, 'Can not look up elements without the element_type'
30
30
  end
31
- pipe.setStarts end_pipe
31
+ pipe.setStarts end_pipe if end_pipe
32
32
  pipe
33
33
  end
34
34
  end
@@ -29,7 +29,7 @@ module Pacer
29
29
 
30
30
  def attach_pipe(end_pipe)
31
31
  pipe = PathTreePipe.new compare
32
- pipe.setStarts end_pipe
32
+ pipe.setStarts end_pipe if end_pipe
33
33
  pipe
34
34
  end
35
35
 
@@ -86,7 +86,7 @@ HELP
86
86
  def attach_pipe(end_pipe)
87
87
  if @enter and @reduce and @leave
88
88
  pipe = ReducerPipe.new self, @enter, @reduce, @leave
89
- pipe.setStarts end_pipe
89
+ pipe.setStarts end_pipe if end_pipe
90
90
  pipe
91
91
  else
92
92
  fail Pacer::ClientError, 'enter, reduce, and leave must all be specified for reducers'
@@ -1,10 +1,18 @@
1
1
  module Pacer
2
2
  module Routes
3
3
  module RouteOperations
4
+ # Arity 2 uses custom sort logic. Arity 1 uses sort_by logic.
4
5
  def sort_section(section = nil, &block)
5
- chain_route transform: :sort_section, sort_by_block: block, section: section
6
+ if not block
7
+ chain_route transform: :sort_section, section: section
8
+ elsif block.arity == 2
9
+ chain_route transform: :sort_section, custom_sort_block: block, section: section
10
+ else
11
+ chain_route transform: :sort_section, sort_by_block: block, section: section
12
+ end
6
13
  end
7
14
 
15
+ # Deprecated: use sort_section
8
16
  def custom_sort_section(section = nil, &block)
9
17
  chain_route transform: :sort_section, custom_sort_block: block, section: section
10
18
  end
@@ -40,27 +48,27 @@ module Pacer
40
48
  attr_reader :pf_1, :pf_2, :to_sort, :to_emit, :section, :route
41
49
  attr_reader :getPathToHere
42
50
 
43
- def initialize(route, section, pipe_function)
51
+ def initialize(route, visitor_pipe, pipe_function)
44
52
  super()
45
53
  @to_emit = []
46
- @section = section
54
+ @visitor_pipe = visitor_pipe
47
55
  @route = route
48
56
  @to_sort = []
49
57
  @paths = []
50
- if section
51
- section.visitor = self
58
+ if visitor_pipe
59
+ visitor_pipe.visitor = self
52
60
  else
53
61
  on_element nil
54
62
  end
55
63
  if pipe_function
56
64
  if pipe_function.arity == 1
57
65
  @pf_1 = pipe_function
58
- section.use_on_element = false
66
+ visitor_pipe.use_on_element = false
59
67
  else
60
68
  @pf_2 = pipe_function
61
69
  end
62
70
  else
63
- section.use_on_element = false
71
+ visitor_pipe.use_on_element = false
64
72
  end
65
73
  end
66
74
 
@@ -119,8 +127,8 @@ module Pacer
119
127
  class CustomSortPipe < SortBySectionPipe
120
128
  attr_reader :sort_block
121
129
 
122
- def initialize(route, section, sort_block, graph, wrapper)
123
- super route, section, nil
130
+ def initialize(route, visitor_pipe, sort_block, graph, wrapper)
131
+ super route, visitor_pipe, nil
124
132
  @sort_block = sort_block
125
133
  @graph = graph
126
134
  @wrapper = wrapper
@@ -1,5 +1,6 @@
1
1
  module Pacer
2
2
  module Routes::RouteOperations
3
+ # Deprecated: use sort_section instead.
3
4
  def stream_sort(buffer = 1000, silo = 100)
4
5
  chain_route :transform => :stream_sort, :buffer => buffer, :silo => silo
5
6
  end
@@ -1,5 +1,6 @@
1
1
  module Pacer
2
2
  module Routes::RouteOperations
3
+ # Deprecated: use uniq_section instead.
3
4
  def stream_uniq(buffer = 1000)
4
5
  chain_route :transform => :stream_uniq, :buffer => buffer
5
6
  end
@@ -15,7 +15,7 @@ module Pacer
15
15
 
16
16
  def attach_pipe(end_pipe)
17
17
  pipe = Pacer::Pipes::PathWrappingPipe.new(graph)
18
- pipe.setStarts end_pipe
18
+ pipe.setStarts end_pipe if end_pipe
19
19
  pipe
20
20
  end
21
21
  end
data/lib/pacer/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Pacer
2
- VERSION = "2.0.10.pre"
2
+ VERSION = "2.0.12"
3
3
  # Clients may optionally define the following constants in the Pacer namespace:
4
4
  # - LOAD_JARS : set to false to manage jar loading in the client. Be sure to load the jars defined in JARFILES.
5
5
  # - LOCKJAR_LOCK_OPTS : set some options to be passed to LockJar.lock (ie. :lockfile, :download_artifacts, :local_repo)
@@ -18,10 +18,14 @@ module Pacer
18
18
 
19
19
  def will_visit!
20
20
  @visitor_count = visitor_count + 1
21
+ visitor_count - 1
21
22
  end
22
23
 
23
- def section_visitor
24
- section_visitors.pop
24
+ def section_visitor!(visitor_num)
25
+ vpipes = Thread.current["visitors_#{object_id}"]
26
+ vpipe = vpipes[visitor_num]
27
+ vpipes[visitor_num] = nil
28
+ vpipe
25
29
  end
26
30
 
27
31
  protected
@@ -34,12 +38,15 @@ module Pacer
34
38
  attr_reader :section_visitors
35
39
 
36
40
  def attach_pipe(end_pipe)
41
+ # With detached pipes, pipe construction happens in
42
+ # multiple threads, multiple times.
37
43
  pipe = end_pipe
38
- @section_visitors = (1..visitor_count).map do
44
+ vpipes = (1..visitor_count).map do
39
45
  pipe = Pacer::Pipes::SimpleVisitorPipe.new Pacer::Wrappers::WrapperSelector.build(graph, element_type, extensions), graph
40
46
  pipe.setStarts end_pipe if end_pipe
41
47
  end_pipe = pipe
42
48
  end
49
+ Thread.current["visitors_#{object_id}"] = vpipes
43
50
  pipe
44
51
  end
45
52
 
@@ -2,7 +2,7 @@ module Pacer
2
2
  module Visitors
3
3
  # This module is mixed in to the route that actually refers to this section.
4
4
  module VisitsSection
5
- attr_reader :section, :section_route
5
+ attr_reader :section, :section_route, :visitor_num
6
6
 
7
7
  def section=(section)
8
8
  if section.is_a? Symbol
@@ -14,14 +14,14 @@ module Pacer
14
14
  else
15
15
  raise ArgumentError, "Unknown section #{ section }. Provide either a name or a route created with the #section methed."
16
16
  end
17
- @section_route.will_visit!
18
- @section_route
17
+ @visitor_num = section_route.will_visit!
18
+ section_route
19
19
  end
20
20
 
21
21
  protected
22
22
 
23
23
  def section_visitor
24
- section_route.section_visitor if section_route
24
+ section_route.section_visitor!(visitor_num) if visitor_num
25
25
  end
26
26
 
27
27
  def section_visitor_target
data/lib/pacer-ext.jar ADDED
Binary file
data/lib/pacer.rb CHANGED
@@ -23,6 +23,7 @@ require 'pp'
23
23
  require 'rubygems'
24
24
  require 'lock_jar'
25
25
  require 'pacer/support/lock_jar'
26
+ require 'pacer-ext.jar'
26
27
 
27
28
  if (not defined? Pacer::LOAD_JARS) or Pacer::LOAD_JARS == true
28
29
  bundle_jarfiles = LockJar.register_bundled_jarfiles # defined in pacer/support/lock_jar.rb
data/pacer.gemspec CHANGED
@@ -13,10 +13,11 @@ Gem::Specification.new do |s|
13
13
  s.summary = %Q{A very efficient and easy to use graph traversal engine.}
14
14
  s.description = %Q{Pacer defines composeable routes through a graph and then traverses them very quickly.}
15
15
 
16
- s.files = `git ls-files`.split("\n")
16
+ s.files = `git ls-files`.split("\n") + ['lib/pacer-ext.jar']
17
17
  s.test_files = `git ls-files -- spec/*`.split("\n")
18
18
  s.require_paths = ['lib']
19
19
  s.add_dependency "lock_jar", "~> 0.10.2"
20
20
  s.add_development_dependency 'xn_gem_release_tasks'
21
+ s.add_development_dependency "rake-compiler", "~> 0.9.2"
21
22
  end
22
23