red_grape 0.0.12 → 0.0.13

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/.gitignore CHANGED
@@ -2,4 +2,4 @@
2
2
  dbfile.bin
3
3
  pkg/
4
4
  red_grape-*.gem
5
- public
5
+ public/js/d3
data/bin/juicer CHANGED
@@ -1,14 +1,33 @@
1
1
  #!/usr/bin/env ruby
2
2
  $: << File.expand_path('../../lib', __FILE__)
3
3
 
4
+ require 'optparse'
4
5
  require 'sinatra'
5
6
  require 'red_grape'
7
+ require 'red_grape/tools/trellis'
6
8
 
9
+ # interprete options
10
+ ::Version = RedGrape::VERSION
11
+ COMMAND_LINE = "#{$0} #{ARGV.join(' ')}"
12
+ OPT = {}
13
+ # port:RedGrape::Tools::Trellis::DEFAULT_PORT
14
+ opts = OptionParser.new
15
+ opts.on('-p', '--port <port number>',
16
+ "Set the port number (default: #{OPT[:port]})"){|v| OPT[:port] = v.to_i}
17
+ opts.on_tail('-v', '--version', 'Show version'){puts(opts.ver); exit}
18
+ opts.on_tail('-h', '--help', 'Show this message'){puts(opts.help); exit}
19
+ opts.order! ARGV
20
+
21
+ # start
7
22
  use Rack::Static, :urls => ['/cellar', '/js', '/css'], :root => 'public'
8
23
 
9
- graphs = {
10
- 'tinkergraph' => RedGrape.create_tinker_graph
11
- }
24
+ graphs = nil
25
+ if OPT[:port]
26
+ graphs = RedGrape::Tools::Trellis.open OPT[:port]
27
+ else
28
+ graphs = RedGrape::GraphStore.new
29
+ graphs[:tinkergraph] = RedGrape.create_tinker_graph
30
+ end
12
31
 
13
32
  get '/' do
