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.
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