red_grape 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,39 @@
1
+ require 'red_grape/pipe/base'
2
+
3
+ module RedGrape
4
+ module Pipe
5
+ class LoopPipe < Pipe::Base
6
+ def pass(obj, context) # TODO: 無理やり・・・
7
+ condition = self.opts.first
8
+ label = self.opts[1]
9
+
10
+ looped_pipe = self.prev.dup
11
+ looped_pipe.prev = self.prev.prev
12
+ (label - 1).times do
13
+ prev = looped_pipe.prev.dup
14
+ prev.next = looped_pipe
15
+ looped_pipe = prev
16
+ end
17
+
18
+ context.loops += 1
19
+ while context.eval :it => obj, &condition
20
+ pipe = looped_pipe
21
+ while pipe
22
+ obj = obj.pass_through pipe, context
23
+ pipe = pipe.next
24
+ end
25
+ context.loops += 1
26
+ end
27
+ context.loops = 1
28
+
29
+ if self.last?
30
+ obj
31
+ else
32
+ context.push_history obj do |ctx|
33
+ obj.pass_through self.next, ctx
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,37 @@
1
+ require 'red_grape/pipe/base'
2
+
3
+ module RedGrape
4
+ module Pipe
5
+ module Out
6
+ def out(*opts)
7
+ OutPipe.new self, *opts
8
+ end
9
+ end
10
+
11
+ class OutPipe < Pipe::Base
12
+ def pipe_name
13
+ @opts.empty? ? super : "#{super}(#{@opts.first})"
14
+ end
15
+
16
+ def pass(obj, context)
17
+ case obj
18
+ when RedGrape::Vertex
19
+ group =
20
+ if self.opts.empty? or self.opts.first.empty? # TODO $B$J$<$+(Blabel$B$K(B[]$B$,F~$C$F$k(B
21
+ VertexGroup.new obj._out_edges.map(&:target)
22
+ else
23
+ label = self.opts.first
24
+ VertexGroup.new obj._out_edges.find_all{|e| e.label == label}.map(&:target)
25
+ end
26
+ if self.last?
27
+ group
28
+ else
29
+ context.push_history obj do |ctx|
30
+ group.pass_through self.next, ctx
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ require 'red_grape/pipe/base'
2
+ require 'red_grape/path_group'
3
+
4
+ module RedGrape
5
+ module Pipe
6
+ class PathsPipe < Pipe::Base
7
+ def pass(obj, context)
8
+ case obj
9
+ when 'TODO'
10
+ else
11
+ context.push_history obj do |ctx|
12
+ if self.last?
13
+ #ctx.history.dup
14
+ PathGroup.new ctx.history
15
+ else
16
+ # TODO
17
+ raise 'not implemented'
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ require 'red_grape/pipe/base'
2
+
3
+ module RedGrape
4
+ module Pipe
5
+ class PropertyPipe < Pipe::Base
6
+ def pipe_name
7
+ "#{super}(#{@opts.first})"
8
+ end
9
+
10
+ def pass(obj, context)
11
+ case obj
12
+ when RedGrape::Vertex
13
+ prop = obj[self.opts.first]
14
+ if self.last?
15
+ prop
16
+ else
17
+ context.push_history obj do |ctx|
18
+ prop.pass_through self.next, ctx
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ require 'red_grape/pipe/base'
2
+
3
+ module RedGrape
4
+ module Pipe
5
+ module SideEffect
6
+ def side_effect(&block)
7
+ SideEffectPipe.new self, block
8
+ end
9
+ end
10
+
11
+ class SideEffectPipe < Pipe::Base
12
+ def pass(obj, context)
13
+ side_effect = self.opts.first
14
+ context.eval :it => obj, &side_effect
15
+ if self.last?
16
+ obj
17
+ else
18
+ obj.pass_through self.next, context
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ require 'red_grape/pipe/base'
2
+
3
+ module RedGrape
4
+ module Pipe
5
+ class TransformPipe < Pipe::Base
6
+ def pass(obj, context)
7
+ case obj
8
+ when 'TODO'
9
+ else
10
+ transformer = self.opts.first
11
+ val = context.eval :it => obj, &transformer
12
+ if self.last?
13
+ val
14
+ else
15
+ raise 'not implemented'
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,46 @@
1
+ require 'red_grape/property_description'
2
+
3
+ module RedGrape
4
+ class PropertiedObject
5
+ def initialize(graph, opts={})
6
+ @graph = graph
7
+ @property = {}
8
+ @property_description = opts[:property_description] || {}
9
+ @property_description.each do |k ,v|
10
+ if v.is_a? Array
11
+ v = PropertyDescription.new(*v)
12
+ elsif v.is_a? Hash
13
+ v = PropertyDescription.new v[:name], v[:type], v[:default]
14
+ end
15
+ @property_description[k] = v
16
+ set_property k, v.default if v.has_default?
17
+ end
18
+ end
19
+
20
+ def set_property(kid, v)
21
+ # TODO: should be refactored
22
+ desc = @property_description[kid]
23
+ if desc.accessible? v
24
+ self[desc.name] = v
25
+ elsif desc.convertable? v
26
+ self[desc.name] = desc.convert v
27
+ else
28
+ raise ArgumentError.new "#{kid} should be #{desc.type}."
29
+ end
30
+ end
31
+
32
+ def []=(k, v)
33
+ # TODO: type check?
34
+ @property[k.to_s] = v
35
+ end
36
+
37
+ def [](k)
38
+ @property[k.to_s]
39
+ end
40
+
41
+ # TODO: $B$$$i$J$$!)(B
42
+ def pass_through(pipe, context)
43
+ pipe.pass self, context
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,47 @@
1
+ module RedGrape
2
+ class PropertyDescription
3
+ attr_reader :name, :type, :default
4
+
5
+ def initialize(name, type, default=nil)
6
+ @name = name
7
+ @type = type
8
+ @default = default
9
+ end
10
+
11
+ def accessible?(val)
12
+ case type
13
+ when 'int', 'integer'
14
+ val.is_a? Integer
15
+ else
16
+ true
17
+ end
18
+ end
19
+
20
+ def convertable?(val)
21
+ case type
22
+ when 'int', 'integer'
23
+ val =~ /^\d+$/
24
+ else
25
+ true
26
+ end
27
+ end
28
+
29
+ def convert(val)
30
+ case type
31
+ when 'int', 'integer'
32
+ val.to_i
33
+ else
34
+ val
35
+ end
36
+ end
37
+
38
+ def has_default?
39
+ not @default.nil?
40
+ end
41
+
42
+ def to_s
43
+ "{#{@name}:#{@type}:#{@default}}"
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,46 @@
1
+ require 'red_grape/propertied_object'
2
+
3
+ module RedGrape
4
+ class Vertex < PropertiedObject
5
+ include RedGrape::Pipe::Out
6
+ include RedGrape::Pipe::OutE
7
+ include RedGrape::Pipe::SideEffect
8
+ include RedGrape::Pipe::As
9
+
10
+ def initialize(graph, id, opts={})
11
+ super graph, opts
12
+ @id = id
13
+ @out_edges = []
14
+ @in_edges = []
15
+ end
16
+
17
+ def _id
18
+ @id
19
+ end
20
+
21
+ def _in_edges
22
+ @in_edges
23
+ end
24
+
25
+ def _out_edges
26
+ @out_edges
27
+ end
28
+
29
+ def add_out_edge(edge)
30
+ @out_edges << edge
31
+ end
32
+
33
+ def add_in_edge(edge)
34
+ @in_edges << edge
35
+ end
36
+
37
+ def to_s
38
+ "v[#{_id}]"
39
+ #"v[id(#{_id}):#{@property}]"
40
+ end
41
+
42
+ def method_missing(name, *args, &block)
43
+ self[name.to_s] or raise NoMethodError.new(name.to_s)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,72 @@
1
+ require 'red_grape/vertex'
2
+
3
+ #TODO delegate to array
4
+ module RedGrape
5
+ class VertexGroup < Vertex
6
+ def initialize(group=[])
7
+ @group = group
8
+ end
9
+
10
+ def _group
11
+ @group
12
+ end
13
+
14
+ def pass_through(pipe, context)
15
+ @group.map! do |v|
16
+ pipe.pass v, context
17
+ end
18
+ @group.reject! do |v|
19
+ v.nil? or (v.respond_to?(:empty?) and v.empty?) # TODO
20
+ end
21
+ @group.map! do |v|
22
+ v.is_a?(self.class) ? v._group : v
23
+ end
24
+ #@group.flatten!
25
+ flatten_group!
26
+ @group.all? {|e| e.is_a? self.class} ? self : @group
27
+ end
28
+
29
+ def flatten_group
30
+ ret = []
31
+ @group.each do |v|
32
+ case v
33
+ when Array
34
+ ret += v.flatten
35
+ when self.class
36
+ ret += v._group.flatten_group
37
+ else
38
+ ret << v
39
+ end
40
+ end
41
+ ret
42
+ end
43
+
44
+ def flatten_group!
45
+ @group = flatten_group
46
+ end
47
+
48
+ def size
49
+ @group.size
50
+ end
51
+
52
+ def map(&block)
53
+ @group.map(&block)
54
+ end
55
+
56
+ def sort(&block)
57
+ @group.sort
58
+ end
59
+
60
+ def empty?
61
+ @group.empty?
62
+ end
63
+
64
+ def to_a
65
+ @group.dup.map{|e| e.is_a?(self.class) ? e.to_a : e}
66
+ end
67
+
68
+ def to_s
69
+ "[#{@group.map(&:to_s).join ', '}]"
70
+ end
71
+ end
72
+ end
data/lib/red_grape.rb ADDED
@@ -0,0 +1,31 @@
1
+ require 'nokogiri'
2
+ require 'red_grape/pipe/in_pipe'
3
+ require 'red_grape/pipe/out_pipe'
4
+ require 'red_grape/pipe/property_pipe'
5
+ require 'red_grape/pipe/paths_pipe'
6
+ require 'red_grape/pipe/filter_pipe'
7
+ require 'red_grape/pipe/transform_pipe'
8
+ require 'red_grape/pipe/side_effect_pipe'
9
+ require 'red_grape/pipe/if_then_else_pipe'
10
+ require 'red_grape/pipe/back_pipe'
11
+ require 'red_grape/pipe/as_pipe'
12
+ require 'red_grape/pipe/loop_pipe'
13
+ require 'red_grape/pipe/has_pipe'
14
+ require 'red_grape/pipe/out_e_pipe'
15
+ require 'red_grape/pipe/in_v_pipe'
16
+ require 'red_grape/graph'
17
+
18
+ module RedGrape
19
+ VERSION = '0.0.1'
20
+
21
+ module_function
22
+ def load_graph(filename)
23
+ Graph.load filename
24
+ end
25
+ end
26
+
27
+ class Object
28
+ def pass_through(pipe, context)
29
+ pipe.pass self, context
30
+ end
31
+ end
@@ -0,0 +1,80 @@
1
+ require 'test/unit'
2
+ require 'stringio'
3
+ require 'red_grape'
4
+
5
+ class GraphTest < Test::Unit::TestCase
6
+ def test_vertex
7
+ graph = RedGrape::Graph.new
8
+ graph.add_vertex id:1, val:'a'
9
+ graph.add_vertex id:2, val:'b'
10
+ graph.add_vertex id:3, val:'c'
11
+
12
+ assert_equal 3, graph.vertex.size
13
+ assert_equal 1, graph.vertex(1)._id
14
+ assert_equal 2, graph.vertex(1, 2).size
15
+ assert_equal 2, graph.vertex([1, 2]).size
16
+ assert_equal 3, graph.vertex(:all).size
17
+ end
18
+
19
+ def test_construct
20
+ graph = RedGrape::Graph.new
21
+ v1 = graph.add_vertex 1, name:'yasushi'
22
+ v2 = graph.add_vertex 2, name:'ando'
23
+ e12 = graph.add_edge 1, :fullname, v1, v2
24
+
25
+ assert_equal 1, v1._out_edges.size
26
+ assert_equal 0, v1._in_edges.size
27
+ assert_equal e12, v1._out_edges.first
28
+
29
+ assert_equal 0, v2._out_edges.size
30
+ assert_equal 1, v2._in_edges.size
31
+ assert_equal e12, v2._in_edges.first
32
+ end
33
+
34
+ def test_load
35
+ data = <<-EOS
36
+ <?xml version="1.0" encoding="UTF-8"?>
37
+ <graphml xmlns="http://graphml.graphdrawing.org/xmlns"
38
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
39
+ xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
40
+ http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
41
+ <key id="weight" for="edge" attr.name="weight" attr.type="float"/>
42
+ <key id="name" for="node" attr.name="name" attr.type="string"/>
43
+ <graph id="G" edgedefault="directed">
44
+ <node id="1">
45
+ <data key="name">marko</data>
46
+ </node>
47
+ <node id="2">
48
+ <data key="name">vadas</data>
49
+ </node>
50
+ <node id="3">
51
+ <data key="name">lop</data>
52
+ </node>
53
+ <edge id="4" source="1" target="2" label="knows">
54
+ <data key="weight">0.5</data>
55
+ </edge>
56
+ <edge id="5" source="1" target="3" label="knows">
57
+ <data key="weight">1.0</data>
58
+ </edge>
59
+ </graph>
60
+ </graphml>
61
+ EOS
62
+ graph = RedGrape::Graph.load data
63
+ assert_equal 'marko', graph.vertex(1).name
64
+ assert_equal 'vadas', graph.vertex(2).name
65
+ assert_equal 'lop', graph.vertex(3).name
66
+ assert_equal 'marko', graph.edge(4).source.name
67
+ assert_equal 'vadas', graph.edge(4).target.name
68
+ assert_equal 'marko', graph.edge(5).source.name
69
+ assert_equal 'lop', graph.edge(5).target.name
70
+
71
+ graph = RedGrape::Graph.load StringIO.new(data)
72
+ assert_equal 'marko', graph.vertex(1).name
73
+ assert_equal 'vadas', graph.vertex(2).name
74
+ assert_equal 'lop', graph.vertex(3).name
75
+ assert_equal 'marko', graph.edge(4).source.name
76
+ assert_equal 'vadas', graph.edge(4).target.name
77
+ assert_equal 'marko', graph.edge(5).source.name
78
+ assert_equal 'lop', graph.edge(5).target.name
79
+ end
80
+ end
@@ -0,0 +1,101 @@
1
+ require 'test/unit'
2
+ require 'red_grape'
3
+
4
+ # test the patterns shown in: http://markorodriguez.com/2011/08/03/on-the-nature-of-pipes/
5
+ class OnTheNatureOfPipesTest < Test::Unit::TestCase
6
+ def setup
7
+ @graph = RedGrape.load_graph 'data/graph-example-1.xml'
8
+ end
9
+
10
+ def test_basic
11
+ assert_equal '1',
12
+ @graph.v(1)._id
13
+
14
+ assert_equal %w(2 3 4),
15
+ @graph.v(1).out.take.map(&:_id).sort
16
+
17
+ assert_equal %w(josh lop vadas),
18
+ @graph.v(1).out.name.take.sort
19
+
20
+ paths = @graph.v(1).out.name.paths.take
21
+ assert_equal 3,
22
+ paths.size
23
+
24
+ assert_equal [3, 3, 3],
25
+ paths.map(&:size)
26
+
27
+ assert_equal %w[OutPipe PropertyPipe(name) PathsPipe],
28
+ @graph.v(1).out.name.paths.to_a
29
+ end
30
+
31
+ def test_filter
32
+ assert_equal %w(2 4),
33
+ @graph.v(1).out('knows').take.map(&:_id).sort
34
+
35
+ assert_equal %W(2),
36
+ @graph.v(1).out('knows').filter{it.age < 30}.take.map(&:_id).sort
37
+
38
+ assert_equal %W(vadas),
39
+ @graph.v(1).out('knows').filter{it.age < 30}.name.take.sort
40
+
41
+ assert_equal [5],
42
+ @graph.v(1).out('knows').filter{it.age < 30}.name.transform{it.size}.take.sort
43
+
44
+ assert_equal %w[OutPipe(knows) FilterPipe PropertyPipe(name) TransformPipe],
45
+ @graph.v(1).out('knows').filter{it.age < 30}.name.transform{it.size}.to_a
46
+ end
47
+
48
+ def test_side_effect
49
+ assert_equal 'marko',
50
+ @graph.v(1).side_effect{@x = it}.take.name
51
+
52
+ assert_equal %w(3),
53
+ @graph.v(1).side_effect{@x = it}.out('created').take.map(&:_id).sort
54
+
55
+ assert_equal %w(1 4 6),
56
+ @graph.v(1).side_effect{@x = it}.out('created').in('created').take.map(&:_id).sort
57
+
58
+ assert_equal %w(4 6),
59
+ @graph.v(1).side_effect{@x = it}.out('created').in('created').filter{it != @x}.take.map(&:_id).sort
60
+
61
+ assert_equal %w(SideEffectPipe OutPipe(created) InPipe(created) FilterPipe),
62
+ @graph.v(1).side_effect{@x = it}.out('created').in('created').filter{it != @x}.to_a
63
+ end
64
+
65
+ def test_if_then_else
66
+ #assert_equal ['vadas', ['ripple', 'lop']],
67
+ assert_equal ['vadas', 'ripple', 'lop'],
68
+ @graph.v(1).out('knows').if_then_else(proc{it.age < 30}, proc{it.name}, proc{it.out('created').name}).take.to_a
69
+ end
70
+
71
+ def test_back
72
+ assert_equal %w(josh vadas),
73
+ @graph.v(1).out('knows').name.take.to_a.sort
74
+
75
+ assert_equal %w(vadas),
76
+ @graph.v(1).out('knows').name.filter{it[0] == 'v'}.take.to_a
77
+
78
+ assert_equal %w(2),
79
+ @graph.v(1).out('knows').name.filter{it[0] == 'v'}.back(2).take.to_a.map(&:_id)
80
+
81
+ assert_equal %w(OutPipe(knows) PropertyPipe(name) FilterPipe BackPipe),
82
+ @graph.v(1).out('knows').name.filter{it[0] == 'v'}.back(2).to_a
83
+
84
+ assert_equal %w(2 4),
85
+ @graph.v(1).out('knows').as('here').name.filter{it[0] == 'v'}.back('here').take.to_a.map(&:_id)
86
+ end
87
+
88
+ def test_loop
89
+ assert_equal %w(3 5),
90
+ @graph.v(1).out.out.take.to_a.map(&:_id).sort
91
+
92
+ assert_equal %w(3 5),
93
+ @graph.v(1).out.loop(1){loops < 3}.take.to_a.map(&:_id).sort
94
+
95
+ assert_equal %w(lop ripple),
96
+ @graph.v(1).out.loop(1){loops < 3}.name.take.sort
97
+
98
+ assert_equal %w(OutPipe LoopPipe PropertyPipe(name)),
99
+ @graph.v(1).out.loop(1){loops < 3}.name.to_a
100
+ end
101
+ end
@@ -0,0 +1,42 @@
1
+ require 'test/unit'
2
+ require 'red_grape'
3
+ require 'red_grape/propertied_object'
4
+ require 'red_grape/property_description'
5
+
6
+ class PropertiedObjectTest < Test::Unit::TestCase
7
+ def test_new
8
+ g = RedGrape::Graph.new
9
+ v = RedGrape::PropertiedObject.new g, property_description:{
10
+ name:RedGrape::PropertyDescription.new('name', 'string'),
11
+ sex:RedGrape::PropertyDescription.new('sex', 'string', 'male'),
12
+ age:RedGrape::PropertyDescription.new('age', 'integer')
13
+ }
14
+ desc = v.instance_eval '@property_description'
15
+ assert_equal 'name', desc[:name].name
16
+ assert_equal 'string', desc[:name].type
17
+ assert_nil desc[:name].default
18
+ assert_equal 'male', desc[:sex].default
19
+
20
+ v = RedGrape::PropertiedObject.new g, property_description:{
21
+ name:['name', 'string'],
22
+ sex:['sex', 'string', 'male'],
23
+ age:['age', 'integer']
24
+ }
25
+ desc = v.instance_eval '@property_description'
26
+ assert_equal 'name', desc[:name].name
27
+ assert_equal 'string', desc[:name].type
28
+ assert_nil desc[:name].default
29
+ assert_equal 'male', desc[:sex].default
30
+
31
+ v = RedGrape::PropertiedObject.new g, property_description:{
32
+ name:{name:'name', type:'string'},
33
+ sex:{name:'sex', type:'string', default:'male'},
34
+ age:{name:'age', type:'integer'}
35
+ }
36
+ desc = v.instance_eval '@property_description'
37
+ assert_equal 'name', desc[:name].name
38
+ assert_equal 'string', desc[:name].type
39
+ assert_nil desc[:name].default
40
+ assert_equal 'male', desc[:sex].default
41
+ end
42
+ end
@@ -0,0 +1,8 @@
1
+ require "test/unit"
2
+ require "red_grape"
3
+
4
+ class TestRedGrape < Test::Unit::TestCase
5
+ def test_sanity
6
+ #flunk "write tests or I will kneecap you"
7
+ end
8
+ end
@@ -0,0 +1,18 @@
1
+ require 'test/unit'
2
+ require 'red_grape'
3
+
4
+ class TraversalPatternsTest < Test::Unit::TestCase
5
+ def setup
6
+ @graph = RedGrape.load_graph 'data/graph-example-1.xml'
7
+ end
8
+
9
+ # https://github.com/tinkerpop/gremlin/wiki/Backtrack-Pattern
10
+ def test_backtrack_pattern
11
+ assert_equal [29], @graph.V.out('knows').has('age', :gt, 30).back(2).age.take
12
+ assert_equal [29], @graph.V.as('x').outE('knows').inV.has('age', :gt, 30).back('x').age.take
13
+ end
14
+
15
+ # https://github.com/tinkerpop/gremlin/wiki/Except-Retain-Pattern
16
+ def test_except_retain_pattern
17
+ end
18
+ end