red_grape 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.autotest ADDED
@@ -0,0 +1,23 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ # Autotest.add_hook :initialize do |at|
6
+ # at.extra_files << "../some/external/dependency.rb"
7
+ #
8
+ # at.libs << ":../some/external"
9
+ #
10
+ # at.add_exception 'vendor'
11
+ #
12
+ # at.add_mapping(/dependency.rb/) do |f, _|
13
+ # at.files_matching(/test_.*rb$/)
14
+ # end
15
+ #
16
+ # %w(TestA TestB).each do |klass|
17
+ # at.extra_class_map[klass] = "test/test_misc.rb"
18
+ # end
19
+ # end
20
+
21
+ # Autotest.add_hook :run_command do |at|
22
+ # system "rake build"
23
+ # end
data/.gemtest ADDED
File without changes
data/History.txt ADDED
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2012-05-16
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,32 @@
1
+ .autotest
2
+ History.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ bin/red_grape
7
+ data/graph-example-1.xml
8
+ lib/red_grape
9
+ lib/red_grape/edge.rb
10
+ lib/red_grape/graph.rb
11
+ lib/red_grape/path_group.rb
12
+ lib/red_grape/pipe
13
+ lib/red_grape/pipe/as_pipe.rb
14
+ lib/red_grape/pipe/back_pipe.rb
15
+ lib/red_grape/pipe/base.rb
16
+ lib/red_grape/pipe/context.rb
17
+ lib/red_grape/pipe/filter_pipe.rb
18
+ lib/red_grape/pipe/if_then_else_pipe.rb
19
+ lib/red_grape/pipe/in_pipe.rb
20
+ lib/red_grape/pipe/loop_pipe.rb
21
+ lib/red_grape/pipe/out_pipe.rb
22
+ lib/red_grape/pipe/paths_pipe.rb
23
+ lib/red_grape/pipe/property_pipe.rb
24
+ lib/red_grape/pipe/side_effect_pipe.rb
25
+ lib/red_grape/pipe/transform_pipe.rb
26
+ lib/red_grape/propertied_object.rb
27
+ lib/red_grape/property_description.rb
28
+ lib/red_grape/vertex.rb
29
+ lib/red_grape/vertex_group.rb
30
+ lib/red_grape.rb
31
+ test/test_on_the_nature_of_pipes.rb
32
+ test/test_red_grape.rb
data/README.txt ADDED
@@ -0,0 +1,59 @@
1
+ # RedGrape - Extremely Simple GraphDB
2
+
3
+ * https://github.com/technohippy/red-grape
4
+
5
+ ## DESCRIPTION:
6
+
7
+ RedGrape is an in-memory graph database written in ruby.
8
+
9
+ ## FEATURES/PROBLEMS:
10
+
11
+ * load GraphML
12
+
13
+ ## SYNOPSIS:
14
+
15
+ g.v(1).out('knows').filter{it.age < 30}.name.transform{it.size}
16
+
17
+ ## REQUIREMENTS:
18
+
19
+ * Nokogiri (http://nokogiri.org/)
20
+
21
+ ## DEVELOPERS:
22
+
23
+ After checking out the source, run:
24
+
25
+ $ rake newb
26
+
27
+ This task will install any missing dependencies, run the tests/specs,
28
+ and generate the RDoc.
29
+
30
+ ## REFERENCES:
31
+
32
+ * [Tinkerpop](http://tinkerpop.com/)
33
+ ** [Gremlin](https://github.com/tinkerpop/gremlin/wiki)
34
+ ** [Pipes](https://github.com/tinkerpop/pipes/wiki/)
35
+
36
+ ## LICENSE:
37
+
38
+ (The MIT License)
39
+
40
+ Copyright (c) 2012 ANDO Yasushi
41
+
42
+ Permission is hereby granted, free of charge, to any person obtaining
43
+ a copy of this software and associated documentation files (the
44
+ 'Software'), to deal in the Software without restriction, including
45
+ without limitation the rights to use, copy, modify, merge, publish,
46
+ distribute, sublicense, and/or sell copies of the Software, and to
47
+ permit persons to whom the Software is furnished to do so, subject to
48
+ the following conditions:
49
+
50
+ The above copyright notice and this permission notice shall be
51
+ included in all copies or substantial portions of the Software.
52
+
53
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
54
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
55
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
56
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
57
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
58
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
59
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ # Hoe.plugin :compiler
7
+ # Hoe.plugin :gem_prelude_sucks
8
+ # Hoe.plugin :inline
9
+ # Hoe.plugin :racc
10
+ # Hoe.plugin :rcov
11
+ # Hoe.plugin :rubyforge
12
+
13
+ Hoe.spec 'red_grape' do
14
+ developer('ANDO Yasushi', 'andyjpn@gmail.com')
15
+
16
+ # self.rubyforge_name = 'red_grapex' # if different than 'red_grape'
17
+ end
18
+
19
+ # vim: syntax=ruby
data/bin/red_grape ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ abort "you need to write me"
@@ -0,0 +1,54 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <graphml xmlns="http://graphml.graphdrawing.org/xmlns"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
5
+ http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
6
+ <key id="weight" for="edge" attr.name="weight" attr.type="float"/>
7
+ <key id="name" for="node" attr.name="name" attr.type="string"/>
8
+ <key id="age" for="node" attr.name="age" attr.type="int"/>
9
+ <key id="lang" for="node" attr.name="lang" attr.type="string"/>
10
+ <graph id="G" edgedefault="directed">
11
+ <node id="1">
12
+ <data key="name">marko</data>
13
+ <data key="age">29</data>
14
+ </node>
15
+ <node id="2">
16
+ <data key="name">vadas</data>
17
+ <data key="age">27</data>
18
+ </node>
19
+ <node id="3">
20
+ <data key="name">lop</data>
21
+ <data key="lang">java</data>
22
+ </node>
23
+ <node id="4">
24
+ <data key="name">josh</data>
25
+ <data key="age">32</data>
26
+ </node>
27
+ <node id="5">
28
+ <data key="name">ripple</data>
29
+ <data key="lang">java</data>
30
+ </node>
31
+ <node id="6">
32
+ <data key="name">peter</data>
33
+ <data key="age">35</data>
34
+ </node>
35
+ <edge id="7" source="1" target="2" label="knows">
36
+ <data key="weight">0.5</data>
37
+ </edge>
38
+ <edge id="8" source="1" target="4" label="knows">
39
+ <data key="weight">1.0</data>
40
+ </edge>
41
+ <edge id="9" source="1" target="3" label="created">
42
+ <data key="weight">0.4</data>
43
+ </edge>
44
+ <edge id="10" source="4" target="5" label="created">
45
+ <data key="weight">1.0</data>
46
+ </edge>
47
+ <edge id="11" source="4" target="3" label="created">
48
+ <data key="weight">0.4</data>
49
+ </edge>
50
+ <edge id="12" source="6" target="3" label="created">
51
+ <data key="weight">0.2</data>
52
+ </edge>
53
+ </graph>
54
+ </graphml>
@@ -0,0 +1,34 @@
1
+ require 'red_grape/propertied_object'
2
+
3
+ module RedGrape
4
+ class Edge < PropertiedObject
5
+ attr_reader :source, :target, :label
6
+
7
+ def initialize(graph, id, source, target, label, opts={})
8
+ super graph, opts
9
+ @id = id
10
+ @source = source.is_a?(Vertex) ? source : graph.vertex(source)
11
+ @target = target.is_a?(Vertex) ? target : graph.vertex(target)
12
+ @source.add_out_edge self
13
+ @target.add_in_edge self
14
+ @label = label
15
+ end
16
+
17
+ def _id
18
+ @id
19
+ end
20
+
21
+ def _source
22
+ @source
23
+ end
24
+
25
+ def _target
26
+ @target
27
+ end
28
+
29
+ def to_s
30
+ #"e[id(#{_id}):v[#{@source._id}]-v[#{@target._id}]:#{@property}]"
31
+ "e[#{_id}:v[#{@source._id}]-v[#{@target._id}]]"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,139 @@
1
+ require 'stringio'
2
+ require 'red_grape/vertex'
3
+ require 'red_grape/vertex_group'
4
+ require 'red_grape/edge'
5
+ require 'red_grape/edge_group'
6
+ require 'red_grape/property_description'
7
+
8
+ module RedGrape
9
+ class Graph
10
+ # TODO: https://github.com/tinkerpop/blueprints/blob/bed2e64010882be66bf3b46d3c3e4b4ef4f6f2d9/blueprints-core/src/main/java/com/tinkerpop/blueprints/pgm/impls/tg/TinkerGraphFactory.java
11
+ NAMESPACES = {
12
+ 'xmlns' => 'http://graphml.graphdrawing.org/xmlns',
13
+ 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
14
+ }
15
+
16
+ class <<self
17
+ def load(filename)
18
+ if filename =~ /^<\?xml/
19
+ self.new.load StringIO.new(filename)
20
+ else
21
+ self.new.load filename
22
+ end
23
+ end
24
+ end
25
+
26
+ def initialize
27
+ @vertices = {}
28
+ @edges = {}
29
+ @property_descriptions = {}
30
+ end
31
+
32
+ def vertex(*id)
33
+ if 1 < id.size
34
+ VertexGroup.new(id.map{|i| @vertices[i.to_s]})
35
+ elsif id.size == 0
36
+ VertexGroup.new @vertices.values
37
+ else
38
+ case id.first
39
+ when Array
40
+ VertexGroup.new(id.first.map{|i| @vertices[i.to_s]})
41
+ when :all
42
+ VertexGroup.new @vertices.values
43
+ else
44
+ @vertices[id.first.to_s]
45
+ end
46
+ end
47
+ end
48
+ alias v vertex
49
+ alias V vertex
50
+
51
+ def edge(id)
52
+ @edges[id.to_s]
53
+ end
54
+ alias e edge
55
+
56
+ def add_vertex(id, v=nil)
57
+ if v
58
+ if v.is_a? Hash
59
+ v = Vertex.new self, id, v
60
+ end
61
+ else
62
+ if id.is_a? Hash
63
+ v = id
64
+ id = v[:id] || v['id']
65
+ else
66
+ v = id
67
+ id = v._id
68
+ end
69
+ v = Vertex.new self, id, v
70
+ end
71
+ raise ArgumentError.new 'invalid id' unless id == v._id
72
+
73
+ @vertices[id.to_s] = v
74
+ end
75
+
76
+ def add_edge(id, label, from, to)
77
+ edge = if id.is_a? Edge
78
+ id
79
+ else
80
+ id = id.to_s
81
+ from = self.vertex[from.to_s] unless from.is_a? Vertex
82
+ to = self.vertex[to.to_s] unless to.is_a? Vertex
83
+ add_vertex from unless self.vertex(from._id)
84
+ add_vertex to unless self.vertex(to._id)
85
+ Edge.new self, id, from, to, label
86
+ end
87
+ @edges[edge._id] = edge
88
+ end
89
+
90
+ def load(file, type=:xml)
91
+ file = File.open file if file.is_a? String
92
+ case type
93
+ when :xml
94
+ parse_xml Nokogiri::XML(file)
95
+ end
96
+ self
97
+ end
98
+
99
+ # assumption: any sub-graph does not exist.
100
+ def parse_xml(xml)
101
+ nodes(xml, 'key').each do |key_elm|
102
+ defaults = nodes key_elm, 'default'
103
+ default = defaults.size == 0 ? nil : defaults.first.children.to_s
104
+ prop = PropertyDescription.new key_elm['attr.name'], key_elm['attr.type'], default
105
+ (@property_descriptions[key_elm['for']] ||= {})[key_elm['id']] = prop
106
+ end
107
+ nodes(xml, 'node').each do |node_elm|
108
+ vertex = Vertex.new self, node_elm['id'],
109
+ :property_description => @property_descriptions['node']
110
+ data = nodes node_elm, 'data'
111
+ data.each do |data_elm|
112
+ vertex.set_property data_elm['key'], data_elm.children.first.to_s
113
+ end
114
+ @vertices[vertex._id] = vertex
115
+ end
116
+ nodes(xml, 'edge').each do |edge_elm|
117
+ edge = Edge.new self, edge_elm['id'], edge_elm['source'], edge_elm['target'],
118
+ edge_elm['label'], :property_description => @property_descriptions['edge']
119
+ data = nodes edge_elm, 'data'
120
+ data.each do |data_elm|
121
+ edge.set_property data_elm['key'], data_elm.children.first.to_s
122
+ end
123
+ @edges[edge._id] = edge
124
+ end
125
+ end
126
+
127
+ def find(*args)
128
+ Graph::Vertex.new
129
+ end
130
+
131
+ def nodes(xml, elm)
132
+ xml.xpath(".//xmlns:#{elm}", NAMESPACES)
133
+ end
134
+
135
+ def to_s
136
+ {:vertices => @vertices, :edges => @edges}.to_s
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,16 @@
1
+ #TODO delegate to an array
2
+ module RedGrape
3
+ class PathGroup
4
+ def initialize(ary)
5
+ @paths = ary.dup
6
+ end
7
+
8
+ def size
9
+ @paths.size
10
+ end
11
+
12
+ def to_s
13
+ @paths.to_s
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ require 'red_grape/pipe/base'
2
+
3
+ module RedGrape
4
+ module Pipe
5
+ module As
6
+ def as(*opts)
7
+ AsPipe.new self, *opts
8
+ end
9
+ end
10
+
11
+ class AsPipe < Pipe::Base
12
+ def pass(obj, context)
13
+ label = self.opts.first.to_s
14
+ case self.prev
15
+ when Pipe::Base
16
+ # TODO: why??
17
+ context.mark! label
18
+ obj
19
+ when Vertex
20
+ if self.last?
21
+ context.mark! label
22
+ obj
23
+ else
24
+ context.push_history obj do |ctx|
25
+ context.mark! label
26
+ obj.pass_through self.next, ctx
27
+ end
28
+ end
29
+ else
30
+ raise 'not yet'
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,25 @@
1
+ require 'red_grape/pipe/base'
2
+
3
+ module RedGrape
4
+ module Pipe
5
+ class BackPipe < Pipe::Base
6
+ def pass(obj, context)
7
+ label = self.opts.first
8
+ obj = case label
9
+ when Integer
10
+ context.history[-label]
11
+ else
12
+ context.mark label
13
+ end
14
+
15
+ if self.last?
16
+ obj
17
+ else
18
+ context.push_history obj do |ctx|
19
+ obj.pass_through self.next, ctx
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,83 @@
1
+ require 'red_grape/pipe/context'
2
+
3
+ module RedGrape
4
+ module Pipe
5
+ class Base
6
+ attr_accessor :opts, :prev, :next, :value
7
+
8
+ def initialize(prev, *opts, &block)
9
+ @prev = prev
10
+ @next = nil
11
+ @opts = opts
12
+ @block = block
13
+ end
14
+
15
+ def pipe_name
16
+ self.class.name.split('::').last
17
+ end
18
+
19
+ def first?
20
+ not @prev.kind_of? RedGrape::Pipe::Base
21
+ end
22
+
23
+ def last?
24
+ @next.nil?
25
+ end
26
+
27
+ def done?
28
+ not @value.nil?
29
+ end
30
+
31
+ def take
32
+ if first?
33
+ @prev.pass_through self, Pipe::Context.new
34
+ else
35
+ @prev.take
36
+ end
37
+ end
38
+
39
+ def to_s
40
+ #TODO: flag
41
+ take.to_s
42
+ end
43
+
44
+ def to_a
45
+ pipe = self
46
+ ret = [pipe.pipe_name]
47
+ until pipe.first?
48
+ pipe = pipe.prev
49
+ ret.unshift pipe.pipe_name
50
+ end
51
+ ret
52
+ end
53
+
54
+ def size
55
+ len = 0
56
+ pipe = self
57
+ while pipe.is_a?(RedGrape::Pipe::Base) and pipe.prev
58
+ len += 1
59
+ pipe = pipe.prev
60
+ end
61
+ len
62
+ end
63
+
64
+ # note: both prev and next are not copied.
65
+ def dup
66
+ self.class.new nil, @opts, &@block
67
+ end
68
+
69
+ def method_missing(name, *args, &block)
70
+ class_name = "#{name.to_s.sub(/^./){$&.upcase}.gsub(/_(.)/){$1.upcase}}Pipe"
71
+ args.unshift block if block
72
+ pipe_class =
73
+ if RedGrape::Pipe.const_defined? class_name
74
+ eval "RedGrape::Pipe::#{class_name}"
75
+ else
76
+ args.unshift name
77
+ RedGrape::Pipe::PropertyPipe
78
+ end
79
+ @next = pipe_class.new self, *args
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,34 @@
1
+ module RedGrape
2
+ module Pipe
3
+ class Context
4
+ attr_accessor :it, :loops
5
+ attr_reader :history
6
+
7
+ def initialize
8
+ @history = []
9
+ @marks = {}
10
+ @loops = 1
11
+ end
12
+
13
+ def push_history(obj, &block)
14
+ @history.push obj
15
+ ret = block.call self
16
+ @history.pop
17
+ ret
18
+ end
19
+
20
+ def mark!(label)
21
+ @marks[label] = @history.last
22
+ end
23
+
24
+ def mark(label)
25
+ @marks[label]
26
+ end
27
+
28
+ def eval(args={}, &block)
29
+ args.each {|k, v| self.send "#{k}=", v}
30
+ instance_eval(&block)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,22 @@
1
+ require 'red_grape/pipe/base'
2
+
3
+ module RedGrape
4
+ module Pipe
5
+ class FilterPipe < Pipe::Base
6
+ def pass(obj, context)
7
+ filter = self.opts.first
8
+ if context.eval :it => obj, &filter
9
+ if self.last?
10
+ obj
11
+ else
12
+ context.push_history obj do |ctx|
13
+ obj.pass_through self.next, ctx
14
+ end
15
+ end
16
+ else
17
+ nil
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ require 'red_grape/pipe/base'
2
+
3
+ module RedGrape
4
+ module Pipe
5
+ class IfThenElsePipe < Pipe::Base
6
+ def pass(obj, context)
7
+ condition = self.opts[0]
8
+ then_block = self.opts[1]
9
+ else_block = self.opts[2]
10
+ ret =
11
+ if context.eval({:it => obj}, &condition)
12
+ context.eval({:it => obj}, &then_block)
13
+ else
14
+ context.eval({:it => obj}, &else_block)
15
+ end
16
+ ret = ret.take if ret.is_a? Pipe::Base
17
+ if self.last?
18
+ ret
19
+ else
20
+ context.push_history ret do |ctx|
21
+ ret.pass_through self.next, ctx
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ require 'red_grape/pipe/base'
2
+
3
+ module RedGrape
4
+ module Pipe
5
+ class InPipe < Pipe::Base
6
+ def pipe_name
7
+ @opts.empty? ? super : "#{super}(#{@opts.first})"
8
+ end
9
+
10
+ def pass(obj, context)
11
+ case obj
12
+ when RedGrape::Vertex
13
+ group =
14
+ if self.opts.empty?
15
+ VertexGroup.new obj._in_edges.map(&:source)
16
+ else
17
+ label = self.opts.first
18
+ VertexGroup.new obj._in_edges.find_all{|e| e.label == label}.map(&:source)
19
+ end
20
+ if self.last?
21
+ group
22
+ else
23
+ context.push_history obj do |ctx|
24
+ group.pass_through self.next, ctx
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end