red_grape 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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