pacer 0.9.1.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. data/.autotest +8 -0
  2. data/.document +5 -0
  3. data/.gitignore +26 -0
  4. data/.rspec +1 -0
  5. data/.rvmrc +0 -0
  6. data/CONTRIBUTORS +5 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +24 -0
  9. data/README.md +187 -0
  10. data/Rakefile +49 -0
  11. data/autotest/discover.rb +1 -0
  12. data/bin/autospec +16 -0
  13. data/bin/autotest +16 -0
  14. data/bin/rake +16 -0
  15. data/bin/rcov +16 -0
  16. data/bin/rspec +16 -0
  17. data/bin/yard +16 -0
  18. data/bin/yardoc +16 -0
  19. data/lib/pacer/blueprints/extensions.rb +77 -0
  20. data/lib/pacer/blueprints/multi_graph.rb +121 -0
  21. data/lib/pacer/blueprints/ruby_graph.rb +199 -0
  22. data/lib/pacer/blueprints/tg.rb +100 -0
  23. data/lib/pacer/blueprints.rb +4 -0
  24. data/lib/pacer/core/graph/edges_route.rb +92 -0
  25. data/lib/pacer/core/graph/element_route.rb +171 -0
  26. data/lib/pacer/core/graph/graph_index_route.rb +48 -0
  27. data/lib/pacer/core/graph/graph_route.rb +55 -0
  28. data/lib/pacer/core/graph/mixed_route.rb +96 -0
  29. data/lib/pacer/core/graph/vertices_route.rb +220 -0
  30. data/lib/pacer/core/graph.rb +13 -0
  31. data/lib/pacer/core/route.rb +502 -0
  32. data/lib/pacer/core/side_effect.rb +11 -0
  33. data/lib/pacer/core.rb +8 -0
  34. data/lib/pacer/exceptions.rb +11 -0
  35. data/lib/pacer/extensions/block_filter_element.rb +22 -0
  36. data/lib/pacer/extensions.rb +6 -0
  37. data/lib/pacer/filter/block_filter.rb +31 -0
  38. data/lib/pacer/filter/collection_filter.rb +109 -0
  39. data/lib/pacer/filter/empty_filter.rb +70 -0
  40. data/lib/pacer/filter/future_filter.rb +68 -0
  41. data/lib/pacer/filter/index_filter.rb +30 -0
  42. data/lib/pacer/filter/loop_filter.rb +95 -0
  43. data/lib/pacer/filter/object_filter.rb +55 -0
  44. data/lib/pacer/filter/property_filter/edge_filters.rb +93 -0
  45. data/lib/pacer/filter/property_filter/filters.rb +269 -0
  46. data/lib/pacer/filter/property_filter.rb +111 -0
  47. data/lib/pacer/filter/random_filter.rb +13 -0
  48. data/lib/pacer/filter/range_filter.rb +104 -0
  49. data/lib/pacer/filter/uniq_filter.rb +12 -0
  50. data/lib/pacer/filter/where_filter/node_visitor.rb +280 -0
  51. data/lib/pacer/filter/where_filter.rb +47 -0
  52. data/lib/pacer/filter.rb +17 -0
  53. data/lib/pacer/function_resolver.rb +43 -0
  54. data/lib/pacer/graph/edge_mixin.rb +127 -0
  55. data/lib/pacer/graph/element_mixin.rb +202 -0
  56. data/lib/pacer/graph/graph_indices_mixin.rb +93 -0
  57. data/lib/pacer/graph/graph_mixin.rb +361 -0
  58. data/lib/pacer/graph/graph_transactions_mixin.rb +207 -0
  59. data/lib/pacer/graph/index_mixin.rb +30 -0
  60. data/lib/pacer/graph/vertex_mixin.rb +119 -0
  61. data/lib/pacer/graph.rb +14 -0
  62. data/lib/pacer/pipe/blackbox_pipeline.rb +48 -0
  63. data/lib/pacer/pipe/block_filter_pipe.rb +38 -0
  64. data/lib/pacer/pipe/collection_filter_pipe.rb +10 -0
  65. data/lib/pacer/pipe/cross_product_transform_pipe.rb +48 -0
  66. data/lib/pacer/pipe/enumerable_pipe.rb +30 -0
  67. data/lib/pacer/pipe/expandable_pipe.rb +63 -0
  68. data/lib/pacer/pipe/id_collection_filter_pipe.rb +33 -0
  69. data/lib/pacer/pipe/is_empty_pipe.rb +30 -0
  70. data/lib/pacer/pipe/is_unique_pipe.rb +61 -0
  71. data/lib/pacer/pipe/label_collection_filter_pipe.rb +21 -0
  72. data/lib/pacer/pipe/label_prefix_pipe.rb +21 -0
  73. data/lib/pacer/pipe/loop_pipe.rb +86 -0
  74. data/lib/pacer/pipe/map_pipe.rb +36 -0
  75. data/lib/pacer/pipe/never_pipe.rb +9 -0
  76. data/lib/pacer/pipe/process_pipe.rb +37 -0
  77. data/lib/pacer/pipe/property_comparison_pipe.rb +40 -0
  78. data/lib/pacer/pipe/ruby_pipe.rb +25 -0
  79. data/lib/pacer/pipe/simple_visitor_pipe.rb +43 -0
  80. data/lib/pacer/pipe/stream_sort_pipe.rb +84 -0
  81. data/lib/pacer/pipe/stream_uniq_pipe.rb +33 -0
  82. data/lib/pacer/pipe/type_filter_pipe.rb +22 -0
  83. data/lib/pacer/pipe/unary_transform_pipe.rb +59 -0
  84. data/lib/pacer/pipe/variable_store_iterator_wrapper.rb +26 -0
  85. data/lib/pacer/pipe/visitor_pipe.rb +67 -0
  86. data/lib/pacer/pipes.rb +61 -0
  87. data/lib/pacer/route/mixin/bulk_operations.rb +52 -0
  88. data/lib/pacer/route/mixin/route_operations.rb +107 -0
  89. data/lib/pacer/route/mixin/variable_route_module.rb +26 -0
  90. data/lib/pacer/route/mixins.rb +3 -0
  91. data/lib/pacer/route.rb +228 -0
  92. data/lib/pacer/routes.rb +6 -0
  93. data/lib/pacer/side_effect/aggregate.rb +31 -0
  94. data/lib/pacer/side_effect/counted.rb +30 -0
  95. data/lib/pacer/side_effect/group_count.rb +44 -0
  96. data/lib/pacer/side_effect/is_unique.rb +32 -0
  97. data/lib/pacer/side_effect/section.rb +25 -0
  98. data/lib/pacer/side_effect/visitor.rb +37 -0
  99. data/lib/pacer/side_effect.rb +11 -0
  100. data/lib/pacer/support/array_list.rb +28 -0
  101. data/lib/pacer/support/enumerable.rb +100 -0
  102. data/lib/pacer/support/hash.rb +9 -0
  103. data/lib/pacer/support/iterator_mixins.rb +110 -0
  104. data/lib/pacer/support/native_exception.rb +22 -0
  105. data/lib/pacer/support/proc.rb +16 -0
  106. data/lib/pacer/support.rb +10 -0
  107. data/lib/pacer/transform/cap.rb +50 -0
  108. data/lib/pacer/transform/gather.rb +9 -0
  109. data/lib/pacer/transform/has_count_cap.rb +41 -0
  110. data/lib/pacer/transform/join.rb +181 -0
  111. data/lib/pacer/transform/map.rb +23 -0
  112. data/lib/pacer/transform/path.rb +50 -0
  113. data/lib/pacer/transform/process.rb +23 -0
  114. data/lib/pacer/transform/scatter.rb +23 -0
  115. data/lib/pacer/transform/sort_section.rb +103 -0
  116. data/lib/pacer/transform/stream_sort.rb +21 -0
  117. data/lib/pacer/transform/stream_uniq.rb +21 -0
  118. data/lib/pacer/transform.rb +16 -0
  119. data/lib/pacer/utils/graph_analysis.rb +112 -0
  120. data/lib/pacer/utils/trie.rb +93 -0
  121. data/lib/pacer/utils/tsort.rb +65 -0
  122. data/lib/pacer/utils/y_files.rb +127 -0
  123. data/lib/pacer/utils.rb +10 -0
  124. data/lib/pacer/version.rb +13 -0
  125. data/lib/pacer/wrappers/edge_wrapper.rb +51 -0
  126. data/lib/pacer/wrappers/element_wrapper.rb +78 -0
  127. data/lib/pacer/wrappers/new_element.rb +106 -0
  128. data/lib/pacer/wrappers/vertex_wrapper.rb +51 -0
  129. data/lib/pacer/wrappers.rb +19 -0
  130. data/lib/pacer-0.9.1.1-standalone.jar +0 -0
  131. data/lib/pacer.rb +290 -0
  132. data/pacer.gemspec +30 -0
  133. data/pom/standalone.xml +22 -0
  134. data/pom.xml +124 -0
  135. data/samples/grateful-dead.xml +26380 -0
  136. data/samples/grateful_dead.rb +63 -0
  137. data/samples/profile.rb +15 -0
  138. data/spec/data/grateful-dead.xml +26380 -0
  139. data/spec/data/pacer.graphml +319 -0
  140. data/spec/pacer/blueprints/dex_spec.rb +172 -0
  141. data/spec/pacer/blueprints/neo4j_spec.rb +177 -0
  142. data/spec/pacer/blueprints/tg_spec.rb +128 -0
  143. data/spec/pacer/core/graph/edges_route_spec.rb +52 -0
  144. data/spec/pacer/core/graph/element_route_spec.rb +46 -0
  145. data/spec/pacer/core/graph/graph_route_spec.rb +94 -0
  146. data/spec/pacer/core/graph/vertices_route_spec.rb +169 -0
  147. data/spec/pacer/core/route_spec.rb +197 -0
  148. data/spec/pacer/filter/collection_filter_spec.rb +19 -0
  149. data/spec/pacer/filter/empty_filter_spec.rb +29 -0
  150. data/spec/pacer/filter/future_filter_spec.rb +97 -0
  151. data/spec/pacer/filter/loop_filter_spec.rb +31 -0
  152. data/spec/pacer/filter/property_filter_spec.rb +111 -0
  153. data/spec/pacer/filter/random_filter_spec.rb +17 -0
  154. data/spec/pacer/filter/uniq_filter_spec.rb +18 -0
  155. data/spec/pacer/filter/where_filter_spec.rb +93 -0
  156. data/spec/pacer/graph/edge_mixin_spec.rb +116 -0
  157. data/spec/pacer/graph/element_mixin_spec.rb +297 -0
  158. data/spec/pacer/graph/graph_mixin_spec.rb +538 -0
  159. data/spec/pacer/graph/index_mixin_spec.rb +0 -0
  160. data/spec/pacer/graph/vertex_mixin_spec.rb +192 -0
  161. data/spec/pacer/pipe/block_filter_pipe_spec.rb +0 -0
  162. data/spec/pacer/pipe/labels_filter_pipe_spec.rb +0 -0
  163. data/spec/pacer/pipe/ruby_pipe_spec.rb +0 -0
  164. data/spec/pacer/pipe/type_filter_pipe_spec.rb +0 -0
  165. data/spec/pacer/route/mixin/base_spec.rb +419 -0
  166. data/spec/pacer/route/mixin/bulk_operations_spec.rb +30 -0
  167. data/spec/pacer/route/mixin/route_operations_spec.rb +127 -0
  168. data/spec/pacer/support/array_list_spec.rb +0 -0
  169. data/spec/pacer/support/enumerable_spec.rb +115 -0
  170. data/spec/pacer/transform/join_spec.rb +138 -0
  171. data/spec/pacer/transform/path_spec.rb +54 -0
  172. data/spec/pacer/utils/tsort_spec.rb +89 -0
  173. data/spec/pacer/wrapper/edge_wrapper_spec.rb +33 -0
  174. data/spec/pacer/wrapper/element_wrapper_spec.rb +169 -0
  175. data/spec/pacer/wrapper/vertex_wrapper_spec.rb +33 -0
  176. data/spec/pacer_spec.rb +0 -0
  177. data/spec/spec_helper.rb +91 -0
  178. data/spec/support/contexts.rb +14 -0
  179. data/spec/support/graph_runner.rb +142 -0
  180. data/spec/support/matchers.rb +19 -0
  181. data/spec/support/use_transactions.rb +31 -0
  182. data/spec/tackle/simple_mixin.rb +21 -0
  183. data/spec/tackle/tinkerpop_graph_mixins.rb +60 -0
  184. metadata +364 -0
