Permalink
Browse files

Merge pull request #32 from pangloss/payload

Allow elements to have a payload.
  • Loading branch information...
2 parents 49112be + 921be8a commit 417765cfb2223ab684435b7f26eaaa38fc0bf3b2 @pangloss committed Oct 1, 2012
@@ -0,0 +1,44 @@
+module Pacer
+ module Payload
+ class Element
+ include com.tinkerpop.blueprints.Element
+ extend Forwardable
+
+ def initialize(element, payload = nil)
+ @element = element
+ self.payload = payload
+ end
+
+ def inspect
+ "#<Payload #{ element.inspect } -- #{ payload.inspect }>"
+ end
+
+ attr_reader :element
+ attr_accessor :payload
+ end
+
+ class Edge < Element
+ include com.tinkerpop.blueprints.Edge
+
+ def_delegators :@element,
+ # Object
+ :equals, :toString, :hashCode,
+ # Element
+ :getId, :getPropertyKeys, :getProperty, :setProperty, :removeProperty, :getRawElement,
+ # Edge
+ :getLabel, :getVertex, :getRawEdge
+ end
+
+ class Vertex < Element
+ include com.tinkerpop.blueprints.Vertex
+
+ def_delegators :@element,
+ # Object
+ :equals, :toString, :hashCode,
+ # Element
+ :getId, :getPropertyKeys, :getProperty, :setProperty, :removeProperty, :getRawElement,
+ # Vertex
+ :getEdges, :getVertices, :query, :getRawVertex
+ end
+ end
+end
@@ -29,6 +29,24 @@ def subgraph(target_graph = nil, opts = {})
target_graph
end
+ def payloads
+ map element_type: :path do |path|
+ path.map do |e|
+ if e.is_a? Pacer::Payload::Element
+ e.payload
+ elsif e.is_a? Pacer::Wrappers::ElementWrapper
+ e.element_payload
+ end
+ end
+ end
+ end
+
+ def compact_paths
+ map element_type: :path do |path|
+ path.compact
+ end
+ end
+
protected
def configure_iterator(iter)
View
@@ -48,6 +48,8 @@ module SideEffect end
require 'pacer/blueprints/ruby_graph'
require 'pacer/blueprints/multi_graph'
+require 'pacer/blueprints/payload_elements'
+
require 'pacer/support/array_list'
require 'pacer/support/enumerable'
require 'pacer/support/proc'
@@ -86,6 +88,7 @@ module SideEffect end
require 'pacer/transform/scatter'
require 'pacer/transform/has_count_cap'
require 'pacer/transform/sort_section'
+require 'pacer/transform/payload'
require 'pacer/side_effect/aggregate'
require 'pacer/side_effect/as'
@@ -10,39 +10,43 @@ module PathRoute
# -- becomes --
# [a [b [c]
# [d]]
- # [e [f
- # g]]]
- def tree
- wrapped.chain_route transform: :path_tree, element_type: :object
+ # [e [f]
+ # [g]]]
+
+ # The default comparator block is { |prev, current| prev == current }
+ def tree(&block)
+ wrapped.chain_route transform: :path_tree, element_type: :object, compare: block
end
end
end
end
module Transform
module PathTree
+ attr_accessor :compare
+
protected
def attach_pipe(end_pipe)
- pipe = PathTreePipe.new
+ pipe = PathTreePipe.new compare
pipe.setStarts end_pipe
pipe
end
-
class PathTreePipe < Pacer::Pipes::RubyPipe
- def initialize
- super
+ def initialize(compare = nil)
+ super()
self.building_path = nil
self.prev_path = nil
+ self.compare = compare || proc { |a, b| a == b }
end
# NOTE: doesn't handle variable length paths yet...
def processNextStart()
while true
path = starts.next
if building_path
- if path.first == building_path.first
+ if compare.call path.first, building_path.first
add_path path
else
return next_path(path)
@@ -63,18 +67,24 @@ def processNextStart()
private
- attr_accessor :building_path, :prev_path
+ attr_accessor :building_path, :prev_path, :compare
def make(path)
- path.reverse.inject(nil) { |inner, e| [e, inner].compact }
+ path.reverse.inject(nil) do |inner, e|
+ if inner
+ [e, inner]
+ else
+ [e]
+ end
+ end
end
def add_path(path)
working = building_path
- (1..path.length).each do |pos|
+ (1..path.length - 1).each do |pos|
current = path[pos]
prev = prev_path[pos]
- if current == prev
+ if compare.call current, prev
working = working.last
else
if pos < path.length
@@ -0,0 +1,50 @@
+module Pacer::Core::Graph::ElementRoute
+ def payload(&block)
+ chain_route transform: :payload, block: block
+ end
+end
+
+module Pacer::Transform
+ module Payload
+ attr_accessor :block
+
+ protected
+
+ def attach_pipe(end_pipe)
+ pipe = PayloadPipe.new(self, block)
+ pipe.setStarts end_pipe if end_pipe
+ pipe
+ end
+
+ class PayloadPipe < Pacer::Pipes::RubyPipe
+ field_reader :currentEnd
+
+ attr_reader :block, :wrapper
+
+ def initialize(route, block)
+ super()
+ if route.element_type == :edge
+ @wrapper = Pacer::Payload::Edge
+ elsif route.element_type == :vertex
+ @wrapper = Pacer::Payload::Vertex
+ else
+ fail Pacer::ClientError, 'Can not use PayloadPipe on non-element data'
+ end
+ block ||= proc { |el| nil }
+ @block = Pacer::Wrappers::WrappingPipeFunction.new route, block
+ end
+
+ def processNextStart
+ el = starts.next
+ @wrapper.new el, block.call(el)
+ end
+
+ def getPathToHere
+ path = super
+ path.remove path.size - 1
+ path.add currentEnd
+ path
+ end
+ end
+ end
+end
@@ -4,9 +4,12 @@ class EdgeWrapper < ElementWrapper
include Pacer::Core::Graph::EdgesRoute
def_delegators :@element,
- :getId, :getLabel, :getPropertyKeys, :getProperty, :setProperty, :removeProperty,
- :getVertex,
- :getRawEdge
+ # Object
+ :equals, :toString, :hashCode,
+ # Element
+ :getId, :getPropertyKeys, :getProperty, :setProperty, :removeProperty, :getRawElement,
+ # Edge
+ :getLabel, :getVertex, :getRawEdge
class << self
def wrappers
@@ -160,6 +160,10 @@ def <=>(other)
display_name.to_s <=> other.display_name.to_s
end
+ def element_payload
+ element.payload if element.is_a? Pacer::Payload::Element
+ end
+
protected
def after_initialize
@@ -4,9 +4,12 @@ class VertexWrapper < ElementWrapper
include Pacer::Core::Graph::VerticesRoute
def_delegators :@element,
- :getId, :getPropertyKeys, :getProperty, :setProperty, :removeProperty,
- :getEdges,
- :getRawVertex
+ # Object
+ :equals, :toString, :hashCode,
+ # Element
+ :getId, :getPropertyKeys, :getProperty, :setProperty, :removeProperty, :getRawElement,
+ # Vertex
+ :getEdges, :getVertices, :query, :getRawVertex
class << self
def wrappers
@@ -0,0 +1,97 @@
+require 'spec_helper'
+
+Run.tg :read_only do
+ use_pacer_graphml_data :read_only
+
+ describe Pacer::Transform::PathTree do
+ let(:first) do
+ graph.v(type: 'person').payload do |p|
+ { pos: 'first',
+ type: p[:type],
+ name: p[:name] }
+ end.lookahead { |v| v.out_e.in_v(type: 'project') }
+ end
+ let(:second) do
+ first.out_e.in_v(type: 'project').payload do |prj|
+ { pos: 'second',
+ type: 'project',
+ name: prj[:name] }
+ end
+ end
+ let(:paths) { second.paths }
+ let(:payloads) { paths.payloads }
+ let(:compacted) { payloads.compact_paths }
+
+ describe 'basic paths' do
+ subject { paths }
+
+ its(:count) { should == 4 }
+
+ it 'should have 3 elements' do
+ paths.each { |x| x.length.should == 3 }
+ end
+ end
+
+ describe 'tree of paths' do
+ subject { paths.tree }
+
+ its(:count) { should == first.count }
+ its(:first) { should be_a Array }
+
+ it 'should be a tree' do
+ (pangloss, wrote, pacer), (a2, e2, b2), (_, e3, b3), (_, e4, b4) = paths.to_a
+ subject.to_a.should == [
+ [pangloss, [wrote, [pacer]]],
+ [a2, [e2, [b2]],
+ [e3, [b3]],
+ [e4, [b4]]]
+ ]
+ end
+ end
+
+ describe 'tree of payloads' do
+ subject { payloads.tree }
+
+ its(:count) { should == first.count }
+ its(:first) { should be_a Array }
+
+ it 'should be a tree' do
+ (pangloss, wrote, pacer), (a2, _, b2), (_, _, b3), (_, _, b4) = payloads.to_a
+ subject.to_a.should == [
+ [pangloss, [wrote, [pacer]]],
+ [a2, [nil, [b2],
+ [b3],
+ [b4]]]
+ ]
+ end
+ end
+
+ describe 'tree of compacted payloads' do
+ subject { compacted.tree }
+
+ its(:count) { should == first.count }
+ its(:first) { should be_a Array }
+
+ it 'should be a tree' do
+ (pangloss, pacer), (a2, b2), (_, b3), (_, b4) = compacted.to_a
+ subject.to_a.should == [
+ [pangloss, [pacer]],
+ [a2, [b2],
+ [b3],
+ [b4]]
+ ]
+ end
+ end
+
+ describe 'tree of compacted payloads by type' do
+ subject { compacted.tree { |a, b| a[:type] == b[:type] } }
+
+ it 'should be a tree, taking the first match' do
+ pangloss, pacer = compacted.first
+ subject.to_a.should == [
+ [pangloss, [pacer]]
+ ]
+ end
+ end
+ end
+end

0 comments on commit 417765c

Please sign in to comment.