14
33
  {
@@ -57,7 +76,7 @@ get %r{/graphs/?$} do
57
76
  {
58
77
  version: RedGrape::VERSION,
59
78
  name: 'Juicer: A Graph Server',
60
- graphs: graphs.keys
79
+ graphs: graphs.graph_names
61
80
  }.to_pretty_json
62
81
  end
63
82
 
@@ -108,4 +127,3 @@ get %r{/graphs/([^/]+)/vertices/([^/]+)/(out_e|in_e|both_e|outE|inE|bothE)/?$} d
108
127
  vertex.send(dir)[].map(&:to_h).to_pretty_json
109
128
  end
110
129
  end
111
-
data/bin/redgrape CHANGED
@@ -3,14 +3,14 @@ $: << File.expand_path('../../lib', __FILE__)
3
3
 
4
4
  require 'optparse'
5
5
  require 'red_grape'
6
- require 'red_grape/tools/graph_store'
6
+ require 'red_grape/tools/trellis'
7
7
  require 'red_grape/tools/irg'
8
8
 
9
9
  # interprete options
10
10
  ::Version = RedGrape::VERSION
11
11
  COMMAND_LINE = "#{$0} #{ARGV.join(' ')}"
12
12
  OPT = {
13
- port:RedGrape::Tools::GraphStore::DEFAULT_PORT
13
+ port:RedGrape::Tools::Trellis::DEFAULT_PORT
14
14
  }
15
15
  opts = OptionParser.new
16
16
  opts.on('-p', '--port <port number>',
data/bin/trellis CHANGED
@@ -3,13 +3,13 @@ $: << File.expand_path('../../lib', __FILE__)
3
3
 
4
4
  require 'optparse'
5
5
  require 'red_grape'
6
- require 'red_grape/tools/graph_store'
6
+ require 'red_grape/tools/trellis'
7
7
 
8
8
  # interprete options
9
9
  ::Version = RedGrape::VERSION
10
10
  COMMAND_LINE = "#{$0} #{ARGV.join(' ')}"
11
11
  OPT = {
12
- port:RedGrape::Tools::GraphStore::DEFAULT_PORT,
12
+ port:RedGrape::Tools::Trellis::DEFAULT_PORT,
13
13
  dbfile:'dbfile.bin'
14
14
  }
15
15
  opts = OptionParser.new
@@ -23,7 +23,7 @@ opts.order! ARGV
23
23
 
24
24
  # start
25
25
  Signal.trap :INT, 'EXIT'
26
- RedGrape::Tools::GraphStore.start(OPT[:port], OPT[:dbfile]) do
26
+ RedGrape::Tools::Trellis.start(OPT[:port], OPT[:dbfile]) do
27
27
  puts <<-EOS
28
28
  +=================+
29
29
  | + T + |
@@ -75,6 +75,10 @@ module RedGrape
75
75
  self.class == obj.class && self.id == obj.id
76
76
  end
77
77
 
78
+ def <=>(other)
79
+ self.id <=> other.id
80
+ end
81
+
78
82
  def directed_value(direction, out_value, in_value, error=true)
79
83
  case direction.to_s
80
84
  when 'out'
@@ -17,7 +17,7 @@ module RedGrape
17
17
 
18
18
  # Returns the default graph which has 6 vertices and 6 edges.
19
19
  def create_tinker_graph
20
- self.new do |g|
20
+ self.new :tinkergraph do |g|
21
21
  v1 = g.add_vertex 1, name:'marko', age:29
22
22
  v2 = g.add_vertex 2, name:'vadas', age:27
23
23
  v3 = g.add_vertex 3, name:'lop', lang:'java'
@@ -67,12 +67,13 @@ module RedGrape
67
67
  end
68
68
  end
69
69
 
70
- attr_accessor :serializer
70
+ attr_accessor :name, :serializer
71
71
 
72
72
  # Returns a new instance
73
73
  # _block_ :: a block which is given the instance as the first argument
74
74
  # to initialize it
75
- def initialize(&block)
75
+ def initialize(name=nil, &block)
76
+ @name = name || uid
76
77
  @serializer = Serializer::GraphMLSerializer.new self
77
78
  @vertices = {}
78
79
  @edges = {}
@@ -0,0 +1,29 @@
1
+ require 'red_grape'
2
+
3
+ module RedGrape
4
+ class GraphStore
5
+ def initialize
6
+ @graphs = {}
7
+ end
8
+
9
+ def graph_names
10
+ @graphs.keys.sort
11
+ end
12
+
13
+ def graph(key)
14
+ @graphs[key.to_s.to_sym]
15
+ end
16
+ alias [] graph
17
+
18
+ def put_graph(key, graph)
19
+ graph.name = key.to_s.to_sym
20
+ self << graph
21
+ end
22
+ alias []= put_graph
23
+
24
+ def <<(graph)
25
+ raise ArgumentError.new('The given graph does not have its name.') unless graph.name
26
+ @graphs[graph.name.to_sym] = graph
27
+ end
28
+ end
29
+ end
@@ -62,10 +62,8 @@ module RedGrape
62
62
  context.resume_from_aggregating
63
63
  elsif context.grouping?
64
64
  context.resume_from_grouping
65
- elsif context.gathering?
66
- context.resume_from_gathering
67
- elsif context.counting?
68
- context.resume_from_counting
65
+ elsif context.accumulating?
66
+ context.resume_from_accumulating
69
67
  else
70
68
  val
71
69
  end
@@ -7,11 +7,13 @@ module RedGrape
7
7
  def initialize
8
8
  @history = []
9
9
  @marks = {}
10
+
10
11
  @aggregating_items = {}
11
12
  @aggregated_items = {}
12
13
  @grouping_items = {}
13
- @gathering_items = []
14
- @counting_items = []
14
+
15
+ @accumulated_items = {}
16
+
15
17
  @loops = 1
16
18
  end
17
19
 
@@ -88,44 +90,92 @@ module RedGrape
88
90
  end
89
91
  end
90
92
 
91
- def gather(obj, next_pipe)
92
- @gathering_items << [obj, next_pipe]
93
+ def accumulate(key, obj, &block)
94
+ items = (@accumulated_items[key] ||= {})
95
+ (items[:objects] ||= []) << obj
96
+ items[:resume_block] = block if block
93
97
  end
94
98
 
95
- def gathering?
96
- not @gathering_items.empty?
99
+ def accumulating?(key=nil)
100
+ if key
101
+ not @accumulated_items[key].nil?
102
+ else
103
+ @accumulated_items.any?{|k, _| accumulating? k}
104
+ end
97
105
  end
98
106
 
99
- def clear_gathering
100
- @gathering_items.clear
107
+ def clear_accumulating(key=nil)
108
+ if key
109
+ @accumulated_items[key] = nil if @accumulated_items[key]
110
+ else
111
+ @accumulated_items.clear
112
+ end
101
113
  end
102
114
 
103
- def resume_from_gathering
104
- obj, pipe = *@gathering_items.first
105
- objs = @gathering_items.map &:first
106
- objs.should_pass_through_whole = true
107
- if pipe
108
- push_history objs do |ctx|
109
- pipe.prev = objs
110
- pipe.take ctx
115
+ def resume_from_accumulating(key=nil, should_clear=true)
116
+ ret = nil
117
+ if key
118
+ if items = @accumulated_items[key]
119
+ ret = items[:resume_block].call(items[:objects]) if items[:resume_block]
111
120
  end
121
+ @accumulated_items[key] = nil if should_clear
112
122
  else
113
- objs
123
+ @accumulated_items.each do |k, _|
124
+ ret = resume_from_accumulating k
125
+ end
126
+ end
127
+ ret
128
+ end
129
+
130
+ def gather(obj, next_pipe)
131
+ accumulate :gather, obj do |objs|
132
+ objs = objs.dup
133
+ objs.should_pass_through_whole = true
134
+ if next_pipe
135
+ push_history objs do |ctx|
136
+ next_pipe.prev = objs
137
+ next_pipe.take ctx
138
+ end
139
+ else
140
+ objs
141
+ end
114
142
  end
115
143
  end
116
144
 
145
+ def gathering?
146
+ accumulating? :gather
147
+ end
148
+
149
+ def resume_from_gathering
150
+ resume_from_accumulating :gather
151
+ end
152
+
117
153
  def count(obj, next_pipe)
118
- @counting_items << obj
154
+ accumulate :count, obj do |objs|
155
+ objs.size
156
+ end
119
157
  end
120
158
 
121
159
  def counting?
122
- not @counting_items.empty?
160
+ accumulating? :count
123
161
  end
124
162
 
125
163
  def resume_from_counting
126
- size = @counting_items.size
127
- @counting_items.clear
128
- size
164
+ resume_from_accumulating :count
165
+ end
166
+
167
+ def order(obj, next_pipe)
168
+ accumulate :order, obj do |objs|
169
+ objs.sort
170
+ end
171
+ end
172
+
173
+ def ordering?
174
+ accumulating? :order
175
+ end
176
+
177
+ def resume_from_ordering
178
+ resume_from_accumulating :order
129
179
  end
130
180
 
131
181
  def eval(args={}, &block)
@@ -0,0 +1,12 @@
1
+ require 'red_grape/pipe/base'
2
+
3
+ module RedGrape
4
+ module Pipe
5
+ class OrderPipe < Pipe::Base
6
+ def pass(obj, context)
7
+ context.order obj, self.next
8
+ obj
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,9 +1,18 @@
1
+ require 'forwardable'
1
2
  require 'red_grape/pipe/base'
2
- require 'red_grape/path_group'
3
3
 
4
4
  module RedGrape
5
+ class PathGroup
6
+ extend Forwardable
7
+ def_delegators :@paths, :size, :to_s, :[], :first, :last
8
+
9
+ def initialize(ary)
10
+ @paths = ary.dup
11
+ end
12
+ end
13
+
5
14
  module Pipe
6
- class PathsPipe < Pipe::Base
15
+ class PathPipe < Pipe::Base
7
16
  def pass(obj, context)
8
17
  context.push_history obj do |ctx|
9
18
  history = ctx.history.dup
@@ -26,6 +35,6 @@ module RedGrape
26
35
  end
27
36
  end
28
37
 
29
- PathPipe = PathsPipe
38
+ PathsPipe = PathPipe
30
39
  end
31
40
  end
@@ -5,7 +5,7 @@ module RedGrape
5
5
  class ScatterPipe < Pipe::Base
6
6
  def pass(obj, context)
7
7
  obj.should_pass_through_whole = false
8
- context.clear_gathering
8
+ context.clear_accumulating :gather
9
9
  pass_next context, obj
10
10
  end
11
11
  end
@@ -4,7 +4,14 @@ module RedGrape
4
4
  module Pipe
5
5
  class SelectPipe < Pipe::Base
6
6
  def pass(obj, context)
7
- context.marks
7
+ if converter = self.opts[0]
8
+ context.marks.inject({}) do |h, kv|
9
+ h[kv[0]] = context.eval(:it => kv[1], &converter)
10
+ h
11
+ end
12
+ else
13
+ context.marks
14
+ end
8
15
  end
9
16
  end
10
17
  end
@@ -2,7 +2,7 @@ require 'drb/drb'
2
2
  require 'irb'
3
3
  require 'irb/completion'
4
4
  require 'red_grape'
5
- require 'red_grape/tools/graph_store'
5
+ require 'red_grape/tools/trellis'
6
6
 
7
7
  module RedGrape
8
8
  module Tools
@@ -61,7 +61,7 @@ Subcommands:
61
61
  }
62
62
 
63
63
  RedGrape.set_auto_take
64
- $store = RedGrape::Tools::GraphStore.open port if port
64
+ $store = RedGrape::Tools::Trellis.open port if port
65
65
  block.call if block
66
66
  IRB.start
67
67
  end
@@ -1,9 +1,10 @@
1
1
  require 'drb/drb'
2
2
  require 'red_grape'
3
+ require 'red_grape/graph_store'
3
4
 
4
5
  module RedGrape
5
6
  module Tools
6
- class GraphStore
7
+ class Trellis < GraphStore
7
8
  DEFAULT_PORT = 28282
8
9
 
9
10
  class << self
@@ -32,26 +33,6 @@ module RedGrape
32
33
  end
33
34
  end
34
35
 
35
- def graphs
36
- @graphs.keys.sort
37
- end
38
-
39
- def graph(key)
40
- self[key]
41
- end
42
-
43
- def put_graph(key, graph)
44
- self[key] = graph
45
- end
46
-
47
- def [](key)
48
- @graphs[key.to_sym]
49
- end
50
-
51
- def []=(key, graph)
52
- @graphs[key.to_sym] = graph
53
- end
54
-
55
36
  def start(port=nil, &block)
56
37
  at_exit do
57
38
  File.open @filename, 'w' do |file|
data/lib/red_grape.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module RedGrape
2
- VERSION = '0.0.12'
2
+ VERSION = '0.0.13'
3
3
  end
4
4
 
5
5
  require 'ext/nil_class_ext'
@@ -27,11 +27,12 @@ require 'red_grape/pipe/in_e_pipe'
27
27
  require 'red_grape/pipe/in_v_pipe'
28
28
  require 'red_grape/pipe/loop_pipe'
29
29
  require 'red_grape/pipe/map_pipe'
30
+ require 'red_grape/pipe/order_pipe'
30
31
  require 'red_grape/pipe/out_pipe'
31
32
  require 'red_grape/pipe/out_e_pipe'
32
33
  require 'red_grape/pipe/out_v_pipe'
33
34
  require 'red_grape/pipe/property_pipe'
34
- require 'red_grape/pipe/paths_pipe'
35
+ require 'red_grape/pipe/path_pipe'
35
36
  require 'red_grape/pipe/retain_pipe'
36
37
  require 'red_grape/pipe/scatter_pipe'
37
38
  require 'red_grape/pipe/select_pipe'
@@ -40,6 +41,7 @@ require 'red_grape/pipe/transform_pipe'
40
41
  require 'red_grape/pipe/v_pipe'
41
42
  require 'red_grape/pipe/e_pipe'
42
43
  require 'red_grape/graph'
44
+ require 'red_grape/graph_store'
43
45
 
44
46
  module RedGrape
45
47
  module_function
@@ -0,0 +1,220 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Force-Directed Layout</title>
5
+ <!--script type='text/javascript' src='/js/d3.v3.js'></script-->
6
+ <script type='text/javascript' src='/js/d3.v2.js'></script>
7
+ <style type='text/css'>
8
+
9
+ div.node {
10
+ border-radius: 6px;
11
+ width: 12px;
12
+ height: 12px;
13
+ margin: -6px 0 0 -6px;
14
+ position: absolute;
15
+
16
+ border-radius: 10px;
17
+ width: 20px;
18
+ height: 20px;
19
+ margin: -10px 0 0 -10px;
20
+ color: white;
21
+ text-align:center;
22
+ vertical-align:middle;
23
+ font-size:0.5em;
24
+ }
25
+
26
+ div.link {
27
+ position: absolute;
28
+ border-bottom: solid #999 1px;
29
+ height: 0;
30
+ -webkit-transform-origin: 0 0;
31
+ -moz-transform-origin: 0 0;
32
+ -ms-transform-origin: 0 0;
33
+ -o-transform-origin: 0 0;
34
+ transform-origin: 0 0;
35
+
36
+ font-size:0.1em;
37
+ padding-left:10px;
38
+ white-space:nowrap;
39
+ color:#999;
40
+ }
41
+
42
+ div.graph {
43
+ position:absolute;
44
+ left:200px;
45
+ top:0;
46
+ height:auto;
47
+ }
48
+
49
+ div.inspector {
50
+ position:absolute;
51
+ top:0;
52
+ left:0;
53
+ width:200px;
54
+ height:500px;
55
+ height:100%;
56
+ background-color:#eee;
57
+ box-shadow: -2px 2px 3px #888 inset;
58
+ }
59
+
60
+ div.inspector h2 {
61
+ margin:0;
62
+ padding-left:10px;
63
+ margin-top:10px;
64
+ font-size:1em;
65
+ font-weight:normal;
66
+ background-color:gray;
67
+ color:white;
68
+ }
69
+
70
+ div.inspector dt {
71
+ border-bottom:1px solid gray;
72
+ display:block;
73
+ width:80px;
74
+ float:left;
75
+ padding-left:10px;
76
+ font-weight:bold;
77
+ }
78
+
79
+ div.inspector dd {
80
+ border-bottom:1px solid gray;
81
+ display:block;
82
+ width:160px;
83
+ height:auto;
84
+ }
85
+ </style>
86
+ </head>
87
+ <body>
88
+ <script type='text/javascript'>
89
+ function isIn(array, key) {
90
+ for (var i in array) {
91
+ if (array[i] == key) return true
92
+ }
93
+ return false;
94
+ }
95
+
96
+ var height = window.document.body.scrollHeight;
97
+ var width = window.document.body.scrollWidth - 200;
98
+ var graphId = window.location.hash.substring(1);
99
+ var radius = 6,
100
+ fill = d3.scale.category20();
101
+ var force = d3.layout.force()
102
+ .charge(-120)
103
+ .linkDistance(100)
104
+ .gravity(0)
105
+ .size([width, height]);
106
+ var vis = d3.select('body').append('div')
107
+ .attr('class', 'graph')
108
+ .style('width', width + 'px')
109
+ .style('height', height + 'px');
110
+
111
+ /*
112
+ setTimeout(function() {
113
+ force.linkDistance(30);
114
+ force.stop();
115
+ vis.selectAll('div.link').style('font-size', '0');
116
+ force.start();
117
+ }, 5000);
118
+
119
+ setTimeout(function() {
120
+ force.linkDistance(100);
121
+ force.stop();
122
+ vis.selectAll('div.link').style('font-size', '0.1em');
123
+ force.start();
124
+ }, 10000);
125
+ */
126
+
127
+ var vertices = [];
128
+ var edges = [];
129
+ d3.json('/graphs/' + graphId + '/vertices', function(json) {
130
+ for (var i = 0; i < json.length; i++) {
131
+ vertices.push(json[i].results);
132
+ }
133
+
134
+ function getVertexIndex(id) {
135
+ for (var i = 0; i < vertices.length; i++) {
136
+ if (vertices[i]._id == id) return i;
137
+ }
138
+ return -1;
139
+ }
140
+
141
+ d3.json('/graphs/' + graphId + '/edges', function(json) {
142
+ for (var i = 0; i < json.length; i++) {
143
+ var result = json[i].results;
144
+ edges.push({
145
+ source:getVertexIndex(result._source),
146
+ target:getVertexIndex(result._target),
147
+ data:result
148
+ });
149
+ }
150
+
151
+ var link = vis.selectAll('div.link')
152
+ .data(edges)
153
+ .enter().append('div')
154
+ .attr('class', 'link');
155
+
156
+ link.append('text')
157
+ .attr('text-anchor', 'middle')
158
+ .attr('dy', '.1em')
159
+ .text(function(d) { return '-[' + d.data._label + ']->'; });
160
+
161
+ var node = vis.selectAll('div.node')
162
+ .data(vertices)
163
+ .enter().append('div')
164
+ .attr('class', 'node')
165
+ .style('background', function(d) { return fill(d.group); })
166
+ .style('border-color', function(d) { return d3.rgb(fill(d.group)).darker(); })
167
+ .on('click', function(d) {
168
+ var props = '';
169
+ for (var k in d) {
170
+ if (!isIn(['_type', 'index', 'weight', 'x', 'y', 'px', 'py', 'fixed'], k)) {
171
+ props += '<dt>' + k + '</dt><dd>' + d[k] + '</dd>';
172
+ }
173
+ }
174
+ d3.select('.inspector').html('<h2>Properties</h2><dl>' + props + '</dl>');
175
+ })
176
+ .call(force.drag);
177
+
178
+ node.append('text')
179
+ .attr('text-anchor', 'middle')
180
+ .attr('dy', '.3em')
181
+ .text(function(d) { return d._id; });
182
+
183
+ force
184
+ .nodes(vertices)
185
+ .links(edges)
186
+ .on('tick', tick)
187
+ .start();
188
+
189
+ function tick() {
190
+ node.style('left', function(d) { return (d.x = Math.max(radius, Math.min(width - radius, d.x))) + 'px'; })
191
+ .style('top', function(d) { return (d.y = Math.max(radius, Math.min(height - radius, d.y))) + 'px'; });
192
+
193
+ link.style('left', function(d) { return d.source.x + 'px'; })
194
+ .style('top', function(d) { return d.source.y + 'px'; })
195
+ .style('width', length)
196
+ .style('-webkit-transform', transform)
197
+ .style('-moz-transform', transform)
198
+ .style('-ms-transform', transform)
199
+ .style('-o-transform', transform)
200
+ .style('transform', transform);
201
+ }
202
+
203
+ function transform(d) {
204
+ return 'rotate(' + Math.atan2(d.target.y - d.source.y, d.target.x - d.source.x) * 180 / Math.PI + 'deg)';
205
+ }
206
+
207
+ function length(d) {
208
+ var dx = d.target.x - d.source.x,
209
+ dy = d.target.y - d.source.y;
210
+ return Math.sqrt(dx * dx + dy * dy) + 'px';
211
+ }
212
+ });
213
+ });
214
+ </script>
215
+ <div class='inspector'>
216
+ <h2>Properties</h2>
217
+ &nbsp;Click a node.
218
+ </div>
219
+ </body>
220
+ </html>