data/.autotest ADDED
@@ -0,0 +1,8 @@
1
+ require 'autotest/bundler'
2
+ Autotest.add_hook(:initialize) {|at|
3
+ at.add_exception %r{\.git} # ignore Version Control System
4
+ at.add_exception %r{/\.} # ignore any file that starts with a . (dot)
5
+ at.add_exception %r{^\./tmp} # ignore temp files, lest autotest will run again, and again...
6
+ at.add_exception %r{^\./(coverage|data|samples|pkg|\.yardoc)}
7
+ nil
8
+ }
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,26 @@
1
+ # rcov generated
2
+ coverage
3
+
4
+ # rdoc generated
5
+ rdoc
6
+
7
+ # yard generated
8
+ doc
9
+ .yardoc
10
+
11
+ # bundler
12
+ .bundle
13
+ Gemfile.lock
14
+
15
+ # jeweler generated
16
+ pkg
17
+
18
+ # Project specific
19
+ *.graph
20
+ *.db
21
+ *.graphml
22
+ pkg
23
+ tmp
24
+ lib/*.jar
25
+ jdex.log
26
+ bin
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ #--format d
data/.rvmrc ADDED
File without changes
data/CONTRIBUTORS ADDED
@@ -0,0 +1,5 @@
1
+ Maintainer:
2
+ Darrick Wiebe <dw@xnlogic.com>
3
+
4
+ Contributors:
5
+ *
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # A sample Gemfile
2
+ source "http://rubygems.org"
3
+
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2010, TinkerPop [http://tinkerpop.com]
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ * Neither the name of the TinkerPop nor the
12
+ names of its contributors may be used to endorse or promote products
13
+ derived from this software without specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL TINKERPOP BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,187 @@
1
+ # Pacer
2
+
3
+ Pacer is a JRuby library that enables very expressive graph traversals.
4
+
5
+ It currently supports 2 major graph database: [Neo4j](http://neo4j.org) and [Dex](http://www.sparsity-technologies.com/dex) using the [Tinkerpop](http://tinkerpop.com) graphdb stack. Plus there's a very convenient in-memory graph called TinkerGraph which is part of [Blueprints](http://blueprints.tinkerpop.com).
6
+
7
+ Pacer allows you to create, modify and traverse graphs using very fast and memory efficient stream processing thanks to the very cool [Pipes](http://pipes.tinkerpop.com) library. That also means that almost all processing is done in pure Java, so when it comes the usual Ruby expressiveness vs. speed problem, you can have your cake and eat it too, it's very fast!
8
+
9
+ ## Mailing List
10
+
11
+ With the release of 0.8.1, I just set up a brand new [pacer google
12
+ group](http://groups.google.com/group/pacer-users?lnk=gcimv). Join and
13
+ let's get the conversation going!
14
+
15
+ ## Documentation
16
+
17
+ Pacer is documented with a comprehensive RSpec test suite and with a
18
+ thorough YARD documentation. [Dig in!](http://rubydoc.info/github/pangloss/pacer/develop/frames)
19
+
20
+ If you like, you can also use the documentation locally via
21
+
22
+ gem install yard
23
+ yard server
24
+
25
+ ## Installation
26
+
27
+ The easiest way to get Pacer is `gem install pacer`.
28
+
29
+ If you want to hack on Pacer, you'll need to have
30
+ [maven](http://maven.apache.org/) installed (I recommend `brew install
31
+ maven`), then use `rake jar` to set up maven's pom.xml file and run the
32
+ maven build script.
33
+
34
+ *Note* Pacer currently relies on some features that are not yet in the
35
+ main Pipes repo. You will need to build the `develop` branch of [my pipes
36
+ repo](https://github.com/pangloss/pipes) by cloning it and running `mvn
37
+ clean install`.
38
+
39
+ ## Graph Database Support
40
+
41
+ The Tinkerpop suite supports a number of graph data stores. They are all
42
+ compatible with Pacer, but I have not yet implemented the simple
43
+ adapters Pacer needs to use them yet. Here is the list of what's
44
+ supported so far:
45
+
46
+ * TinkerGraph - In-memory graph db, built in and ready to use without
47
+ additional dependencies.
48
+ * [Neo4J](http://neo4j.org) - The industry-leading graph db. `gem
49
+ install pacer-neo4j`
50
+ [pangloss/pacer-neo4j](https://github.com/pangloss/pacer-neo4j)
51
+ * [Dex](http://sparsity-technologies.com) - A very fast, relatively new graph db. `gem
52
+ install pacer-dex`
53
+ [pangloss/pacer-dex](https://github.com/pangloss/pacer-dex)
54
+
55
+ You can run any or all of the above graph databases. Pacer supports
56
+ running them simultaneuosly and even supports having many of any given
57
+ type open at once.
58
+
59
+ ### Interoperation with the neo4j gem
60
+
61
+ Pacer can work together with other Ruby GraphDB libraries, too. The
62
+ first functioning example is with theo neo4j gem. Hopefully more will
63
+ follow soon as I find out about them or get requests to support them.
64
+
65
+ To use Pacer together with the neo4j gem, get your Pacer graph instance
66
+ as follows:
67
+
68
+ ```ruby
69
+ require 'neo4j'
70
+ require 'pacer-neo4j'
71
+ Neo4j.db.start
72
+ graph = Pacer.neo4j(Neo4j.db.graph)
73
+ ```
74
+
75
+ After that, you can continue to use the graph as normal with *both*
76
+ gems. Any update that's committed with one gem will be visible
77
+ immediately to the other because they are now both pointing to the same
78
+ Java graphdb instance.
79
+
80
+ ### A note on safely exiting
81
+
82
+ Some databases need to be shutdown cleanly when the program exits. You
83
+ can shut a database down anytime by calling `graph.shutdown`, but you
84
+ don't need to worry about it much. Pacer uses Ruby's `at_exit` hook to
85
+ automatically shut down all open databases!
86
+
87
+ ## Example traversals
88
+
89
+ Friend recommendation algorithm expressed in basic traversal functions:
90
+
91
+ ```ruby
92
+ friends = person.out_e(:friend).in_v(:type => 'person')
93
+ friends.out_e(:friend).in_v(:type => 'person').except(friends).except(person).most_frequent(0...10)
94
+ ```
95
+
96
+ or using Pacer's route extensions to create your own query methods:
97
+
98
+ ```ruby
99
+ person.friends.friends.except(person.friends).except(person).most_frequent(0...10)
100
+ ```
101
+
102
+ or to take it one step further:
103
+
104
+ ```ruby
105
+ person.recommended_friends
106
+ ```
107
+
108
+ ## Create and populate a graph
109
+
110
+ To get started, you need to know just a few methods. First, open up a graph (if one doesn't exist it will be automatically created) and add some vertices to it:
111
+
112
+ ```ruby
113
+ dex = Pacer.dex '/tmp/dex_demo'
114
+ pangloss = dex.create_vertex :name => 'pangloss', :type => 'user'
115
+ okram = dex.create_vertex :name => 'okram', :type => 'user'
116
+ group = dex.create_vertex :name => 'Tinkerpop', :type => 'group'
117
+ ```
118
+
119
+ Now, let's see what we've got:
120
+
121
+ ```ruby
122
+ dex.v
123
+ ```
124
+
125
+ produces:
126
+
127
+ ```ruby
128
+ #<V[1024]> #<V[1025]> #<V[1026]>
129
+ Total: 3
130
+ => #<GraphV>
131
+ ```
132
+
133
+ There are our vertices. Let's look their properties:
134
+
135
+ ```ruby
136
+ dex.v.properties
137
+
138
+ {"name"=>"pangloss", "type"=>"user"} {"name"=>"okram", "type"=>"user"}
139
+ {"name"=>"Tinkerpop", "type"=>"group"}
140
+ Total: 3
141
+ => #<GraphV -> Obj-Map>
142
+ ```
143
+
144
+ Now let's put an edge between them:
145
+
146
+ ```ruby
147
+ dex.create_edge okram, pangloss, :inspired
148
+ => #<E[2048]:1025-inspired-1024>
149
+ ```
150
+
151
+ That's great for creating an edge but what if I've got lots to create? Try this method instead which can add edges to the cross product of all vertices in one route with all vertices in the other:
152
+
153
+ ```ruby
154
+ group.add_edges_to :member, dex.v(:type => 'user')
155
+
156
+ #<E[4097]:1026-member-1024> #<E[4098]:1026-member-1025>
157
+ Total: 2
158
+ => #<Obj 2 ids -> lookup>
159
+ ```
160
+
161
+ There is plenty more to see as well! Please dig into the code and the spec suite to find loads of examples and edge cases. And if you think of a case that I've missed, I'll greatly appreciate your contributions!
162
+
163
+ ## Design Philosophy
164
+
165
+ I want Pacer and its ecosystem to become a repository for real implementations of ideas, best practices and techniques for streaming data manipulation. I've got lots of ideas that I'd like to add, and although Pacer seems to be quite rock solid right now -- and I am using it in limited production environments -- it is still in flux. If we find a better way to do something, we're going to do it that way even if that means breaking changes from one release to another.
166
+
167
+ Once Pacer matures further, a decision will be made to 'lock it down' at least a little more, hopefully there will be a community in place by then to help determine the right time for that to happen!
168
+
169
+ ## Pluggable Architecture
170
+
171
+ Pacer is meant to be extensible and is built on a very modular architecture. Nearly every chainable route method is actually implemented in an independent module that is plugged into the route only when it's in use. That allows great flexibility in adding methods to routes without clogging up the method namespace. It also makes it natural to make pacer plugin gems.
172
+
173
+ There are lots of examples of route extensions right inside Pacer. Have a look at the [lib/pacer/filter](https://github.com/pangloss/pacer/tree/develop/lib/pacer/filter), [side_effect](https://github.com/pangloss/pacer/tree/develop/lib/pacer/side_effect) and [transform](https://github.com/pangloss/pacer/tree/develop/lib/pacer/transform) folders to see the modules that are built into Pacer. They vary widely in complexity, so take a look around.
174
+
175
+ If you want to add a traversal technique to Pacer, you can fork Pacer and send me a pull request or just create your own pacer-&lt;feature name&gt; plugin! To see how to build your own Pacer plugin, see my [example pacer-bloomfilter plugin](https://github.com/pangloss/pacer-bloomfilter) which also has a readme file that goes into considerable detail on the process of creating plugins and provides some additional usage examples as well.
176
+
177
+ As a side note, don't worry about any magic happening behind the scenes to discover or automatically load pacer plugins, there is none of that! If you want to use a pacer plugin, treat it like any other gem, add it to your Gemfile (if that's what you use) and <code>require</code> the gem as normal if you need it.
178
+
179
+ ## Gremlin
180
+
181
+ If you're already familiar with [Gremlin](http://gremlin.tinkerpop.com), please look at my [Introducing Pacer](http://ofallpossibleworlds.wordpress.com/2010/12/19/introducing-pacer) post for a simple introduction and explanation of how Pacer is at once similar to and quite different from Gremlin, the project that inspired it. That post is a little out of date at this point since it refers to the original version of Gremlin. Groovy Gremlin is the latest version, inspired in turn by Pacer!
182
+
183
+ A great introduction to the underlying concept of pipes can be found in Marko Rodriguez' post [On the Nature of Pipes](http://markorodriguez.com/2011/08/03/on-the-nature-of-pipes/)
184
+
185
+ ## Test Coverage
186
+
187
+ I'm aiming for 100% test coverage in Pacer and am currently nearly there in the core classes, but there is a way to go with the filter, transform and side effect route modules. Open coverage/index.html to see the current state of test coverage. And of course contributions would be much apreciated.
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ require 'rspec/core/rake_task'
6
+ require 'yard'
7
+
8
+ RSpec::Core::RakeTask.new(:spec) do |spec|
9
+ spec.pattern = FileList['spec/**/*_spec.rb']
10
+ end
11
+
12
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
13
+ spec.pattern = 'spec/**/*_spec.rb'
14
+ spec.ruby_opts = '--debug'
15
+ spec.rcov = true
16
+ spec.rcov_opts = %w{--exclude generator_internal,jsignal_internal,gems\/,spec\/}
17
+ end
18
+
19
+ task :default => :spec
20
+
21
+ desc 'Generate documentation'
22
+ YARD::Rake::YardocTask.new do |t|
23
+ t.files = ['lib/**/*.rb', '-', 'LICENSE.txt']
24
+ t.options = ['--no-private']
25
+ end
26
+
27
+ file 'pom.xml' => 'lib/pacer/version.rb' do
28
+ pom = File.read 'pom.xml'
29
+ when_writing('Update pom.xml version number') do
30
+ open 'pom.xml', 'w' do |f|
31
+ pom.each_line do |line|
32
+ line.sub!(%r{<gem.version>.*</gem.version>}, "<gem.version>#{ Pacer::VERSION }</gem.version>")
33
+ line.sub!(%r{<blueprints.version>.*</blueprints.version>}, "<blueprints.version>#{ Pacer::BLUEPRINTS_VERSION }</blueprints.version>")
34
+ line.sub!(%r{<pipes.version>.*</pipes.version>}, "<pipes.version>#{ Pacer::PIPES_VERSION }</pipes.version>")
35
+ f << line
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ file Pacer::JAR_PATH => 'pom.xml' do
42
+ when_writing("Execute 'mvn package' task") do
43
+ system('mvn clean package')
44
+ end
45
+ end
46
+
47
+ task :jar => Pacer::JAR_PATH
48
+ task :build => Pacer::JAR_PATH
49
+ task :install => Pacer::JAR_PATH
@@ -0,0 +1 @@
1
+ Autotest.add_discovery { "rspec2" }
data/bin/autospec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env jruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'autospec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'autospec')
data/bin/autotest ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env jruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'autotest' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('ZenTest', 'autotest')
data/bin/rake ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env jruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rake', 'rake')
data/bin/rcov ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env jruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rcov' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rcov', 'rcov')
data/bin/rspec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env jruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'rspec')
data/bin/yard ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env jruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'yard' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('yard', 'yard')
data/bin/yardoc ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env jruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'yardoc' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('yard', 'yardoc')
@@ -0,0 +1,77 @@
1
+ module Pacer
2
+ # NOTE these extensions modules can only be included in classes that don't include the
3
+ # default Java method aliases generated when importing Java classes into JRuby. In those
4
+ # classes the methods must be copied and pasted in order to overwrite the aliased methods.
5
+ module GraphExtensions
6
+ def self.included(target)
7
+ target.class_eval do
8
+ include GraphMixin
9
+ include GraphIndicesMixin if target.ancestors.include? com.tinkerpop.blueprints.pgm.IndexableGraph
10
+ include GraphTransactionsStub
11
+ include ManagedTransactionsMixin
12
+ include Pacer::Core::Route
13
+ include Pacer::Core::Graph::GraphRoute
14
+ include Pacer::Core::Graph::GraphIndexRoute if target.ancestors.include? com.tinkerpop.blueprints.pgm.IndexableGraph
15
+ end
16
+ end
17
+
18
+ # Override to return an enumeration-friendly array of vertices.
19
+ def get_vertices
20
+ getVertices.iterator.to_route(:graph => self, :element_type => :vertex)
21
+ end
22
+
23
+ # Override to return an enumeration-friendly array of edges.
24
+ def get_edges
25
+ getEdges.iterator.to_route(:graph => self, :element_type => :edge)
26
+ end
27
+
28
+ def ==(other)
29
+ other.class == self.class and other.object_id == self.object_id
30
+ end
31
+
32
+ end
33
+
34
+ module VertexExtensions
35
+ def self.included(target)
36
+ target.class_eval do
37
+ include Pacer::Core::Graph::VerticesRoute
38
+ include ElementMixin
39
+ include VertexMixin
40
+ end
41
+ end
42
+ end
43
+
44
+ module EdgeExtensions
45
+ def self.included(target)
46
+ target.class_eval do
47
+ include Pacer::Core::Graph::EdgesRoute
48
+ include ElementMixin
49
+ include EdgeMixin
50
+ end
51
+ end
52
+
53
+ def in_vertex(extensions = nil)
54
+ v = inVertex
55
+ v.graph = graph
56
+ if extensions.is_a? Enumerable
57
+ v.add_extensions extensions
58
+ elsif extensions
59
+ v.add_extensions [extensions]
60
+ else
61
+ v
62
+ end
63
+ end
64
+
65
+ def out_vertex(extensions = nil)
66
+ v = outVertex
67
+ v.graph = graph
68
+ if extensions.is_a? Enumerable
69
+ v.add_extensions extensions
70
+ elsif extensions
71
+ v.add_extensions [extensions]
72
+ else
73
+ v
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,121 @@
1
+ module Pacer
2
+ class MultiGraph < RubyGraph
3
+ def element_class
4
+ RubyElement
5
+ end
6
+
7
+ def vertex_class
8
+ MultiVertex
9
+ end
10
+
11
+ def edge_class
12
+ RubyEdge
13
+ end
14
+ end
15
+
16
+
17
+ class MultiVertex < RubyVertex
18
+ import com.tinkerpop.pipes.util.MultiIterator
19
+
20
+ def initialize(*args)
21
+ super
22
+ @vertex_keys = Set[]
23
+ @join_keys = @vertex_keys
24
+ end
25
+
26
+ attr_accessor :vertex_keys, :join_keys
27
+
28
+ def join_on(keys)
29
+ if keys.is_a? Enumerable
30
+ @join_keys = keys.map { |k| k.to_s }.to_set
31
+ elsif keys
32
+ @join_keys = Set[keys.to_s]
33
+ else
34
+ @join_keys = Set[]
35
+ end
36
+ end
37
+
38
+ def vertices
39
+ join_keys.flat_map do |key|
40
+ @properties[key]
41
+ end
42
+ end
43
+
44
+ def setProperty(key, value)
45
+ case value
46
+ when Pacer::Vertex
47
+ vertex_keys << key.to_s
48
+ super
49
+ when Hash
50
+ vertex_keys.delete key.to_s
51
+ super
52
+ when Enumerable
53
+ values = value.to_a
54
+ if values.any? and values.all? { |v| v.is_a? Pacer::Vertex }
55
+ vertex_keys << key.to_s
56
+ end
57
+ super(key, values)
58
+ else
59
+ vertex_keys.delete key.to_s
60
+ super
61
+ end
62
+ end
63
+
64
+ def append_property_array(key, value)
65
+ values = value.to_a
66
+ existing_values = getProperty(key)
67
+ if existing_values
68
+ if vertex_keys.include? key and not values.all? { |v| v.is_a? Pacer::Vertex }
69
+ vertex_keys.delete key.to_s
70
+ end
71
+ raise "Can't append to key #{ key } because it is not an Array" unless existing_values.is_a? Array
72
+ else
73
+ existing_values = []
74
+ setProperty(key, existing_values)
75
+ vertex_keys << key.to_s if values.all? { |v| v.is_a? Pacer::Vertex }
76
+ end
77
+ existing_values.concat values
78
+ end
79
+
80
+ def removeProperty(key)
81
+ vertex_keys.delete key.to_s
82
+ super
83
+ end
84
+
85
+ def getOutEdges(*labels)
86
+ vs = vertices
87
+ if vs.any?
88
+ labels = extract_varargs_strings(labels)
89
+ p = Pacer::Pipes::IdentityPipe.new
90
+ p.setStarts(MultiIterator.new super(*labels), *vs.map { |v| v.getOutEdges(*labels).iterator })
91
+ p
92
+ else
93
+ super
94
+ end
95
+ end
96
+
97
+ def getInEdges(*labels)
98
+ vs = vertices
99
+ if vs.any?
100
+ labels = extract_varargs_strings(labels)
101
+ p = Pacer::Pipes::IdentityPipe.new
102
+ p.setStarts MultiIterator.new super(*labels), *vs.map { |v| v.getInEdges(*labels).iterator }
103
+ p
104
+ else
105
+ super
106
+ end
107
+ end
108
+
109
+ def inspect
110
+ s = super
111
+ s[2] = 'M'
112
+ s
113
+ end
114
+
115
+ def to_s
116
+ "m[#{ element_id }]"
117
+ end
118
+
119
+ include VertexExtensions
120
+ end
121
+ end