diff --git a/.document b/.document new file mode 100644 index 00000000..3d618dd8 --- /dev/null +++ b/.document @@ -0,0 +1,5 @@ +lib/**/*.rb +bin/* +- +features/**/*.feature +LICENSE.txt diff --git a/.gitignore b/.gitignore index 472a7e2c..5b47f13b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,20 @@ +# rcov generated +coverage + +# rdoc generated +rdoc + +# yard generated +doc +.yardoc + +# bundler +.bundle + +# jeweler generated +pkg + +# Project specific *.graph *.db vendor/pipes*.jar diff --git a/CONTRIBUTORS b/CONTRIBUTORS new file mode 100644 index 00000000..e1f264ae --- /dev/null +++ b/CONTRIBUTORS @@ -0,0 +1,5 @@ +Maintainer: + Darrick Wiebe + +Contributors: +* diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..b99f07ac --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,24 @@ +Copyright (c) 2010, TinkerPop [http://tinkerpop.com] +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the TinkerPop nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL TINKERPOP BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Rakefile b/Rakefile index b29ed382..4b259214 100644 --- a/Rakefile +++ b/Rakefile @@ -11,16 +11,26 @@ begin gem.email = "darrick@innatesoftware.com" gem.homepage = "http://github.com/pangloss/pacer" gem.authors = ["Darrick Wiebe"] + gem.license = "MIT" gem.add_dependency "nokogiri", "~> 1.4" gem.add_development_dependency "rspec", "~> 2.1" gem.add_development_dependency "rr", "~> 1.0" gem.files = FileList['lib/**/*.rb', 'script/*', '[A-Z]*', 'spec/**/*', 'vendor/*'].to_a # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings end + Jeweler::GemcutterTasks.new + Jeweler::RubygemsDotOrgTasks.new rescue LoadError puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler" end +begin + require 'yard' + YARD::Rake::YardocTask.new +rescue LoadError + puts "YARD not available. gem install yard" +end + if Config::CONFIG['host_os'] =~ /mswin/ def jruby_path Pathname.new ENV['path'].split(';').grep(/jruby/).first @@ -44,18 +54,18 @@ if Config::CONFIG['host_os'] =~ /mswin/ end end task :install => jgem + task :build => jgem end end -require 'spec/rake/spectask' -Spec::Rake::SpecTask.new(:spec) do |spec| - spec.libs << 'lib' << 'spec' - spec.spec_files = FileList['spec/**/*_spec.rb'] +require 'rspec/core' +require 'rspec/core/rake_task' +RSpec::Core::RakeTask.new(:spec) do |spec| + spec.pattern = FileList['spec/**/*_spec.rb'] end -Spec::Rake::SpecTask.new(:rcov) do |spec| - spec.libs << 'lib' << 'spec' +RSpec::Core::RakeTask.new(:rcov) do |spec| spec.pattern = 'spec/**/*_spec.rb' spec.rcov = true end @@ -63,13 +73,3 @@ end task :spec => :check_dependencies task :default => :spec - -require 'rake/rdoctask' -Rake::RDocTask.new do |rdoc| - version = File.exist?('VERSION') ? File.read('VERSION') : "" - - rdoc.rdoc_dir = 'rdoc' - rdoc.title = "pacer #{version}" - rdoc.rdoc_files.include('README*') - rdoc.rdoc_files.include('lib/**/*.rb') -end diff --git a/VERSION b/VERSION index 17e51c38..341cf11f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.1 +0.2.0 \ No newline at end of file diff --git a/lib/pacer.rb b/lib/pacer.rb index 4f6de635..b005eef1 100644 --- a/lib/pacer.rb +++ b/lib/pacer.rb @@ -2,7 +2,7 @@ require 'pp' module Pacer - unless defined? Pacer::VERSION + unless const_defined? :VERSION VERSION = '0.1.0' PATH = File.expand_path(File.join(File.dirname(__FILE__), '..')) $:.unshift File.join(PATH, 'lib') @@ -12,8 +12,10 @@ module Pacer exit 1 end + START_TIME = Time.now + require File.join(PATH, 'vendor/blueprints-neo4j-adapter-0.1-SNAPSHOT-standalone.jar') - require File.join(PATH, 'vendor/neo4j-lucene-index-0.2-1.2.M04.jar') + require File.join(PATH, 'vendor/neo4j-lucene-index-0.2-1.2.M05.jar') end require 'pacer/graph' @@ -22,14 +24,29 @@ module Pacer require 'pacer/neo4j' require 'pacer/tg' require 'pacer/support' + require 'pacer/utils' class << self - # Reload all Ruby files in the Pacer library. Useful for debugging in the - # console. Does not do any of the fancy stuff that Rails reloading does. - # Certain types of changes will still require restarting the session. + attr_accessor :debug_info + + # Returns the time pacer was last reloaded (or when it was started). + def reload_time + @reload_time || START_TIME + end + + # Reload all Ruby modified files in the Pacer library. Useful for debugging + # in the console. Does not do any of the fancy stuff that Rails reloading + # does. Certain types of changes will still require restarting the + # session. def reload! - Dir[File.join(PATH, 'lib/**/*.rb')].each { |file| load file } - true + require 'pathname' + Pathname.new(__FILE__).parent.find do |path| + if path.extname == '.rb' and path.mtime > reload_time + puts path.to_s + load path.to_s + end + end + @reload_time = Time.now end # Set to true to prevent inspecting any route from printing @@ -44,7 +61,7 @@ def hide_route_elements # Returns how many terminal columns we have. def columns - @columns || 120 + @columns || 150 end # Tell the graph how many terminal columns we have. @@ -63,6 +80,16 @@ def inspect_limit=(n) @inspect_limit = n end + def verbose=(v) + @verbose = v + end + + def verbose? + @verbose = true if @verbose.nil? + @verbose + end + alias verbose verbose? + def vertex?(element) element.is_a? com.tinkerpop.blueprints.pgm.Vertex end diff --git a/lib/pacer/graph.rb b/lib/pacer/graph.rb index 73f961f4..b938388d 100644 --- a/lib/pacer/graph.rb +++ b/lib/pacer/graph.rb @@ -1,244 +1,9 @@ module Pacer import com.tinkerpop.blueprints.pgm.Graph - - module Graph - def import(path) - path = File.expand_path path - begin - stream = java.net.URL.new(path).open_stream - rescue java.net.MalformedURLException - stream = java.io.FileInputStream.new path - end - com.tinkerpop.blueprints.pgm.parser.GraphMLReader.input_graph self, stream - true - end - - def export(path) - path = File.expand_path path - stream = java.io.FileOutputStream.new path - com.tinkerpop.blueprints.pgm.parser.GraphMLWriter.outputGraph self, stream - end - end - - module VertexMixin - - def add_extension(mod) - super - if mod.const_defined? :Vertex - extend mod::Vertex - extensions << mod - end - end - - # Returns a human-readable representation of the vertex. - def inspect - "#<#{ ["V[#{id}]", display_name].compact.join(' ') }>" - end - - # Returns the display name of the vertex. - def display_name - graph.vertex_name.call self if graph and graph.vertex_name - end - - # Deletes the vertex from its graph along with all related edges. - def delete! - graph.remove_vertex self - end - - def clone_into(target_graph, opts = {}) - return if target_graph.vertex(id) - v = target_graph.add_vertex id - properties.each do |name, value| - v[name] = value - end - yield v if block_given? - v - end - - def copy_into(target_graph, opts = {}) - v = target_graph.add_vertex nil - properties.each do |name, value| - v[name] = value - end - yield v if block_given? - v - end - end - - - module EdgeMixin - - def add_extension(mod) - super - if mod.const_defined? :Edge - extend mod::Edge - extensions << mod - end - end - - # Returns a human-readable representation of the edge. - def inspect - "#" - end - - # Returns the display name of the vertex. - def display_name - if graph and graph.edge_name - graph.edge_name.call self - else - "#{ out_vertex.id }-#{ get_label }-#{ in_vertex.id }" - end - end - - # Deletes the edge from its graph. - def delete! - graph.remove_edge self - end - - # Returns a path if arguments are given, otherwise returns the out vertex - # itself. - def out_v(*args) - if args.any? - super - else - out_vertex - end - end - - # Returns a path if arguments are given, otherwise returns the in vertex - # itself. - def in_v(*args) - if args.any? - super - else - in_vertex - end - end - - def clone_into(target_graph, opts = {}) - return if target_graph.edge(id) - iv = target_graph.vertex(in_v.id) - ov = target_graph.vertex(out_v.id) - if opts[:create_vertices] - iv ||= in_v.clone_into target_graph - ov ||= out_v.clone_into target_graph - end - return if not iv or not ov - e = target_graph.add_edge id, iv, ov, label - properties.each do |name, value| - e[name] = value - end - yield e if block_given? - e - end - - def copy_into(target_graph, opts = {}) - iv = target_graph.vertex(in_v.id) - ov = target_graph.vertex(out_v.id) - return if not iv or not ov - e = target_graph.add_edge nil, iv, ov, label - properties.each do |name, value| - e[name] = value - end - yield e if block_given? - e - end - end - - - module ElementMixin - def self.included(target) - target.send :include, Enumerable unless target.is_a? Enumerable - end - - def add_extension(mod) - if mod.const_defined? :Route - extend mod::Route - extensions << mod - end - end - - def extensions - @extensions ||= Set[] - end - - # If any objects in the given array are modules that contain a Route - # submodule, extend this route with the Route module. - def add_extensions(exts) - modules = exts.select { |obj| obj.is_a? Module } - modules.each do |mod| - add_extension(mod) - end - self - end - - def v(*args) - route = super - if args.empty? and not block_given? - route.add_extensions extensions - end - end - - def e(*args) - route = super - if args.empty? and not block_given? - route.add_extensions extensions - end - end - - # Specify the graph the element belongs to. For internal use only. - def graph=(graph) - @graph = graph - end - - # The graph the element belongs to. Used to help prevent objects from - # different graphs from being accidentally associated, as well as to get - # graph-specific data for the element. - def graph - @graph - end - - # Convenience method to retrieve a property by name. - def [](key) - get_property(key.to_s) - end - - # Convenience method to set a property by name to the given value. - def []=(key, value) - set_property(key.to_s, value) - end - - # Specialize result to return self for elements. - def result(name = nil) - self - end - - # Query whether the current node belongs to the given graph. - def from_graph?(g) - g == graph - end - - # Returns a hash of property values by name. - def properties - property_keys.inject({}) { |h, name| h[name] = get_property(name); h } - end - - # Returns a basic display name for the element. This method should be specialized. - def display_name - id - end - - # Yields the element once or returns an enumerator containing self if no - # block is given. Follows Ruby conventions and is meant to be used along - # with the Enumerable mixin. - def each - if block_given? - yield self - else - [self].to_enum - end - end - end end -require 'pacer/graph/transactions' +require 'pacer/graph/graph_mixin' +require 'pacer/graph/element_mixin' +require 'pacer/graph/vertex_mixin' +require 'pacer/graph/edge_mixin' +require 'pacer/graph/graph_transactions_mixin' diff --git a/lib/pacer/graph/edge_mixin.rb b/lib/pacer/graph/edge_mixin.rb new file mode 100644 index 00000000..28d99497 --- /dev/null +++ b/lib/pacer/graph/edge_mixin.rb @@ -0,0 +1,84 @@ +module Pacer + module EdgeMixin + def add_extension(mod) + super + if mod.const_defined? :Edge + extend mod::Edge + extensions << mod + end + self + end + + # Returns a human-readable representation of the edge. + def inspect + "#" + end + + # Returns the display name of the vertex. + def display_name + if @graph and @graph.edge_name + @graph.edge_name.call self + else + "#{ out_vertex.get_id }-#{ get_label }-#{ in_vertex.get_id }" + end + end + + # Deletes the edge from its graph. + def delete! + @graph.remove_edge self + end + + # Returns a path if arguments are given, otherwise returns the out vertex + # itself. + def out_v(*args) + if args.any? + super + else + v = out_vertex + v.graph = graph + v + end + end + + # Returns a path if arguments are given, otherwise returns the in vertex + # itself. + def in_v(*args) + if args.any? + super + else + v = in_vertex + v.graph = graph + v + end + end + + def clone_into(target_graph, opts = {}) + return if target_graph.edge(get_id) + iv = target_graph.vertex(in_v.get_id) + ov = target_graph.vertex(out_v.get_id) + if opts[:create_vertices] + iv ||= in_v.clone_into target_graph + ov ||= out_v.clone_into target_graph + end + return if not iv or not ov + e = target_graph.create_edge get_id, iv, ov, label + properties.each do |name, value| + e[name] = value + end + yield e if block_given? + e + end + + def copy_into(target_graph, opts = {}) + iv = target_graph.vertex(in_v.get_id) + ov = target_graph.vertex(out_v.get_id) + return if not iv or not ov + e = target_graph.create_edge nil, iv, ov, label + properties.each do |name, value| + e[name] = value + end + yield e if block_given? + e + end + end +end diff --git a/lib/pacer/graph/element_mixin.rb b/lib/pacer/graph/element_mixin.rb new file mode 100644 index 00000000..61379dea --- /dev/null +++ b/lib/pacer/graph/element_mixin.rb @@ -0,0 +1,115 @@ +module Pacer + module ElementMixin + def self.included(target) + target.send :include, Enumerable unless target.is_a? Enumerable + end + + def add_extension(mod) + if mod.const_defined? :Route + extend mod::Route + extensions << mod + end + self + end + + def extensions + @extensions ||= Set[] + end + + # If any objects in the given array are modules that contain a Route + # submodule, extend this route with the Route module. + def add_extensions(exts) + modules = exts.select { |obj| obj.is_a? Module or obj.is_a? Class } + modules.each do |mod| + add_extension(mod) + end + self + end + + def v(*args) + route = super + if args.empty? and not block_given? + route.add_extensions extensions + end + route + end + + def e(*args) + route = super + if args.empty? and not block_given? + route.add_extensions extensions + end + route + end + + # Specify the graph the element belongs to. For internal use only. + def graph=(graph) + @graph = graph + end + + # The graph the element belongs to. Used to help prevent objects from + # different graphs from being accidentally associated, as well as to get + # graph-specific data for the element. + def graph + @graph + end + + # Convenience method to retrieve a property by name. + def [](key) + get_property(key.to_s) + end + + # Convenience method to set a property by name to the given value. + def []=(key, value) + if value + set_property(key.to_s, value) if value != get_property(key.to_s) + else + remove_property(key.to_s) + end + end + + # Specialize result to return self for elements. + def result(name = nil) + self + end + + # Query whether the current node belongs to the given graph. + def from_graph?(g) + g == graph + end + + # Returns a hash of property values by name. + def properties + property_keys.inject({}) { |h, name| h[name] = get_property(name); h } + end + + def properties=(props) + (property_keys - props.keys.map { |k| k.to_s }).each do |key| + remove_property key + end + props.each do |key, value| + self[key] = value + end + end + + # Returns a basic display name for the element. This method should be specialized. + def display_name + get_id + end + + def <=>(other) + display_name.to_s <=> other.display_name.to_s + end + + # Yields the element once or returns an enumerator containing self if no + # block is given. Follows Ruby conventions and is meant to be used along + # with the Enumerable mixin. + def each + if block_given? + yield self + else + [self].to_enum + end + end + end +end diff --git a/lib/pacer/graph/graph_mixin.rb b/lib/pacer/graph/graph_mixin.rb new file mode 100644 index 00000000..7d1e2e79 --- /dev/null +++ b/lib/pacer/graph/graph_mixin.rb @@ -0,0 +1,90 @@ +module Pacer + module GraphMixin + def self.included(target) + target.class_eval do + protected :addVertex, :addEdge + protected :getVertex, :getEdge + alias vertex get_vertex + alias edge get_edge + end + end + + attr_accessor :in_bulk_job + + def get_vertex(id) + v = getVertex(id) + v.graph = self + v + end + + def get_edge(id) + v = getEdge(id) + v.graph = self + v + end + + def create_vertex(*args) + if args.last.is_a? Hash + props = args.last + end + id = args.first if args.first.is_a? Fixnum + v = addVertex(id) + if props + props.each { |k, v| e[k.to_s] = v if v } + end + v.graph = self + v + end + + def create_edge(id, from_v, to_v, label, props = nil) + e = addEdge(id, from_v, to_v, label) + e.graph = self + if props + props.each { |k, v| e[k.to_s] = v if v } + end + e + end + + def import(path) + path = File.expand_path path + begin + stream = java.net.URL.new(path).open_stream + rescue java.net.MalformedURLException + stream = java.io.FileInputStream.new path + end + com.tinkerpop.blueprints.pgm.parser.GraphMLReader.input_graph self, stream + true + end + + def export(path) + path = File.expand_path path + stream = java.io.FileOutputStream.new path + com.tinkerpop.blueprints.pgm.parser.GraphMLWriter.outputGraph self, stream + end + + def bulk_job_size=(size) + @bulk_job_size = size + end + + def bulk_job_size + @bulk_job_size || 5000 + end + + def in_bulk_job? + @in_bulk_job + end + + def load_vertices(ids) + ids.map do |id| + vertex id rescue nil + end.compact + end + + def load_edges(ids) + ids.map do |id| + edge id rescue nil + end.compact + end + + end +end diff --git a/lib/pacer/graph/graph_transactions_mixin.rb b/lib/pacer/graph/graph_transactions_mixin.rb new file mode 100644 index 00000000..34f732ab --- /dev/null +++ b/lib/pacer/graph/graph_transactions_mixin.rb @@ -0,0 +1,165 @@ +module Pacer + import com.tinkerpop.blueprints.pgm.TransactionalGraph + + def self.graphs_in_transaction + @graphs ||= Set[] + end + + module ManagedTransactionsMixin + def manage_transactions=(v) + @manage_transactions = v + end + + def manage_transactions? + @manage_transactions = true if @manage_transactions.nil? + @manage_transactions + end + alias manage_transactions manage_transactions? + + def unmanaged_transactions + old_value = @manage_transactions + @manage_transactions = false + yield + ensure + @manage_transactions = old_value + end + + def managed_transactions + if manage_transactions? + manual_transactions { yield } + else + yield + end + end + + def managed_manual_transaction + if manage_transactions? + manual_transaction { yield } + else + yield + end + end + + def managed_transaction + if manage_transactions? + transaction { yield } + else + yield + end + end + + def managed_start_transaction + begin_transaction if manage_transactions? + end + + def managed_commit_transaction + commit_transaction if manage_transactions? + end + + def managed_checkpoint + checkpoint if manage_transactions? + end + end + + module GraphTransactionsStub + def manual_transaction + yield + end + + def manual_transactions + yield + end + + def transaction + yield + end + + def begin_transaction + end + + def commit_transaction + end + + def rollback_transaction + end + + def checkpoint + end + end + + module GraphTransactionsMixin + def manual_transaction + manual_transactions do + transaction do + yield + end + end + end + + def manual_transactions + original_mode = get_transaction_mode + if original_mode != TransactionalGraph::Mode::MANUAL + begin + puts "transaction mode reset to MANUAL" if Pacer.verbose == :very + set_transaction_mode TransactionalGraph::Mode::MANUAL + yield + rescue IRB::Abort, Exception + rollback_transaction if Pacer.graphs_in_transaction.include? self + raise + ensure + puts "transaction mode reset to #{ original_mode }" if Pacer.verbose == :very + set_transaction_mode original_mode + end + else + yield + end + end + + def transaction + begin_transaction + conclusion = TransactionalGraph::Conclusion::FAILURE + begin + catch :transaction_failed do + yield + conclusion = TransactionalGraph::Conclusion::SUCCESS + end + rescue IRB::Abort + puts "transaction aborted" if Pacer.verbose == :very + raise + ensure + puts "transaction finished #{ conclusion }" if Pacer.verbose == :very + stop_transaction conclusion + Pacer.graphs_in_transaction.delete self + end + end + + def begin_transaction + r = startTransaction + Pacer.graphs_in_transaction << self + puts "transaction started" if Pacer.verbose == :very + r + end + + def commit_transaction + r = stop_transaction TransactionalGraph::Conclusion::SUCCESS + Pacer.graphs_in_transaction.delete self + puts "transaction committed" if Pacer.verbose == :very + r + end + + def rollback_transaction + r = stop_transaction TransactionalGraph::Conclusion::FAILURE + Pacer.graphs_in_transaction.delete self + puts "transaction rolled back" if Pacer.verbose == :very + r + end + + def checkpoint + commit_transaction + begin_transaction + Pacer.graphs_in_transaction << self + end + + #protected :startTransaction + end +end diff --git a/lib/pacer/graph/transactions.rb b/lib/pacer/graph/transactions.rb deleted file mode 100644 index e37c5f32..00000000 --- a/lib/pacer/graph/transactions.rb +++ /dev/null @@ -1,45 +0,0 @@ -module Pacer - import com.tinkerpop.blueprints.pgm.TransactionalGraph - - module Graph - def manual_transactions - original_mode = get_transaction_mode - if original_mode != TransactionalGraph::Mode::MANUAL - begin - set_transaction_mode TransactionalGraph::Mode::MANUAL - yield - ensure - set_transaction_mode original_mode - end - else - yield - end - end - - def transaction - start_transaction - conclusion = TransactionalGraph::Conclusion::FAILURE - begin - catch :transaction_failed do - yield - conclusion = TransactionalGraph::Conclusion::SUCCESS - end - ensure - stop_transaction conclusion - end - end - - def commit_transaction - stop_transaction TransactionalGraph::Conclusion::SUCCESS - end - - def rollback_transaction - stop_transaction TransactionalGraph::Conclusion::FAILURE - end - - def checkpoint - commit_transaction - start_transaction - end - end -end diff --git a/lib/pacer/graph/vertex_mixin.rb b/lib/pacer/graph/vertex_mixin.rb new file mode 100644 index 00000000..2ac121ad --- /dev/null +++ b/lib/pacer/graph/vertex_mixin.rb @@ -0,0 +1,50 @@ +module Pacer + module VertexMixin + def add_extension(mod) + super + if mod.const_defined? :Vertex + extend mod::Vertex + extensions << mod + end + self + end + + # Returns a human-readable representation of the vertex. + def inspect + "#<#{ ["V[#{get_id}]", display_name].compact.join(' ') }>" + end + + # Returns the display name of the vertex. + def display_name + @graph.vertex_name.call self if @graph and @graph.vertex_name + end + + # Deletes the vertex from its graph along with all related edges. + def delete! + @graph.remove_vertex self + end + + # Copies including the vertex id unless a vertex with that id + # already exists. + def clone_into(target_graph, opts = {}) + return if target_graph.vertex(id) + v = target_graph.create_vertex id + properties.each do |name, value| + v[name] = value + end + yield v if block_given? + v + end + + # Make a new copy of the element with the next available vertex + # id. + def copy_into(target_graph, opts = {}) + v = target_graph.create_vertex + properties.each do |name, value| + v[name] = value + end + yield v if block_given? + v + end + end +end diff --git a/lib/pacer/neo4j.rb b/lib/pacer/neo4j.rb index b59b994c..a711c7e9 100644 --- a/lib/pacer/neo4j.rb +++ b/lib/pacer/neo4j.rb @@ -39,12 +39,12 @@ def register_neo_shutdown(path) # Extend the java class imported from blueprints. class Neo4jGraph + include GraphMixin + include GraphTransactionsMixin + include ManagedTransactionsMixin include Routes::Base include Routes::GraphRoute - alias vertex get_vertex - alias edge get_edge - def element_type(et) case et when :vertex, com.tinkerpop.blueprints.pgm.Vertex, VertexMixin diff --git a/lib/pacer/pipe/block_filter_pipe.rb b/lib/pacer/pipe/block_filter_pipe.rb index 55861bcb..5c9d825d 100644 --- a/lib/pacer/pipe/block_filter_pipe.rb +++ b/lib/pacer/pipe/block_filter_pipe.rb @@ -5,12 +5,30 @@ def initialize(starts, back, block) set_starts(starts) @back = back @block = block + + @extensions = @back.extensions + @extension = package_extensions @back, @back.extensions + end + + def package_extensions(route, extensions) + extension = Module.new + extensions.each do |mod| + extension.send :include, mod::Route if mod.const_defined? :Route + if route.is_a? Pacer::Routes::VerticesRouteModule + extension.send(:include, mod::Vertex) if mod.const_defined? :Vertex + elsif route.is_a? Pacer::Routes::EdgeRouteModule + extension.send(:include, mod::Edge) if mod.const_defined? :Edge + end + end + extension end def processNextStart() while s = @starts.next s.extend Pacer::Routes::SingleRoute s.back = @back + s.extensions.replace @extensions + s.extend @extension ok = @block.call s return s if ok end diff --git a/lib/pacer/pipes.rb b/lib/pacer/pipes.rb index 9945d693..2799513c 100644 --- a/lib/pacer/pipes.rb +++ b/lib/pacer/pipes.rb @@ -20,6 +20,7 @@ module Pipes import com.tinkerpop.pipes.pgm.EdgeVertexPipe import com.tinkerpop.pipes.split.CopySplitPipe + import com.tinkerpop.pipes.split.RobinSplitPipe import com.tinkerpop.pipes.merge.RobinMergePipe import com.tinkerpop.pipes.merge.ExhaustiveMergePipe end diff --git a/lib/pacer/route/branched_route.rb b/lib/pacer/route/branched_route.rb index ac13e78f..b4556e42 100644 --- a/lib/pacer/route/branched_route.rb +++ b/lib/pacer/route/branched_route.rb @@ -39,12 +39,17 @@ def merge MixedElementsRoute.new(self) end + def robin_split + split_pipe(Pacer::Pipes::RobinSplitPipe) + end + def exhaustive merge_pipe(Pacer::Pipes::ExhaustiveMergePipe) end - def merge_pipe(pipe_class) + def merge_pipe(pipe_class, &block) @merge_pipe = pipe_class + @configure_merge_pipe = block self end @@ -52,8 +57,9 @@ def merge_pipe? @merge_pipe end - def split_pipe(pipe_class) + def split_pipe(pipe_class, &block) @split_pipe = pipe_class + @configure_split_pipe = block self end @@ -81,6 +87,7 @@ def add_branches_to_pipe(pipe) if split_pipe.respond_to? :route= split_pipe.route = self end + @configure_split_pipe.call(pipe) if @configure_split_pipe idx = 0 pipes = @branches.map do |branch_start, branch_end| branch_start.new_identity_pipe.set_starts(split_pipe.get_split(idx)) @@ -90,6 +97,7 @@ def add_branches_to_pipe(pipe) end pipe = @merge_pipe.new pipe.set_starts(pipes) + @configure_merge_pipe.call(pipe) if @configure_merge_pipe pipe end diff --git a/lib/pacer/route/indexed_edges_route.rb b/lib/pacer/route/indexed_edges_route.rb index 3b34d6bf..e0740ad0 100644 --- a/lib/pacer/route/indexed_edges_route.rb +++ b/lib/pacer/route/indexed_edges_route.rb @@ -1,22 +1,6 @@ module Pacer::Routes class IndexedEdgesRoute < EdgesRoute - def initialize(index, key, value, filters, block) - if filters == [key => value] and block.nil? - # indexed count is possible if no other conditions - @index = index - @key = key - @value = value - end - initialize_path(proc { r = index.get(key, value); r ? r.iterator : [] }, filters, block) - end - - def count - if @index and @key and @value - @index.count(@key, @value) - else - super - end - end + include IndexedRouteModule protected diff --git a/lib/pacer/route/indexed_vertices_route.rb b/lib/pacer/route/indexed_vertices_route.rb index e4016351..d8df8c2c 100644 --- a/lib/pacer/route/indexed_vertices_route.rb +++ b/lib/pacer/route/indexed_vertices_route.rb @@ -1,22 +1,6 @@ module Pacer::Routes class IndexedVerticesRoute < VerticesRoute - def initialize(index, key, value, filters, block) - if filters == [key => value] and block.nil? - # indexed count is possible if no other conditions - @index = index - @key = key - @value = value - end - initialize_path(proc { r = index.get(key, value); r ? r.iterator : [] }, filters, block) - end - - def count - if @index and @key and @value - @index.count(@key, @value) - else - super - end - end + include IndexedRouteModule protected diff --git a/lib/pacer/route/mixin/base.rb b/lib/pacer/route/mixin/base.rb index b1715e51..c4560e9b 100644 --- a/lib/pacer/route/mixin/base.rb +++ b/lib/pacer/route/mixin/base.rb @@ -50,6 +50,13 @@ def self.included(target) # The previous route in the path def back + # TODO: something like @back.look_ahead(self) + # it should be the previous route in the path but which + # emits an element only if the future route that is defined + # actually emits anything. That functionality exists in + # FutureFilterPipe and is how gremlin's .. step works. + # Also, the look_ahead should have a .back method that + # pushes the route back farther... @back end @@ -132,6 +139,7 @@ def except(route) route_class.pipe_filter(self, Pacer::Pipes::CollectionFilterPipe, route.to_hashset, Pacer::Pipes::ComparisonFilterPipe::Filter::EQUAL) end result.add_extensions extensions + result.graph = graph result end @@ -146,6 +154,7 @@ def only(route) route_class.pipe_filter(self, Pacer::Pipes::CollectionFilterPipe, route.to_hashset, Pacer::Pipes::ComparisonFilterPipe::Filter::NOT_EQUAL) end result.add_extensions extensions + result.graph = graph result end @@ -154,7 +163,9 @@ def each iter = iterator if extensions.empty? if block_given? + g = graph while item = iter.next + item.graph ||= g yield item end else @@ -163,11 +174,13 @@ def each else if block_given? while item = iter.next + item.graph ||= graph item.add_extensions extensions yield item end else iter.extend IteratorExtensionsMixin + iter.graph = graph iter.extensions = extensions iter end @@ -181,10 +194,13 @@ def each_path iter = iterator iter.enable_path if block_given? + g = graph while item = iter.next - yield iter.path + yield iter.path.map { |e| e.graph ||= g; e } end else + iter.extend IteratorPathMixin + iter.graph = graph iter end rescue NoSuchElementException @@ -194,13 +210,16 @@ def each_path def each_context iter = iterator if block_given? + g = graph while item = iter.next + item.graph ||= g item.extend Pacer::Routes::SingleRoute item.back = self yield item end else iter.extend IteratorContextMixin + iter.graph = graph iter.context = self iter end @@ -264,6 +283,7 @@ def add_extension(mod) if is_extension or mod.const_defined? :Vertex or mod.const_defined? :Edge extensions << mod end + self end def extensions @@ -273,13 +293,21 @@ def extensions # If any objects in the given array are modules that contain a Route # submodule, extend this route with the Route module. def add_extensions(exts) - modules = exts.select { |obj| obj.is_a? Module } + modules = exts.select { |obj| obj.is_a? Module or obj.is_a? Class } modules.each do |mod| add_extension(mod) end self end + def set_pipe_source(source) + if @back + @back.set_pipe_source source + else + self.source = source + end + end + protected # Initializes some basic instance variables. @@ -412,7 +440,7 @@ def inspect_class_name # the pipes for this route object. def filter_pipe(pipe, args_array, block) if args_array and args_array.any? - modules = args_array.select { |arg| arg.is_a? Module } + modules = args_array.select { |obj| obj.is_a? Module or obj.is_a? Class } pipe = modules.inject(pipe) do |p, mod| if mod.respond_to? :route_conditions args_array = args_array + [mod.route_conditions] @@ -422,6 +450,8 @@ def filter_pipe(pipe, args_array, block) beginning_of_condition = route.send :route_after, self beginning_of_condition.send :source=, pipe route.send :iterator + else + pipe end end pipe = args_array.select { |arg| arg.is_a? Hash }.inject(pipe) do |p, hash| diff --git a/lib/pacer/route/mixin/bulk_operations.rb b/lib/pacer/route/mixin/bulk_operations.rb new file mode 100644 index 00000000..f719984a --- /dev/null +++ b/lib/pacer/route/mixin/bulk_operations.rb @@ -0,0 +1,51 @@ +module Pacer::Routes + + # Additional iteration methods that allow for rapid data + # manipulation in transactional graphs. Bulk operations automatically + # manage transactions in larger batches rather than on every + # element created or removed or every property set. + module BulkOperations + # Like bulk_job that also returns an array of results + def bulk_map(size = nil, target_graph = nil) + result = [] + bulk_job(size, target_graph) do |e| + result << yield(e) + end + result + end + + # Iterates over each element in the route, controlling + # transactions so that they are only committed once every + # +size+ records. + def bulk_job(size = nil, target_graph = nil) + target_graph ||= graph + if target_graph and not target_graph.in_bulk_job? + begin + target_graph.in_bulk_job = true + size ||= target_graph.bulk_job_size + counter = 0 + each_slice(size) do |slice| + print counter if Pacer.verbose? + counter += size + target_graph.managed_manual_transaction do + target_graph.unmanaged_transactions do + slice.each do |element| + yield element + end + end + print '.' if Pacer.verbose? + end + end + ensure + target_graph.in_bulk_job = false + end + elsif target_graph + each do |element| + yield element + end + else + raise 'No graph in route for bulk job' + end + end + end +end diff --git a/lib/pacer/route/mixin/edges_route_module.rb b/lib/pacer/route/mixin/edges_route_module.rb index 4b9b0937..085c1db4 100644 --- a/lib/pacer/route/mixin/edges_route_module.rb +++ b/lib/pacer/route/mixin/edges_route_module.rb @@ -26,9 +26,14 @@ def v(*filters) def e(*filters, &block) route = EdgesRoute.new(self, filters, block) route.pipe_class = nil + route.add_extensions extensions unless route.extensions.any? route end + def filter(*args, &block) + e(*args, &block) + end + # Return an iterator of or yield all labels def labels map { |e| e.get_label } @@ -39,10 +44,14 @@ def labels def result(name = nil) edge_ids = ids if edge_ids.count == 1 - graph.edge ids.first + e = graph.edge ids.first + e.add_extensions extensions + e else r = EdgesRoute.from_edge_ids graph, edge_ids r.info = "#{ name }:#{r.info}" if name + r.add_extensions extensions + r.graph = graph r end end diff --git a/lib/pacer/route/mixin/graph_route.rb b/lib/pacer/route/mixin/graph_route.rb index fbccd0b6..5e72686a 100644 --- a/lib/pacer/route/mixin/graph_route.rb +++ b/lib/pacer/route/mixin/graph_route.rb @@ -29,6 +29,10 @@ def e(*filters, &block) route end + def filter(*args) + raise 'Not implemented' + end + # Specialization of result simply returns self. def result self @@ -82,6 +86,18 @@ def index_keys @index_keys ||= {} end + def vertex(id) + v = super + v.graph = self + v + end + + def edge(id) + e = super + e.graph = self + e + end + protected # Don't try to inspect the graph data when inspecting. @@ -93,7 +109,7 @@ def each_property_filter(filters) filters.each do |filter| if filter.is_a? Hash filter.each { |key, value| yield key, value if key } - elsif filter.is_a? Module + elsif filter.is_a? Module or filter.is_a? Class if filter.respond_to? :route_conditions each_property_filter([filter.route_conditions]) { |k, v| yield k, v } elsif filter.respond_to? :route @@ -124,7 +140,7 @@ def index_key_value(key, value) def indexed_route(element_type, filters, block) element_type = self.element_type(element_type) each_property_filter(filters) do |index_name, index_value| - if index_value.is_a? Module + if index_value.is_a? Module or index_value.is_a? Class return index_value.route(self) elsif index_value idx = (indices || []).detect { |i| use_index?(i, element_type, index_name.to_s, index_value) } diff --git a/lib/pacer/route/mixin/indexed_route_module.rb b/lib/pacer/route/mixin/indexed_route_module.rb new file mode 100644 index 00000000..47a11fcf --- /dev/null +++ b/lib/pacer/route/mixin/indexed_route_module.rb @@ -0,0 +1,47 @@ +module Pacer::Routes + module IndexedRouteModule + def initialize(index, key, value, filters, block) + filters = extract_route_conditions(filters) + if filters.count == 1 and block.nil? + # indexed count is possible if no other conditions + @index = index + @key = key + @value = value + end + filters = remove_filter_key(filters, key) + initialize_path(proc { r = index.get(key, value); r ? r.iterator : [] }, filters, block) + end + + def count + if @index and @key and @value + @index.count(@key, @value) + else + super + end + end + + protected + + def extract_route_conditions(filters) + filters.map do |filter| + if (filter.is_a? Module or filter.is_a? Class) and filter.respond_to? :route_conditions + Hash[filters.first.route_conditions.map { |k, v| [k.to_s, v] }] + else + filter + end + end + end + + def remove_filter_key(filters, key) + filters = filters.map do |filter| + if filter.is_a? Hash + filter = Hash[filter.reject { |k, v| k.to_s == key.to_s }] + if filter.empty? + filter = nil + end + end + filter + end.compact + end + end +end diff --git a/lib/pacer/route/mixin/iterator_mixin.rb b/lib/pacer/route/mixin/iterator_mixin.rb index c357557a..4627582d 100644 --- a/lib/pacer/route/mixin/iterator_mixin.rb +++ b/lib/pacer/route/mixin/iterator_mixin.rb @@ -3,17 +3,23 @@ module Pacer::Routes # transformation on the elements in their collection. Set the block property # to the proc that does the transformation. module IteratorBlockMixin + attr_accessor :graph + # Set the block that does the transformation. def block=(block) @block = block end def next - @block.call(super) + item = super + item.graph ||= @graph + @block.call(item) end end module IteratorContextMixin + attr_accessor :graph + # Set the context def context=(context) @context = context @@ -22,11 +28,24 @@ def context=(context) def next item = super item.back = @context + item.graph ||= @graph item end end + module IteratorPathMixin + attr_accessor :graph + + def next + path = super + path.map { |e| e.graph ||= @graph; e } + path + end + end + module IteratorExtensionsMixin + attr_accessor :graph + # Set the extensions def extensions=(extensions) @extensions = extensions @@ -35,6 +54,7 @@ def extensions=(extensions) def next item = super item.add_extensions @extensions + item.graph ||= @graph item end end diff --git a/lib/pacer/route/mixin/mixed_route_module.rb b/lib/pacer/route/mixin/mixed_route_module.rb index e9e08e0e..1bc38726 100644 --- a/lib/pacer/route/mixin/mixed_route_module.rb +++ b/lib/pacer/route/mixin/mixed_route_module.rb @@ -6,12 +6,22 @@ module MixedRouteModule # Pass through only vertices. def v - VerticesRoute.pipe_filter(self, Pacer::Pipes::TypeFilterPipe, Pacer::VertexMixin) + route = VerticesRoute.pipe_filter(self, Pacer::Pipes::TypeFilterPipe, Pacer::VertexMixin) + route.add_extensions extensions unless route.extensions.any? + route end # Pass through only edges. def e - EdgesRoute.pipe_filter(self, Pacer::Pipes::TypeFilterPipe, Pacer::EdgeMixin) + route = EdgesRoute.pipe_filter(self, Pacer::Pipes::TypeFilterPipe, Pacer::EdgeMixin) + route.add_extensions extensions unless route.extensions.any? + route + end + + def filter(*args, &block) + route = branch { |b| b.v(*args, &block) }.branch { |b| b.v(*args, &block) }.mixed + route.add_extensions extensions unless route.extensions.any? + route end def mixed @@ -66,12 +76,11 @@ def result(name = nil) method, id = ids.first graph.send method, id else - g = graph loader = proc do ids.map { |method, id| graph.send(method, id) } end r = MixedElementsRoute.new(loader) - r.graph = g + r.graph = graph r.pipe_class = nil r.info = "#{ name }:#{ids.count}" r diff --git a/lib/pacer/route/mixin/route_operations.rb b/lib/pacer/route/mixin/route_operations.rb index 2ae68add..a6e1bea8 100644 --- a/lib/pacer/route/mixin/route_operations.rb +++ b/lib/pacer/route/mixin/route_operations.rb @@ -4,6 +4,7 @@ module Pacer::Routes # routes if they support the full route interface. module RouteOperations include BranchableRoute + include BulkOperations def paths PathsRoute.new(self) @@ -29,10 +30,18 @@ def random(bias = 0.5) end # Do not return duplicate elements. - def uniq - route = route_class.pipe_filter(self, Pacer::Pipes::DuplicateFilterPipe) - route.add_extensions extensions - route + def uniq(*filters, &block) + if filters.any? or block + filter(*filters, &block).uniq + else + route = route_class.pipe_filter(self, Pacer::Pipes::DuplicateFilterPipe) + route.add_extensions extensions + route + end + end + + def has?(element) + any? { |e| e == element } end # Accepts a string or symbol to return an array of matching properties, or @@ -57,6 +66,11 @@ def [](prop_or_subset) route.add_extensions extensions route when Array + if prop_or_subset.all? { |i| i.is_a? String or i.is_a? Symbol } + map do |element| + prop_or_subset.map { |i| element.get_property(i.to_s) } + end + end end end @@ -88,13 +102,27 @@ def group_count(*props) each do |e| result[props.map { |p| e.get_property(p) }] += 1 end + else + each do |e| + result[e] += 1 + end end result end + def most_frequent(range = 0) + group_count.sort_by { |k, v| -v }.map { |k, v| k }[range] + end + + def all_but_most_frequent(start_at = 1) + elements = group_count.sort_by { |k, v| -v }.map { |k, v| k }[start_at..-1] + elements ||= [] + elements.to_route(:based_on => self) + end + # Delete all matching elements. def delete! - map { |e| e.delete! } + uniq.bulk_job { |e| e.delete! } end # Store the current intermediate element in the route's vars hash by the @@ -167,13 +195,40 @@ def pages(elements_per_page = 1000) end def clone_into(target_graph, opts = {}) - each do |element| + bulk_job(nil, target_graph) do |element| element.clone_into(target_graph, opts) end end def copy_into(target_graph, opts = {}) - each { |element| element.copy_into(target_graph, opts) } + bulk_job(nil, target_graph) { |element| element.copy_into(target_graph, opts) } + end + + def build_index(index, index_key = nil, property = nil, create = true) + index_name = index + unless index.is_a? com.tinkerpop.blueprints.pgm.Index + index = graph.indices.find { |i| i.index_name == index.to_s } + end + unless index + if create + index = graph.create_index index_name, graph.element_type(first), Pacer.manual_index + else + raise "No index found for #{ index } on #{ graph }" unless index + end + end + index_key ||= index.index_name + property ||= index_key + if block_given? + bulk_job do |element| + value = yield(element) + index.put(index_key, value, element) if value + end + else + bulk_job do |element| + value = element[property] + index.put(index_key, value, element) if value + end + end end protected diff --git a/lib/pacer/route/mixin/vertices_route_module.rb b/lib/pacer/route/mixin/vertices_route_module.rb index 3af62d7c..1f9b4d86 100644 --- a/lib/pacer/route/mixin/vertices_route_module.rb +++ b/lib/pacer/route/mixin/vertices_route_module.rb @@ -22,9 +22,14 @@ def both_e(*filters, &block) def v(*filters, &block) route = VerticesRoute.new(self, filters, block) route.pipe_class = nil + route.add_extensions extensions unless route.extensions.any? route end + def filter(*args, &block) + v(*args, &block) + end + # Undefined for vertex routes. def e(*filters, &block) raise "Can't call edges for VerticesRoute." @@ -35,10 +40,14 @@ def e(*filters, &block) def result(name = nil) v_ids = ids if v_ids.count == 1 - graph.vertex v_ids.first + v = graph.vertex v_ids.first + v.add_extensions extensions + v else r = VerticesRoute.from_vertex_ids graph, v_ids r.info = "#{ name }:#{r.info}" if name + r.add_extensions extensions + r.graph = graph r end end @@ -47,28 +56,53 @@ def result(name = nil) # matching this route to all vertices matching the given # to_route. If any properties are given, they will be applied # to each created edge. - def to(label, to_vertices, props = {}) + def add_edges_to(label, to_vertices, props = {}) case to_vertices - when Base - raise "Must be from same graph" unless to_vertices.from_graph?(graph) - when Enumerable, Iterator - raise "Must be from same graph" unless to_vertices.first.from_graph?(graph) + when Base, Enumerable, java.util.Iterator else - raise "Must be from same graph" unless to_vertices.from_graph?(graph) - to_vertices = [to_vertices] + to_vertices = [to_vertices].compact + end + graph = self.graph + unless graph + v = (detect { |v| v.graph } || to_vertices.detect { |v| v.graph }) + graph = v.graph if v + unless graph + Pacer.debug_info << { :error => 'No graph found', :from => self, :to => to_vertices, :graph => graph } + raise "No graph found" + end end - map do |from_v| - to_vertices.map do |to_v| - begin - e = graph.add_edge(nil, from_v, to_v, label) - props.each do |name, value| - e.set_property name.to_s, value + has_props = !props.empty? + first_edge_id = last_edge_id = nil + counter = 0 + graph.managed_transactions do + graph.managed_transaction do + each do |from_v| + to_vertices.to_route.each do |to_v| + counter += 1 + graph.managed_checkpoint if counter % graph.bulk_job_size == 0 + begin + edge = graph.create_edge(nil, from_v, to_v, label.to_s) + first_edge_id ||= edge.get_id + last_edge_id = edge.get_id + if has_props + props.each do |name, value| + edge[name] = value + end + end + rescue => e + puts e.message + end end - rescue => e - puts e.message end end end + if first_edge_id + if last_edge_id != first_edge_id + (first_edge_id..last_edge_id) + else + first_edge_id + end + end end end end diff --git a/lib/pacer/route/mixins.rb b/lib/pacer/route/mixins.rb index 1b76e1ff..e31baadf 100644 --- a/lib/pacer/route/mixins.rb +++ b/lib/pacer/route/mixins.rb @@ -1,6 +1,7 @@ require 'pacer/route/mixin/iterator_mixin' require 'pacer/route/mixin/base' require 'pacer/route/mixin/branchable_route' +require 'pacer/route/mixin/bulk_operations' require 'pacer/route/mixin/route_operations' require 'pacer/route/mixin/single_route' require 'pacer/route/mixin/mixed_route_module' @@ -9,3 +10,4 @@ require 'pacer/route/mixin/edges_route_module' require 'pacer/route/mixin/vertices_route_module' require 'pacer/route/mixin/identity_route_module' +require 'pacer/route/mixin/indexed_route_module' diff --git a/lib/pacer/route/paths_route.rb b/lib/pacer/route/paths_route.rb index 6768f8b8..b0d2ca68 100644 --- a/lib/pacer/route/paths_route.rb +++ b/lib/pacer/route/paths_route.rb @@ -1,6 +1,7 @@ module Pacer::Routes class PathsRoute include Base + include BulkOperations def initialize(back) @back = back @@ -20,12 +21,12 @@ def subgraph(target_graph = nil) raise "Can't create a subgraph within itself." if target_graph == graph target_graph ||= Pacer.tg target_graph.vertex_name ||= graph.vertex_name - each do |path| - path.select { |e| e.is_a? Pacer::VertexMixin }.each do |vertex| + bulk_job(nil, target_graph) do |path| + path_route = path.to_route(:graph => graph) + path_route.v.each do |vertex| vertex.clone_into target_graph end - - path.select { |e| e.is_a? Pacer::EdgeMixin }.each do |edge| + path_route.e.each do |edge| edge.clone_into target_graph end end diff --git a/lib/pacer/support/array_list.rb b/lib/pacer/support/array_list.rb index d76b9c07..bef59d13 100644 --- a/lib/pacer/support/array_list.rb +++ b/lib/pacer/support/array_list.rb @@ -4,3 +4,11 @@ def inspect to_a.inspect end end + +require 'set' +java.util.HashSet +class Java::JavaUtil::HashSet + def inspect + to_set.inspect + end +end diff --git a/lib/pacer/support/enumerable.rb b/lib/pacer/support/enumerable.rb index 37421f44..7d3b816b 100644 --- a/lib/pacer/support/enumerable.rb +++ b/lib/pacer/support/enumerable.rb @@ -1,10 +1,28 @@ # Extend the built-in Enumerable module: module Enumerable + def one? + counter = 0 + each do + return false if counter == 1 + counter += 1 + end + true + end + + def many? + counter = 0 + any? { if counter == 1; true; else; counter += 1; false; end } + end + # Transform the enumerable into a java HashSet. def to_hashset(method = nil, *args) + return self if self.is_a? java.util.HashSet hs = java.util.HashSet.new - iter = self.each + iter = self.each rescue nil + if not iter and respond_to? :iterator + iter = self.iterator + end e = iter.next if method while true @@ -27,10 +45,32 @@ def to_hashset(method = nil, *args) end end - def to_route(info = nil) - r = Pacer::Routes::MixedElementsRoute.new(proc { select { |e| e.is_a? Pacer::ElementMixin } }) - r.pipe_class = nil - r.info = info - r + def to_route(opts = {}) + if self.is_a? Pacer::Routes::Base + self + else + r = Pacer::Routes::MixedElementsRoute.new(proc { select { |e| e.is_a? Pacer::ElementMixin } }) + r.pipe_class = nil + if based_on = opts[:based_on] + r.graph = based_on.graph + r.add_extensions based_on.extensions + r.info = based_on.info if based_on.info + end + r.add_extensions opts[:extensions] if opts[:extensions] + r.graph = opts[:graph] if opts[:graph] + r.info = opts[:info] + r + end + end + + def group_count + result = Hash.new(0) + if block_given? + each { |e| result[yield(e)] += 1 } + else + each { |e| result[e] += 1 } + end + result end + end diff --git a/lib/pacer/tg.rb b/lib/pacer/tg.rb index f7c20287..bc6764b5 100644 --- a/lib/pacer/tg.rb +++ b/lib/pacer/tg.rb @@ -16,12 +16,12 @@ def self.tg(path = nil) # Extend the java class imported from blueprints. class TinkerGraph + include GraphMixin + include GraphTransactionsStub + include ManagedTransactionsMixin include Routes::Base include Routes::GraphRoute - alias vertex getVertex - alias edge getEdge - # Override to return an enumeration-friendly array of vertices. def get_vertices getVertices.to_a @@ -47,8 +47,6 @@ def element_type(et) end end - # Discourage use of the native getVertex and getEdge methods - protected :get_vertex, :getVertex, :get_edge, :getEdge end diff --git a/lib/pacer/utils.rb b/lib/pacer/utils.rb index ab746642..2e61f4ab 100644 --- a/lib/pacer/utils.rb +++ b/lib/pacer/utils.rb @@ -1,37 +1,7 @@ module Pacer module Utils - module GraphAnalysis - - # Returns a TinkerGraph representing the number of each type of node and how - # many edges (by label) point to each other type of node. - def self.structure(graph, type_field = :type) - result = Pacer.tg - types = graph.v.group_count(type_field).inject({}) do |hash, (type, count)| - v = result.add_vertex nil - v[type_field] = type - v[:count] = count - v.graph = result - hash[type] = v - hash - end - result.v.each do |type_node| - begin - edges = graph.v(type_field => type_node[type_field]).out_e - edge_types = edges.group_count do |e| - [e.label, e.in_vertex[type_field]] - end - edge_types.each do |(label, type), count| - puts "edges #{ type_node[type_field] } #{ label } #{ type }: #{ count }" - type_node.to(label, - types[type], - :count => count) - end - rescue => e - puts e.message - end - end - result - end - end + autoload :YFilesExport, 'pacer/utils/y_files' + autoload :GraphAnalysis, 'pacer/utils/graph_analysis' end end + diff --git a/lib/pacer/utils/graph_analysis.rb b/lib/pacer/utils/graph_analysis.rb new file mode 100644 index 00000000..efa2b9c2 --- /dev/null +++ b/lib/pacer/utils/graph_analysis.rb @@ -0,0 +1,37 @@ +module Pacer + module Utils + module GraphAnalysis + + # Returns a TinkerGraph representing the number of each type of node and how + # many edges (by label) point to each other type of node. + def self.structure(graph, type_field = :type) + result = Pacer.tg + types = graph.v.group_count(type_field).inject({}) do |hash, (type, count)| + v = result.create_vertex nil + v[type_field] = type + v[:count] = count + v.graph = result + hash[type] = v + hash + end + result.v.each do |type_node| + begin + edges = graph.v(type_field => type_node[type_field]).out_e + edge_types = edges.group_count do |e| + [e.label, e.in_vertex[type_field]] + end + edge_types.each do |(label, type), count| + puts "edges #{ type_node[type_field] } #{ label } #{ type }: #{ count }" + type_node.to(label, + types[type], + :count => count) + end + rescue => e + puts e.message + end + end + result + end + end + end +end diff --git a/spec/pacer/graph/edge_mixin_spec.rb b/spec/pacer/graph/edge_mixin_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/graph/element_mixin_spec.rb b/spec/pacer/graph/element_mixin_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/graph/transactions_spec.rb b/spec/pacer/graph/transactions_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/graph/vertex_mixin_spec.rb b/spec/pacer/graph/vertex_mixin_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/pipe/block_filter_pipe_spec.rb b/spec/pacer/pipe/block_filter_pipe_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/pipe/labels_filter_pipe_spec.rb b/spec/pacer/pipe/labels_filter_pipe_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/pipe/ruby_pipe_spec.rb b/spec/pacer/pipe/ruby_pipe_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/pipe/type_filter_pipe.rb b/spec/pacer/pipe/type_filter_pipe.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/pipe/variable_store_iterator_pipe.rb b/spec/pacer/pipe/variable_store_iterator_pipe.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/route/branched_route_spec.rb b/spec/pacer/route/branched_route_spec.rb new file mode 100644 index 00000000..c9f083a6 --- /dev/null +++ b/spec/pacer/route/branched_route_spec.rb @@ -0,0 +1,199 @@ +require 'spec_helper' + +describe BranchedRoute do + before :all do + @g = Pacer.tg 'spec/data/pacer.graphml' + @br = @g.v(:type => 'person'). + branch { |b| b.out_e.in_v(:type => 'project') }. + branch { |b| b.out_e.in_v.out_e } + end + + describe '#inspect' do + it 'should include both branches when inspecting' do + @br.inspect.should == + '# Branched { # Edges(OUT_EDGES) -> Vertices(IN_VERTEX, [{:type=>"project"}])> | # Edges(OUT_EDGES) -> Vertices(IN_VERTEX) -> Edges(OUT_EDGES)> }>' + end + end + + it 'should return matches in round robin order by default' do + @br.to_a.should == + [@g.vertex(1), @g.edge(3), + @g.vertex(4), @g.edge(2), + @g.vertex(2), @g.edge(4), + @g.vertex(3), @g.edge(6), @g.edge(5), @g.edge(7)] + end + + it '#exhaustive should return matches in exhaustive merge order' do + @br.exhaustive.to_a.should == + [@g.vertex(1), @g.vertex(4), @g.vertex(2), @g.vertex(3), + @g.edge(3), @g.edge(2), @g.edge(4), @g.edge(6), @g.edge(5), @g.edge(7)] + end + + it { @br.branch_count.should == 2 } + it { @br.should_not be_root } + + describe '#mixed' do + it { @br.mixed.to_a.should == @br.to_a } + end + + describe 'branch chaining bug' do + before do + @linear = Pacer.tg + @a, @b, @c, @d = @linear.add_vertex('a'), @linear.add_vertex('b'), @linear.add_vertex('c'), @linear.add_vertex('d') + @ab = @linear.add_edge nil, @a, @b, 'to' + @bc = @linear.add_edge nil, @b, @c, 'to' + @cd = @linear.add_edge nil, @c, @d, 'to' + @source = VerticesRoute.from_vertex_ids @linear, ['a', 'b'] + + single = @source.branch { |v| v.out_e.in_v }.branch { |v| v.out_e.in_v } + @single_v = single.v + @single_m = single.mixed + + @v = single.v.branch { |v| v.out_e.in_v }.branch { |v| v.out_e.in_v } + @m = single.mixed.branch { |v| v.out_e.in_v }.branch { |v| v.out_e.in_v } + @ve = single.exhaustive.v.branch { |v| v.out_e.in_v }.branch { |v| v.out_e.in_v }.exhaustive + @me = single.exhaustive.mixed.branch { |v| v.out_e.in_v }.branch { |v| v.out_e.in_v }.exhaustive + end + + it { @single_v.count.should == 4 } + it { @single_m.count.should == 4 } + it { @single_v.group_count { |v| v.id }.should == { 'b' => 2, 'c' => 2 } } + it { @single_m.group_count { |v| v.id }.should == { 'b' => 2, 'c' => 2 } } + + it { @v.count.should == 8 } + it { @m.count.should == 8 } + it { @ve.count.should == 8 } + it { @me.count.should == 8 } + + it { @v.group_count { |v| v.id }.should == { 'c' => 4, 'd' => 4 } } + it { @m.group_count { |v| v.id }.should == { 'c' => 4, 'd' => 4 } } + it { @ve.group_count { |v| v.id }.should == { 'c' => 4, 'd' => 4 } } + it { @me.group_count { |v| v.id }.should == { 'c' => 4, 'd' => 4 } } + + it do + @single_v.paths.map(&:to_a).should == + [[@a, @ab, @b], + [@b, @bc, @c], + [@a, @ab, @b], + [@b, @bc, @c]] + end + + it do + @v.to_a.should == [@c, @c, @d, @d, @c, @c, @d, @d] + @v.paths.map(&:to_a).should == + [[@a, @ab, @b, @bc, @c], [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], [@b, @bc, @c, @cd, @d], + [@a, @ab, @b, @bc, @c], [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], [@b, @bc, @c, @cd, @d]] + end + it do + @v.to_a.should == [@c, @c, @d, @d, @c, @c, @d, @d] + @v.paths.map(&:to_a).should == + [[@a, @ab, @b, @bc, @c], [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], [@b, @bc, @c, @cd, @d], + [@a, @ab, @b, @bc, @c], [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], [@b, @bc, @c, @cd, @d]] + end + it do + @v.to_a.should == [@c, @c, @d, @d, @c, @c, @d, @d] + @ve.paths.map(&:to_a).should == + [[@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], + [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], + [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], + [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d]] + end + it do + @me.to_a.should == [@c, @d, @c, @d, @c, @d, @c, @d] + @me.paths.map(&:to_a).should == + [[@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], + [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], + [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], + [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d]] + end + + end + + describe 'chained branch routes' do + describe 'once' do + before do + @once = @g.v.branch { |v| v.v }.branch { |v| v.v }.v + end + + it 'should double each vertex' do + @once.count.should == @g.v.count * 2 + end + + it 'should have 2 of each vertex' do + @once.group_count { |v| v.id.to_i }.should == { 0 => 2, 1 => 2, 2 => 2, 3 => 2, 4 => 2, 5 => 2, 6 => 2 } + end + end + + describe 'twice' do + before do + # the difference must be with the object that's passed to the branch method + single = @g.v.branch { |v| v.v }.branch { |v| v.v } + @twice_v = single.v.branch { |v| v.v }.branch { |v| v.v } + @twice_m = single.mixed.branch { |v| v.v }.branch { |v| v.v } + @twice_v_e = single.exhaustive.v.branch { |v| v.v }.branch { |v| v.v }.exhaustive + @twice_m_e = single.exhaustive.mixed.branch { |v| v.v }.branch { |v| v.v }.exhaustive + end + + it { @twice_v.count.should == @g.v.count * 2 * 2 } + it { @twice_m.count.should == @g.v.count * 2 * 2 } + it { @twice_v_e.count.should == @g.v.count * 2 * 2 } + it { @twice_m_e.count.should == @g.v.count * 2 * 2 } + + describe 'should have 4 of each' do + it { @twice_v.group_count { |v| v.id.to_i }.sort.should == { 0 => 4, 1 => 4, 2 => 4, 3 => 4, 4 => 4, 5 => 4, 6 => 4 }.sort } + it { @twice_m.group_count { |v| v.id.to_i }.sort.should == { 0 => 4, 1 => 4, 2 => 4, 3 => 4, 4 => 4, 5 => 4, 6 => 4 }.sort } + it { @twice_v_e.group_count { |v| v.id.to_i }.sort.should == { 0 => 4, 1 => 4, 2 => 4, 3 => 4, 4 => 4, 5 => 4, 6 => 4 }.sort } + it { @twice_m_e.group_count { |v| v.id.to_i }.sort.should == { 0 => 4, 1 => 4, 2 => 4, 3 => 4, 4 => 4, 5 => 4, 6 => 4 }.sort } + end + end + end + + describe 'route with a custom split pipe' do + before do + @r = @g.v.branch { |person| person.v }.branch { |project| project.v }.branch { |other| other.out_e }.split_pipe(Tackle::TypeSplitPipe).mixed + end + + describe 'vertices' do + it { @r.v.to_a.should == @r.v.uniq.to_a } + it 'should have only all person and project vertices' do + people_and_projects = Set[*@g.v(:type => 'person')] + Set[*@g.v(:type => 'project')] + Set[*@r.v].should == people_and_projects + end + end + + describe 'edges' do + it { @r.e.to_a.should == @r.e.uniq.to_a } + it 'should have out edges from all vertices except person and project' do + # TODO: this type of thing should be much easier + people_and_projects = Set[*@g.v(:type => 'person')] + Set[*@g.v(:type => 'project')] + vertices = @g.v.to_a - people_and_projects.to_a + edges = Set[*vertices.map { |v| v.out_e.to_a }.flatten] + Set[*@r.e].should == edges + end + end + + describe 'chained' do + def add_branch(vertices_path) + vertices_path. + branch { |person| person.out_e.in_v }. + branch { |project| project.v }. + branch { |other| other.out_e.in_v }.split_pipe(Tackle::TypeSplitPipe).v + end + + it 'should have 5 unique elements when run once' do + @g.v.repeat(1) { |repeater| add_branch(repeater) }.count.should == 12 + @g.v.repeat(1) { |repeater| add_branch(repeater) }.uniq.count.should == 5 + end + + it 'should have 4 unique elements when run twice' do + @g.v.repeat(2) { |repeater| add_branch(repeater) }.count.should == 14 + @g.v.repeat(2) { |repeater| add_branch(repeater) }.uniq.count.should == 4 + end + + it 'should have 4 unique elements when run thrice' do + @g.v.repeat(3) { |repeater| add_branch(repeater) }.count.should == 14 + @g.v.repeat(3) { |repeater| add_branch(repeater) }.uniq.count.should == 4 + end + end + end +end diff --git a/spec/pacer/route/context_route_spec.rb b/spec/pacer/route/context_route_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/route/edge_variable_route_spec.rb b/spec/pacer/route/edge_variable_route_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/route/edges_identity_route_spec.rb b/spec/pacer/route/edges_identity_route_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/route/edges_route_spec.rb b/spec/pacer/route/edges_route_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/route/graph_route_spec.rb b/spec/pacer/route/graph_route_spec.rb new file mode 100644 index 00000000..e63fa30a --- /dev/null +++ b/spec/pacer/route/graph_route_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe GraphRoute do + before do + @g = Pacer.tg + end + + describe '#v' do + it { @g.v.should be_an_instance_of(VerticesRoute) } + end + + describe '#e' do + it { @g.e.should be_an_instance_of(EdgesRoute) } + end + + it { @g.should_not be_a(RouteOperations) } +end diff --git a/spec/pacer/route/indexed_edges_route_spec.rb b/spec/pacer/route/indexed_edges_route_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/route/indexed_vertices_route_spec.rb b/spec/pacer/route/indexed_vertices_route_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/route/mixed_elements_route_spec.rb b/spec/pacer/route/mixed_elements_route_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/route/mixed_identity_route_spec.rb b/spec/pacer/route/mixed_identity_route_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/route/mixed_route_spec.rb b/spec/pacer/route/mixed_route_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/route/mixin/base_spec.rb b/spec/pacer/route/mixin/base_spec.rb new file mode 100644 index 00000000..c7a3d289 --- /dev/null +++ b/spec/pacer/route/mixin/base_spec.rb @@ -0,0 +1,98 @@ +require 'spec_helper' + +describe Base do + before :all do + @g = Pacer.tg 'spec/data/pacer.graphml' + end + + describe '#graph' do + it { @g.v.graph.should == @g } + it { @g.v.out_e.graph.should == @g } + it { @g.v.first.graph.should == @g } + it { @g.v.in_e.first.graph.should == @g } + it { @g.v.in_e.out_v.first.graph.should == @g } + end + + describe '#to_a' do + it { Set[*@g.v].should == Set[*@g.vertices] } + it { Set[*(@g.v.to_a)].should == Set[*@g.vertices] } + it { Set[*@g.e].should == Set[*@g.edges] } + it { Set[*(@g.e.to_a)].should == Set[*@g.edges] } + it { @g.v.to_a.count.should == @g.vertices.count } + it { @g.e.to_a.count.should == @g.edges.count } + end + + describe '#inspect' do + it 'should show the path in the resulting string' do + other_projects_by_gremlin_writer = + @g.v(:name => 'gremlin').as(:grem).in_e(:wrote).out_v.out_e(:wrote) { |e| true }.in_v.except(:grem) + other_projects_by_gremlin_writer.inspect.should == + '# :grem -> Edges(IN_EDGES, [:wrote]) -> Vertices(OUT_VERTEX) -> Edges(OUT_EDGES, [:wrote], &block) -> Vertices(IN_VERTEX) -> Vertices(&block)>' + end + + it { @g.inspect.should == '#' } + end + + describe '#root?' do + it { @g.should be_root } + it { @g.v.should be_root } + it { @g.v[3].should_not be_root } + it { @g.v.out_e.should_not be_root } + it { @g.v.out_e.in_v.should_not be_root } + it { @g.v.result.should be_root } + end + + describe '#[]' do + it { @g.v[2].count.should == 1 } + it { @g.v[2].result.is_a?(Pacer::VertexMixin).should be_true } + end + + describe '#from_graph?' do + it { @g.v.should be_from_graph(@g) } + it { @g.v.out_e.should be_from_graph(@g) } + it { @g.v.out_e.should_not be_from_graph(Pacer.tg) } + end + + describe '#each' do + it { @g.v.each.should be_a(java.util.Iterator) } + it { @g.v.out_e.each.should be_a(java.util.Iterator) } + it { @g.v.each.to_a.should == @g.v.to_a } + end + + describe 'property filter' do + it { @g.v(:name => 'pacer').to_a.should == @g.v.select { |v| v[:name] == 'pacer' } } + it { @g.v(:name => 'pacer').count.should == 1 } + end + + describe 'block filter' do + it { @g.v { false }.count.should == 0 } + it { @g.v { true }.count.should == @g.v.count } + it { @g.v { |v| v.out_e.none? }[:name].should == ['blueprints'] } + + it 'should work with paths' do + paths = @g.v.out_e(:wrote).in_v.paths.map(&:to_a) + filtered_paths = @g.v { true }.out_e(:wrote).e { true }.in_v.paths.map(&:to_a) + filtered_paths.should == paths + end + end + + describe '#result' do + it 'should not be nil when no matching vertices' do + empty = @g.v(:name => 'missing').result + empty.should be_a(VerticesRouteModule) + empty.count.should == 0 + end + + it 'should not be nil when no matching edges' do + empty = @g.e(:missing).result + empty.should be_a(EdgesRouteModule) + empty.count.should == 0 + end + + it 'should not be nil when no matching mixed results' do + empty = @g.v.branch { |x| x.out_e(:missing) }.branch { |x| x.out_e(:missing) } + empty.should be_a(MixedRouteModule) + empty.count.should == 0 + end + end +end diff --git a/spec/pacer/route/mixin/route_operations_spec.rb b/spec/pacer/route/mixin/route_operations_spec.rb new file mode 100644 index 00000000..3a8d60fb --- /dev/null +++ b/spec/pacer/route/mixin/route_operations_spec.rb @@ -0,0 +1,75 @@ +require 'spec_helper' + +describe RouteOperations do + before :all do + @g = Pacer.tg 'spec/data/pacer.graphml' + end + + describe '#uniq' do + it 'should be a route' do + @g.v.uniq.should be_an_instance_of(VerticesRoute) + end + + it 'results should be unique' do + @g.e.in_v.group_count(:name).values.sort.last.should > 1 + @g.e.in_v.uniq.group_count(:name).values.sort.last.should == 1 + end + end + + describe '#random' do + it { Set[*@g.v.random(1)].should == Set[*@g.v] } + it { @g.v.random(0).to_a.should == [] } + it 'should have some number of elements more than 1 and less than all' do + range = 1..(@g.v.count - 1) + range.should include(@g.v.random(0.5).count) + end + end + + describe '#as' do + it 'should set the variable to the correct node' do + vars = Set[] + @g.v.as(:a_vertex).in_e(:wrote) { |edge| vars << edge.vars[:a_vertex] }.count + vars.should == Set[*@g.e(:wrote).in_v] + end + + it 'should not break path generation' do + who_wrote_what = nil + @g.v.as(:who).in_e(:wrote).as(:wrote).out_v.as(:what).v { |v| who_wrote_what = [v.vars[:who], v.vars[:wrote], v.vars[:what]] }.paths.each do |path| + path.to_a.should == who_wrote_what + end + end + end + + describe '#repeat' do + it 'should apply the route part twice' do + route = @g.v.repeat(2) { |tail| tail.out_e.in_v }.inspect + route.should == @g.v.out_e.in_v.out_e.in_v.inspect + end + + it 'should apply the route part 3 times' do + route = @g.v.repeat(3) { |tail| tail.out_e.in_v }.inspect + route.should == @g.v.out_e.in_v.out_e.in_v.out_e.in_v.inspect + end + + describe 'with a range' do + before do + @start = @g.vertex(0).v + @route = @start.repeat(1..3) { |tail| tail.out_e.in_v[0] } + end + + it 'should be equivalent to executing each path separately' do + @route.to_a.should == [@start.out_e.in_v.first, + @start.out_e.in_v.out_e.in_v.first, + @start.out_e.in_v.out_e.in_v.out_e.in_v.first] + end + + it { @route.should be_a(BranchedRoute) } + it { @route.back.should be_a(VerticesRoute) } + it { @route.back.back.should be_nil } + end + end + + describe :delete! do + it 'should not try to delete an element twice' + end +end diff --git a/spec/pacer/route/paths_route_spec.rb b/spec/pacer/route/paths_route_spec.rb new file mode 100644 index 00000000..99a643d5 --- /dev/null +++ b/spec/pacer/route/paths_route_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +describe PathsRoute do + before :all do + @g = Pacer.tg 'spec/data/pacer.graphml' + end + + describe '#paths' do + it 'should return the paths between people and projects' do + Set[*@g.v(:type => 'person').out_e.in_v(:type => 'project').paths.map(&:to_a)].should == + Set[[@g.vertex(0), @g.edge(0), @g.vertex(1)], + [@g.vertex(5), @g.edge(1), @g.vertex(4)], + [@g.vertex(5), @g.edge(13), @g.vertex(2)], + [@g.vertex(5), @g.edge(12), @g.vertex(3)]] + end + + it 'should include all elements traversed' do + @g.v.out_e.in_v.paths.each do |path| + path[0].should be_an_instance_of(Pacer::TinkerVertex) + path[1].should be_an_instance_of(Pacer::TinkerEdge) + path[2].should be_an_instance_of(Pacer::TinkerVertex) + path.length.should == 3 + end + end + + end + + describe '#transpose' do + it 'should return the paths between people and projects' do + Set[*@g.v(:type => 'person').out_e.in_v(:type => 'project').paths.transpose].should == + Set[[@g.vertex(0), @g.vertex(5), @g.vertex(5), @g.vertex(5)], + [@g.edge(0), @g.edge(1), @g.edge(13), @g.edge(12)], + [@g.vertex(1), @g.vertex(4), @g.vertex(2), @g.vertex(3)]] + end + end + + describe '#subgraph' do + before do + @sg = @g.v(:type => 'person').out_e.in_v(:type => 'project').subgraph + + @vertices = @g.v(:type => 'person').to_a + @g.v(:type => 'project').to_a + @edges = @g.v(:type => 'person').out_e(:wrote) + end + + it { Set[*@sg.v.ids].should == Set[*@vertices.map { |v| v.id }] } + it { Set[*@sg.e.ids].should == Set[*@edges.map { |e| e.id }] } + + it { @sg.e.labels.uniq.should == ['wrote'] } + it { Set[*@sg.v.map { |v| v.properties }].should == Set[*@vertices.map { |v| v.properties }] } + end +end + diff --git a/spec/pacer/route/vertex_variable_route_spec.rb b/spec/pacer/route/vertex_variable_route_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/route/vertices_identity_route_spec.rb b/spec/pacer/route/vertices_identity_route_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/route/vertices_route_spec.rb b/spec/pacer/route/vertices_route_spec.rb new file mode 100644 index 00000000..d3a4a9c6 --- /dev/null +++ b/spec/pacer/route/vertices_route_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe VerticesRoute do + before do + @g = Pacer.tg 'spec/data/pacer.graphml' + end + + describe '#out_e' do + it { @g.v.out_e.should be_an_instance_of(EdgesRoute) } + it { @g.v.out_e(:label).should be_an_instance_of(EdgesRoute) } + it { @g.v.out_e(:label) { |x| true }.should be_an_instance_of(EdgesRoute) } + it { @g.v.out_e { |x| true }.should be_an_instance_of(EdgesRoute) } + + it { Set[*@g.v.out_e].should == Set[*@g.edges] } + + it { @g.v.out_e.count.should >= 1 } + + it 'with label filter should work with path generation' do + paths = @g.v.out_e.in_v.in_e { |e| e.label == 'wrote' }.out_v.paths.map(&:to_a) + @g.v.out_e.in_v.in_e(:wrote).out_v.paths.map(&:to_a).should == paths + end + end + + describe :add_edges_to do + it 'should not add properties with null values' + + context 'from empty route' do + + end + + context 'to empty array' do + + end + + context 'to nil' do + + end + end +end + diff --git a/spec/pacer/support/array_list_spec.rb b/spec/pacer/support/array_list_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/support/enumerable_spec.rb b/spec/pacer/support/enumerable_spec.rb new file mode 100644 index 00000000..c02e4273 --- /dev/null +++ b/spec/pacer/support/enumerable_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe Enumerable do + describe '#to_hashset' do + it 'should return an empty hashset' do + [].to_hashset.should == java.util.HashSet.new + end + + it 'should not clone an existing hashset' do + hs = java.util.HashSet.new + hs.to_hashset.should equal(hs) + end + + it 'should create a hashset from an array' do + hs = java.util.HashSet.new + hs.add 'a' + hs.add 'b' + ['a', 'b', 'a'].to_hashset.should == hs + end + + it 'should create a hashset from an arraylist' do + hs = java.util.HashSet.new + hs.add 'a' + hs.add 'b' + al = java.util.ArrayList.new + al.add 'a' + al.add 'b' + al.add 'a' + al.to_hashset.should == hs + end + end +end diff --git a/spec/pacer/support/get_java_field_spec.rb b/spec/pacer/support/get_java_field_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/pacer/vertices_route_spec.rb b/spec/pacer/vertices_route_spec.rb deleted file mode 100644 index 79617083..00000000 --- a/spec/pacer/vertices_route_spec.rb +++ /dev/null @@ -1,460 +0,0 @@ -require 'spec_helper' -require 'set' - -include Pacer::Routes - -describe GraphRoute do - before do - @g = Pacer.tg - end - - describe '#v' do - it { @g.v.should be_an_instance_of(VerticesRoute) } - end - - describe '#e' do - it { @g.e.should be_an_instance_of(EdgesRoute) } - end - - it { @g.should_not be_a(RouteOperations) } -end - - -describe VerticesRoute do - before do - @g = Pacer.tg 'spec/data/pacer.graphml' - end - - describe '#out_e' do - it { @g.v.out_e.should be_an_instance_of(EdgesRoute) } - it { @g.v.out_e(:label).should be_an_instance_of(EdgesRoute) } - it { @g.v.out_e(:label) { |x| true }.should be_an_instance_of(EdgesRoute) } - it { @g.v.out_e { |x| true }.should be_an_instance_of(EdgesRoute) } - - it { Set[*@g.v.out_e].should == Set[*@g.edges] } - - it { @g.v.out_e.count.should >= 1 } - - it 'with label filter should work with path generation' do - paths = @g.v.out_e.in_v.in_e { |e| e.label == 'wrote' }.out_v.paths.map(&:to_a) - @g.v.out_e.in_v.in_e(:wrote).out_v.paths.map(&:to_a).should == paths - end - end -end - -describe Base do - before :all do - @g = Pacer.tg 'spec/data/pacer.graphml' - end - - describe '#graph' do - it { @g.v.graph.should == @g } - it { @g.v.out_e.graph.should == @g } - it { @g.v.first.graph.should == @g } - it { @g.v.in_e.first.graph.should == @g } - it { @g.v.in_e.out_v.first.graph.should == @g } - end - - describe '#to_a' do - it { Set[*@g.v].should == Set[*@g.vertices] } - it { Set[*(@g.v.to_a)].should == Set[*@g.vertices] } - it { Set[*@g.e].should == Set[*@g.edges] } - it { Set[*(@g.e.to_a)].should == Set[*@g.edges] } - it { @g.v.to_a.count.should == @g.vertices.count } - it { @g.e.to_a.count.should == @g.edges.count } - end - - describe '#inspect' do - it 'should show the path in the resulting string' do - other_projects_by_gremlin_writer = - @g.v(:name => 'gremlin').as(:grem).in_e(:wrote).out_v.out_e(:wrote) { |e| true }.in_v.except(:grem) - other_projects_by_gremlin_writer.inspect.should == - '#"gremlin"}]) -> :grem -> Edges(IN_EDGES, [:wrote]) -> Vertices(OUT_VERTEX) -> Edges(OUT_EDGES, [:wrote], &block) -> Vertices(IN_VERTEX) -> Vertices(&block)>' - end - - it { @g.inspect.should == '#' } - end - - describe '#root?' do - it { @g.should be_root } - it { @g.v.should be_root } - it { @g.v[3].should_not be_root } - it { @g.v.out_e.should_not be_root } - it { @g.v.out_e.in_v.should_not be_root } - it { @g.v.result.should be_root } - end - - describe '#[]' do - it { @g.v[2].count.should == 1 } - it { @g.v[2].result.is_a?(Pacer::VertexMixin).should be_true } - end - - describe '#from_graph?' do - it { @g.v.should be_from_graph(@g) } - it { @g.v.out_e.should be_from_graph(@g) } - it { @g.v.out_e.should_not be_from_graph(Pacer.tg) } - end - - describe '#each' do - it { @g.v.each.should be_a(java.util.Iterator) } - it { @g.v.out_e.each.should be_a(java.util.Iterator) } - it { @g.v.each.to_a.should == @g.v.to_a } - end - - describe 'property filter' do - it { @g.v(:name => 'pacer').to_a.should == @g.v.select { |v| v[:name] == 'pacer' } } - it { @g.v(:name => 'pacer').count.should == 1 } - end - - describe 'block filter' do - it { @g.v { false }.count.should == 0 } - it { @g.v { true }.count.should == @g.v.count } - it { @g.v { |v| v.out_e.none? }[:name].should == ['blueprints'] } - - it 'should work with paths' do - paths = @g.v.out_e(:wrote).in_v.paths.map(&:to_a) - filtered_paths = @g.v { true }.out_e(:wrote).e { true }.in_v.paths.map(&:to_a) - filtered_paths.should == paths - end - end - - describe '#result' do - it 'should not be nil when no matching vertices' do - empty = @g.v(:name => 'missing').result - empty.should be_a(VerticesRouteModule) - empty.count.should == 0 - end - - it 'should not be nil when no matching edges' do - empty = @g.e(:missing).result - empty.should be_a(EdgesRouteModule) - empty.count.should == 0 - end - - it 'should not be nil when no matching mixed results' do - empty = @g.v.branch { |x| x.out_e(:missing) }.branch { |x| x.out_e(:missing) } - empty.should be_a(MixedRouteModule) - empty.count.should == 0 - end - end -end - -describe RouteOperations do - before :all do - @g = Pacer.tg 'spec/data/pacer.graphml' - end - - describe '#uniq' do - it 'should be a route' do - @g.v.uniq.should be_an_instance_of(VerticesRoute) - end - - it 'results should be unique' do - @g.e.in_v.group_count(:name).values.sort.last.should > 1 - @g.e.in_v.uniq.group_count(:name).values.sort.last.should == 1 - end - end - - describe '#random' do - it { Set[*@g.v.random(1)].should == Set[*@g.v] } - it { @g.v.random(0).to_a.should == [] } - it 'should have some number of elements more than 1 and less than all' do - range = 1..(@g.v.count - 1) - range.should include(@g.v.random(0.5).count) - end - end - - describe '#as' do - it 'should set the variable to the correct node' do - vars = Set[] - @g.v.as(:a_vertex).in_e(:wrote) { |edge| vars << edge.vars[:a_vertex] }.count - vars.should == Set[*@g.e(:wrote).in_v] - end - - it 'should not break path generation' do - who_wrote_what = nil - @g.v.as(:who).in_e(:wrote).as(:wrote).out_v.as(:what).v { |v| who_wrote_what = [v.vars[:who], v.vars[:wrote], v.vars[:what]] }.paths.each do |path| - path.to_a.should == who_wrote_what - end - end - end - - describe '#repeat' do - it 'should apply the route part twice' do - route = @g.v.repeat(2) { |tail| tail.out_e.in_v }.inspect - route.should == @g.v.out_e.in_v.out_e.in_v.inspect - end - - it 'should apply the route part 3 times' do - route = @g.v.repeat(3) { |tail| tail.out_e.in_v }.inspect - route.should == @g.v.out_e.in_v.out_e.in_v.out_e.in_v.inspect - end - - describe 'with a range' do - before do - @start = @g.vertex(0).v - @route = @start.repeat(1..3) { |tail| tail.out_e.in_v[0] } - end - - it 'should be equivalent to executing each path separately' do - @route.to_a.should == [@start.out_e.in_v.first, - @start.out_e.in_v.out_e.in_v.first, - @start.out_e.in_v.out_e.in_v.out_e.in_v.first] - end - - it { @route.should be_a(BranchedRoute) } - it { @route.back.should be_a(VerticesRoute) } - it { @route.back.back.should be_nil } - end - - end - -end - -describe PathsRoute do - before :all do - @g = Pacer.tg 'spec/data/pacer.graphml' - end - - describe '#paths' do - it 'should return the paths between people and projects' do - Set[*@g.v(:type => 'person').out_e.in_v(:type => 'project').paths.map(&:to_a)].should == - Set[[@g.vertex(0), @g.edge(0), @g.vertex(1)], - [@g.vertex(5), @g.edge(1), @g.vertex(4)], - [@g.vertex(5), @g.edge(13), @g.vertex(2)], - [@g.vertex(5), @g.edge(12), @g.vertex(3)]] - end - - it 'should include all elements traversed' do - @g.v.out_e.in_v.paths.each do |path| - path[0].should be_an_instance_of(Pacer::TinkerVertex) - path[1].should be_an_instance_of(Pacer::TinkerEdge) - path[2].should be_an_instance_of(Pacer::TinkerVertex) - path.length.should == 3 - end - end - - end - - describe '#transpose' do - it 'should return the paths between people and projects' do - Set[*@g.v(:type => 'person').out_e.in_v(:type => 'project').paths.transpose].should == - Set[[@g.vertex(0), @g.vertex(5), @g.vertex(5), @g.vertex(5)], - [@g.edge(0), @g.edge(1), @g.edge(13), @g.edge(12)], - [@g.vertex(1), @g.vertex(4), @g.vertex(2), @g.vertex(3)]] - end - end - - describe '#subgraph' do - before do - @sg = @g.v(:type => 'person').out_e.in_v(:type => 'project').subgraph - - @vertices = @g.v(:type => 'person').to_a + @g.v(:type => 'project').to_a - @edges = @g.v(:type => 'person').out_e(:wrote) - end - - it { Set[*@sg.v.ids].should == Set[*@vertices.map { |v| v.id }] } - it { Set[*@sg.e.ids].should == Set[*@edges.map { |e| e.id }] } - - it { @sg.e.labels.uniq.should == ['wrote'] } - it { Set[*@sg.v.map { |v| v.properties }].should == Set[*@vertices.map { |v| v.properties }] } - end -end - -describe BranchedRoute do - before :all do - @g = Pacer.tg 'spec/data/pacer.graphml' - @br = @g.v(:type => 'person'). - branch { |b| b.out_e.in_v(:type => 'project') }. - branch { |b| b.out_e.in_v.out_e } - end - - describe '#inspect' do - it 'should include both branches when inspecting' do - @br.inspect.should == - '#"person"}]) -> Branched { # Edges(OUT_EDGES) -> Vertices(IN_VERTEX, [{:type=>"project"}])> | # Edges(OUT_EDGES) -> Vertices(IN_VERTEX) -> Edges(OUT_EDGES)> }>' - end - end - - it 'should return matches in round robin order by default' do - @br.to_a.should == - [@g.vertex(1), @g.edge(3), - @g.vertex(4), @g.edge(2), - @g.vertex(2), @g.edge(4), - @g.vertex(3), @g.edge(6), @g.edge(5), @g.edge(7)] - end - - it '#exhaustive should return matches in exhaustive merge order' do - @br.exhaustive.to_a.should == - [@g.vertex(1), @g.vertex(4), @g.vertex(2), @g.vertex(3), - @g.edge(3), @g.edge(2), @g.edge(4), @g.edge(6), @g.edge(5), @g.edge(7)] - end - - it { @br.branch_count.should == 2 } - it { @br.should_not be_root } - - describe '#mixed' do - it { @br.mixed.to_a.should == @br.to_a } - end - - describe 'branch chaining bug' do - before do - @linear = Pacer.tg - @a, @b, @c, @d = @linear.add_vertex('a'), @linear.add_vertex('b'), @linear.add_vertex('c'), @linear.add_vertex('d') - @ab = @linear.add_edge nil, @a, @b, 'to' - @bc = @linear.add_edge nil, @b, @c, 'to' - @cd = @linear.add_edge nil, @c, @d, 'to' - @source = VerticesRoute.from_vertex_ids @linear, ['a', 'b'] - - single = @source.branch { |v| v.out_e.in_v }.branch { |v| v.out_e.in_v } - @single_v = single.v - @single_m = single.mixed - - @v = single.v.branch { |v| v.out_e.in_v }.branch { |v| v.out_e.in_v } - @m = single.mixed.branch { |v| v.out_e.in_v }.branch { |v| v.out_e.in_v } - @ve = single.exhaustive.v.branch { |v| v.out_e.in_v }.branch { |v| v.out_e.in_v }.exhaustive - @me = single.exhaustive.mixed.branch { |v| v.out_e.in_v }.branch { |v| v.out_e.in_v }.exhaustive - end - - it { @single_v.count.should == 4 } - it { @single_m.count.should == 4 } - it { @single_v.group_count { |v| v.id }.should == { 'b' => 2, 'c' => 2 } } - it { @single_m.group_count { |v| v.id }.should == { 'b' => 2, 'c' => 2 } } - - it { @v.count.should == 8 } - it { @m.count.should == 8 } - it { @ve.count.should == 8 } - it { @me.count.should == 8 } - - it { @v.group_count { |v| v.id }.should == { 'c' => 4, 'd' => 4 } } - it { @m.group_count { |v| v.id }.should == { 'c' => 4, 'd' => 4 } } - it { @ve.group_count { |v| v.id }.should == { 'c' => 4, 'd' => 4 } } - it { @me.group_count { |v| v.id }.should == { 'c' => 4, 'd' => 4 } } - - it do - @single_v.paths.map(&:to_a).should == - [[@a, @ab, @b], - [@b, @bc, @c], - [@a, @ab, @b], - [@b, @bc, @c]] - end - - it do - @v.to_a.should == [@c, @c, @d, @d, @c, @c, @d, @d] - @v.paths.map(&:to_a).should == - [[@a, @ab, @b, @bc, @c], [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], [@b, @bc, @c, @cd, @d], - [@a, @ab, @b, @bc, @c], [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], [@b, @bc, @c, @cd, @d]] - end - it do - @v.to_a.should == [@c, @c, @d, @d, @c, @c, @d, @d] - @v.paths.map(&:to_a).should == - [[@a, @ab, @b, @bc, @c], [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], [@b, @bc, @c, @cd, @d], - [@a, @ab, @b, @bc, @c], [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], [@b, @bc, @c, @cd, @d]] - end - it do - @v.to_a.should == [@c, @c, @d, @d, @c, @c, @d, @d] - @ve.paths.map(&:to_a).should == - [[@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], - [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], - [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], - [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d]] - end - it do - @me.to_a.should == [@c, @d, @c, @d, @c, @d, @c, @d] - @me.paths.map(&:to_a).should == - [[@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], - [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], - [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d], - [@a, @ab, @b, @bc, @c], [@b, @bc, @c, @cd, @d]] - end - - end - - describe 'chained branch routes' do - describe 'once' do - before do - @once = @g.v.branch { |v| v.v }.branch { |v| v.v }.v - end - - it 'should double each vertex' do - @once.count.should == @g.v.count * 2 - end - - it 'should have 2 of each vertex' do - @once.group_count { |v| v.id.to_i }.should == { 0 => 2, 1 => 2, 2 => 2, 3 => 2, 4 => 2, 5 => 2, 6 => 2 } - end - end - - describe 'twice' do - before do - # the difference must be with the object that's passed to the branch method - single = @g.v.branch { |v| v.v }.branch { |v| v.v } - @twice_v = single.v.branch { |v| v.v }.branch { |v| v.v } - @twice_m = single.mixed.branch { |v| v.v }.branch { |v| v.v } - @twice_v_e = single.exhaustive.v.branch { |v| v.v }.branch { |v| v.v }.exhaustive - @twice_m_e = single.exhaustive.mixed.branch { |v| v.v }.branch { |v| v.v }.exhaustive - end - - it { @twice_v.count.should == @g.v.count * 2 * 2 } - it { @twice_m.count.should == @g.v.count * 2 * 2 } - it { @twice_v_e.count.should == @g.v.count * 2 * 2 } - it { @twice_m_e.count.should == @g.v.count * 2 * 2 } - - describe 'should have 4 of each' do - it { @twice_v.group_count { |v| v.id.to_i }.sort.should == { 0 => 4, 1 => 4, 2 => 4, 3 => 4, 4 => 4, 5 => 4, 6 => 4 }.sort } - it { @twice_m.group_count { |v| v.id.to_i }.sort.should == { 0 => 4, 1 => 4, 2 => 4, 3 => 4, 4 => 4, 5 => 4, 6 => 4 }.sort } - it { @twice_v_e.group_count { |v| v.id.to_i }.sort.should == { 0 => 4, 1 => 4, 2 => 4, 3 => 4, 4 => 4, 5 => 4, 6 => 4 }.sort } - it { @twice_m_e.group_count { |v| v.id.to_i }.sort.should == { 0 => 4, 1 => 4, 2 => 4, 3 => 4, 4 => 4, 5 => 4, 6 => 4 }.sort } - end - end - end - - describe 'route with a custom split pipe' do - before do - @r = @g.v.branch { |person| person.v }.branch { |project| project.v }.branch { |other| other.out_e }.split_pipe(Tackle::TypeSplitPipe).mixed - end - - describe 'vertices' do - it { @r.v.to_a.should == @r.v.uniq.to_a } - it 'should have only all person and project vertices' do - people_and_projects = Set[*@g.v(:type => 'person')] + Set[*@g.v(:type => 'project')] - Set[*@r.v].should == people_and_projects - end - end - - describe 'edges' do - it { @r.e.to_a.should == @r.e.uniq.to_a } - it 'should have out edges from all vertices except person and project' do - # TODO: this type of thing should be much easier - people_and_projects = Set[*@g.v(:type => 'person')] + Set[*@g.v(:type => 'project')] - vertices = @g.v.to_a - people_and_projects.to_a - edges = Set[*vertices.map { |v| v.out_e.to_a }.flatten] - Set[*@r.e].should == edges - end - end - - describe 'chained' do - def add_branch(vertices_path) - vertices_path. - branch { |person| person.out_e.in_v }. - branch { |project| project.v }. - branch { |other| other.out_e.in_v }.split_pipe(Tackle::TypeSplitPipe).v - end - - it 'should have 5 unique elements when run once' do - @g.v.repeat(1) { |repeater| add_branch(repeater) }.count.should == 12 - @g.v.repeat(1) { |repeater| add_branch(repeater) }.uniq.count.should == 5 - end - - it 'should have 4 unique elements when run twice' do - @g.v.repeat(2) { |repeater| add_branch(repeater) }.count.should == 14 - @g.v.repeat(2) { |repeater| add_branch(repeater) }.uniq.count.should == 4 - end - - it 'should have 4 unique elements when run thrice' do - @g.v.repeat(3) { |repeater| add_branch(repeater) }.count.should == 14 - @g.v.repeat(3) { |repeater| add_branch(repeater) }.uniq.count.should == 4 - end - end - end -end diff --git a/spec/pacer_spec.rb b/spec/pacer_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4cc4019f..5d425afd 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,8 +1,11 @@ require 'pacer' +require 'set' Dir['./spec/support/**/*.rb'].map {|f| require f} Dir['./spec/tackle/*.rb'].map {|f| require f} +include Pacer::Routes + module RSpec module Core module Matchers @@ -32,6 +35,7 @@ def in_editor? c.filter_run :focus => true c.run_all_when_everything_filtered = true Pacer.hide_route_elements = true + Pacer.verbose = false c.mock_with :rr # Not sure what this does: ... diff --git a/vendor/blueprints-neo4j-adapter-0.1-SNAPSHOT-standalone.jar b/vendor/blueprints-neo4j-adapter-0.1-SNAPSHOT-standalone.jar index 1f8cdc9f..2be4f981 100644 Binary files a/vendor/blueprints-neo4j-adapter-0.1-SNAPSHOT-standalone.jar and b/vendor/blueprints-neo4j-adapter-0.1-SNAPSHOT-standalone.jar differ diff --git a/vendor/neo4j-lucene-index-0.2-1.2.M04.jar b/vendor/neo4j-lucene-index-0.2-1.2.M04.jar deleted file mode 100644 index 1dfda495..00000000 Binary files a/vendor/neo4j-lucene-index-0.2-1.2.M04.jar and /dev/null differ diff --git a/vendor/neo4j-lucene-index-0.2-1.2.M05.jar b/vendor/neo4j-lucene-index-0.2-1.2.M05.jar new file mode 100644 index 00000000..5d656687 Binary files /dev/null and b/vendor/neo4j-lucene-index-0.2-1.2.M05.jar differ diff --git a/vendor/pipes-0.2-SNAPSHOT-standalone.jar b/vendor/pipes-0.2-SNAPSHOT-standalone.jar index a51e72b4..f1ec18df 100644 Binary files a/vendor/pipes-0.2-SNAPSHOT-standalone.jar and b/vendor/pipes-0.2-SNAPSHOT-standalone.jar differ