red_grape 0.0.12 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
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>