red_grape 0.0.1 → 0.0.2
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/Manifest.txt +25 -10
- data/README.txt +54 -5
- data/bin/redgrape +77 -0
- data/data/graph-example-2.xml +26380 -0
- data/lib/ext/array_ext.rb +41 -0
- data/lib/ext/object_ext.rb +16 -0
- data/lib/red_grape/edge.rb +4 -17
- data/lib/red_grape/{propertied_object.rb → element.rb} +26 -4
- data/lib/red_grape/graph.rb +75 -73
- data/lib/red_grape/path_group.rb +5 -9
- data/lib/red_grape/pipe/aggregate_pipe.rb +13 -0
- data/lib/red_grape/pipe/as_pipe.rb +3 -10
- data/lib/red_grape/pipe/back_pipe.rb +1 -8
- data/lib/red_grape/pipe/base.rb +51 -6
- data/lib/red_grape/pipe/cap_pipe.rb +16 -0
- data/lib/red_grape/pipe/context.rb +64 -3
- data/lib/red_grape/pipe/except_pipe.rb +15 -0
- data/lib/red_grape/pipe/fill_pipe.rb +19 -0
- data/lib/red_grape/pipe/filter_pipe.rb +1 -7
- data/lib/red_grape/pipe/group_count_pipe.rb +17 -0
- data/lib/red_grape/pipe/has_pipe.rb +24 -0
- data/lib/red_grape/pipe/if_then_else_pipe.rb +1 -7
- data/lib/red_grape/pipe/in_pipe.rb +12 -15
- data/lib/red_grape/pipe/in_v_pipe.rb +12 -0
- data/lib/red_grape/pipe/loop_pipe.rb +16 -25
- data/lib/red_grape/pipe/out_e_pipe.rb +25 -0
- data/lib/red_grape/pipe/out_pipe.rb +6 -15
- data/lib/red_grape/pipe/paths_pipe.rb +21 -9
- data/lib/red_grape/pipe/property_pipe.rb +2 -11
- data/lib/red_grape/pipe/retain_pipe.rb +16 -0
- data/lib/red_grape/pipe/transform_pipe.rb +3 -11
- data/lib/red_grape/property_description.rb +6 -0
- data/lib/red_grape/serializer/graphml_serializer.rb +73 -0
- data/lib/red_grape/vertex.rb +7 -20
- data/lib/red_grape.rb +15 -6
- data/test/{test_propertied_object.rb → test_element.rb} +5 -5
- data/test/test_graph.rb +10 -19
- data/test/test_on_the_nature_of_pipes.rb +13 -13
- data/test/test_pipe.rb +16 -0
- data/test/test_pipe_base.rb +17 -0
- data/test/test_traversal_patterns.rb +69 -0
- metadata +38 -22
- data/bin/red_grape +0 -3
- data/lib/red_grape/vertex_group.rb +0 -72
- data/test/test_red_grape.rb +0 -8
@@ -0,0 +1,41 @@
|
|
1
|
+
class Array
|
2
|
+
def pass_through(pipe, context)
|
3
|
+
loops = context.loops
|
4
|
+
map! do |e|
|
5
|
+
context.loops = loops
|
6
|
+
pipe.pass e._, context # TODO
|
7
|
+
end
|
8
|
+
context.loops = loops
|
9
|
+
normalize_for_graph
|
10
|
+
end
|
11
|
+
|
12
|
+
def normalize_for_graph
|
13
|
+
reject! {|e| e.nil? or (e.respond_to?(:empty?) and e.empty?)}
|
14
|
+
flatten!
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def vertex_array?
|
19
|
+
all?{|e| e.is_a? RedGrape::Vertex}
|
20
|
+
end
|
21
|
+
|
22
|
+
def edge_array?
|
23
|
+
all?{|e| e.is_a? RedGrape::Edge}
|
24
|
+
end
|
25
|
+
|
26
|
+
def graph_item_array?
|
27
|
+
vertex_array? or edge_array?
|
28
|
+
end
|
29
|
+
|
30
|
+
alias method_missing_without_prop_pipe method_missing
|
31
|
+
def method_missing_with_prop_pipe(name, *args, &block)
|
32
|
+
if graph_item_array?
|
33
|
+
map! {|e| e.send name, *args, &block}
|
34
|
+
else
|
35
|
+
method_missing_without_prop_pipe name, *args, &block
|
36
|
+
end
|
37
|
+
end
|
38
|
+
alias method_missing method_missing_with_prop_pipe
|
39
|
+
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Object
|
2
|
+
def pass_through(pipe, context)
|
3
|
+
pipe.pass self, context
|
4
|
+
end
|
5
|
+
|
6
|
+
def _
|
7
|
+
ret = self.dup
|
8
|
+
ret.extend RedGrape::Pipe::Out
|
9
|
+
ret.extend RedGrape::Pipe::OutE
|
10
|
+
ret.extend RedGrape::Pipe::In
|
11
|
+
ret.extend RedGrape::Pipe::SideEffect
|
12
|
+
ret.extend RedGrape::Pipe::As
|
13
|
+
ret.extend RedGrape::Pipe::Fill
|
14
|
+
ret
|
15
|
+
end
|
16
|
+
end
|
data/lib/red_grape/edge.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
require 'red_grape/
|
1
|
+
require 'red_grape/element'
|
2
2
|
|
3
3
|
module RedGrape
|
4
|
-
class Edge <
|
5
|
-
attr_reader :source, :target, :label
|
4
|
+
class Edge < Element
|
5
|
+
attr_reader :id, :source, :target, :label
|
6
6
|
|
7
7
|
def initialize(graph, id, source, target, label, opts={})
|
8
8
|
super graph, opts
|
@@ -14,21 +14,8 @@ module RedGrape
|
|
14
14
|
@label = label
|
15
15
|
end
|
16
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
17
|
def to_s
|
30
|
-
|
31
|
-
"e[#{_id}:v[#{@source._id}]-v[#{@target._id}]]"
|
18
|
+
"e[#{@id}][#{@source.id}-#{@label}->#{@target.id}]"
|
32
19
|
end
|
33
20
|
end
|
34
21
|
end
|
@@ -1,11 +1,21 @@
|
|
1
1
|
require 'red_grape/property_description'
|
2
2
|
|
3
3
|
module RedGrape
|
4
|
-
class
|
4
|
+
class Element
|
5
5
|
def initialize(graph, opts={})
|
6
6
|
@graph = graph
|
7
7
|
@property = {}
|
8
|
-
|
8
|
+
if opts[:property_description]
|
9
|
+
@property_description = opts[:property_description]
|
10
|
+
(opts[:property] || {}).each do |k, v|
|
11
|
+
self[k] = v
|
12
|
+
end
|
13
|
+
else
|
14
|
+
@property_description = {}
|
15
|
+
opts.each do |k, v|
|
16
|
+
self[k] = v
|
17
|
+
end
|
18
|
+
end
|
9
19
|
@property_description.each do |k ,v|
|
10
20
|
if v.is_a? Array
|
11
21
|
v = PropertyDescription.new(*v)
|
@@ -17,6 +27,7 @@ module RedGrape
|
|
17
27
|
end
|
18
28
|
end
|
19
29
|
|
30
|
+
# set property value with type checking
|
20
31
|
def set_property(kid, v)
|
21
32
|
# TODO: should be refactored
|
22
33
|
desc = @property_description[kid]
|
@@ -29,8 +40,8 @@ module RedGrape
|
|
29
40
|
end
|
30
41
|
end
|
31
42
|
|
43
|
+
# set property value without type checking
|
32
44
|
def []=(k, v)
|
33
|
-
# TODO: type check?
|
34
45
|
@property[k.to_s] = v
|
35
46
|
end
|
36
47
|
|
@@ -38,9 +49,20 @@ module RedGrape
|
|
38
49
|
@property[k.to_s]
|
39
50
|
end
|
40
51
|
|
41
|
-
# TODO: $B$$$i$J$$!)(B
|
42
52
|
def pass_through(pipe, context)
|
43
53
|
pipe.pass self, context
|
44
54
|
end
|
55
|
+
|
56
|
+
def copy(depth=nil)
|
57
|
+
self # TODO
|
58
|
+
end
|
59
|
+
|
60
|
+
def ==(obj)
|
61
|
+
self.class == obj.class && self.id == obj.id
|
62
|
+
end
|
63
|
+
|
64
|
+
def method_missing(name, *args, &block)
|
65
|
+
self[name.to_s] or raise NoMethodError.new(name.to_s)
|
66
|
+
end
|
45
67
|
end
|
46
68
|
end
|
data/lib/red_grape/graph.rb
CHANGED
@@ -1,57 +1,89 @@
|
|
1
|
-
require '
|
1
|
+
require 'nokogiri'
|
2
2
|
require 'red_grape/vertex'
|
3
|
-
require 'red_grape/vertex_group'
|
4
3
|
require 'red_grape/edge'
|
5
|
-
require 'red_grape/edge_group'
|
6
4
|
require 'red_grape/property_description'
|
5
|
+
require 'red_grape/serializer/graphml_serializer'
|
7
6
|
|
8
7
|
module RedGrape
|
9
8
|
class Graph
|
10
|
-
|
11
|
-
NAMESPACES = {
|
12
|
-
'xmlns' => 'http://graphml.graphdrawing.org/xmlns',
|
13
|
-
'xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
|
14
|
-
}
|
15
|
-
|
16
|
-
class <<self
|
9
|
+
class << self
|
17
10
|
def load(filename)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
11
|
+
self.new.load filename
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_tinker_graph
|
15
|
+
self.new do |g|
|
16
|
+
v1 = g.add_vertex 1, name:'marko', age:29
|
17
|
+
v2 = g.add_vertex 2, name:'vadas', age:27
|
18
|
+
v3 = g.add_vertex 3, name:'lop', lang:'java'
|
19
|
+
v4 = g.add_vertex 4, name:'josh', age:32
|
20
|
+
v5 = g.add_vertex 5, name:'ripple', lang:'java'
|
21
|
+
v6 = g.add_vertex 6, name:'peter', age:35
|
22
|
+
g.add_edge 7, v1, v2, 'knows', weight:0.5
|
23
|
+
g.add_edge 8, v1, v4, 'knows', weight:1.0
|
24
|
+
g.add_edge 9, v1, v3, 'created', weight:0.4
|
25
|
+
g.add_edge 10, v4, v5, 'created', weight:1.0
|
26
|
+
g.add_edge 11, v4, v3, 'created', weight:0.4
|
27
|
+
g.add_edge 12, v6, v3, 'created', weight:0.2
|
22
28
|
end
|
23
29
|
end
|
24
30
|
end
|
25
31
|
|
26
|
-
|
32
|
+
attr_accessor :serializer
|
33
|
+
|
34
|
+
def initialize(&block)
|
35
|
+
@serializer = Serializer::GraphMLSerializer.new self
|
27
36
|
@vertices = {}
|
28
37
|
@edges = {}
|
29
38
|
@property_descriptions = {}
|
39
|
+
block.call self if block
|
30
40
|
end
|
31
41
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
42
|
+
def items(type, *id)
|
43
|
+
items = case type
|
44
|
+
when :vertex, :vertices
|
45
|
+
@vertices
|
46
|
+
when :edge, :edges
|
47
|
+
@edges
|
48
|
+
else
|
49
|
+
raise ArgumentError.new('type should be :vertex or :edge')
|
50
|
+
end
|
51
|
+
|
52
|
+
if id.size == 2 # TODO: for the time being
|
53
|
+
items.values.select{|e| e[id[0]] == id[1]}
|
54
|
+
elsif 1 < id.size
|
55
|
+
id.map{|i| items[i.to_s]}
|
35
56
|
elsif id.size == 0
|
36
|
-
|
57
|
+
items.values
|
37
58
|
else
|
38
59
|
case id.first
|
39
60
|
when Array
|
40
|
-
|
61
|
+
id.first.map{|i| items[i.to_s]}
|
41
62
|
when :all
|
42
|
-
|
63
|
+
items.values
|
43
64
|
else
|
44
|
-
|
65
|
+
items[id.first.to_s]
|
45
66
|
end
|
46
67
|
end
|
47
68
|
end
|
48
|
-
alias v vertex
|
49
|
-
alias V vertex
|
50
69
|
|
51
|
-
def
|
52
|
-
|
70
|
+
def vertex(*id)
|
71
|
+
items :vertex, *id
|
72
|
+
end
|
73
|
+
|
74
|
+
def v(*id)
|
75
|
+
vertex(*id)._
|
53
76
|
end
|
54
|
-
alias
|
77
|
+
alias V v
|
78
|
+
|
79
|
+
def edge(*id)
|
80
|
+
items :edge, *id
|
81
|
+
end
|
82
|
+
|
83
|
+
def e(*id)
|
84
|
+
edge(*id)._
|
85
|
+
end
|
86
|
+
alias E e
|
55
87
|
|
56
88
|
def add_vertex(id, v=nil)
|
57
89
|
if v
|
@@ -62,78 +94,48 @@ module RedGrape
|
|
62
94
|
if id.is_a? Hash
|
63
95
|
v = id
|
64
96
|
id = v[:id] || v['id']
|
65
|
-
|
97
|
+
elsif id.respond_to?(:id)
|
66
98
|
v = id
|
67
|
-
id = v.
|
99
|
+
id = v.id
|
100
|
+
else
|
101
|
+
v = {}
|
68
102
|
end
|
69
103
|
v = Vertex.new self, id, v
|
70
104
|
end
|
71
|
-
raise ArgumentError.new 'invalid id' unless id == v.
|
105
|
+
raise ArgumentError.new 'invalid id' unless id == v.id
|
72
106
|
|
73
107
|
@vertices[id.to_s] = v
|
74
108
|
end
|
75
109
|
|
76
|
-
def add_edge(id,
|
110
|
+
def add_edge(id, from, to, label, opts={})
|
77
111
|
edge = if id.is_a? Edge
|
78
112
|
id
|
79
113
|
else
|
80
114
|
id = id.to_s
|
81
115
|
from = self.vertex[from.to_s] unless from.is_a? Vertex
|
82
116
|
to = self.vertex[to.to_s] unless to.is_a? Vertex
|
83
|
-
add_vertex from unless self.vertex(from.
|
84
|
-
add_vertex to unless self.vertex(to.
|
85
|
-
Edge.new self, id, from, to, label
|
117
|
+
add_vertex from unless self.vertex(from.id)
|
118
|
+
add_vertex to unless self.vertex(to.id)
|
119
|
+
Edge.new self, id, from, to, label, opts
|
86
120
|
end
|
87
|
-
@edges[edge.
|
121
|
+
@edges[edge.id] = edge
|
88
122
|
end
|
89
123
|
|
90
|
-
def load(
|
91
|
-
|
92
|
-
case type
|
93
|
-
when :xml
|
94
|
-
parse_xml Nokogiri::XML(file)
|
95
|
-
end
|
96
|
-
self
|
124
|
+
def load(filename)
|
125
|
+
@serializer.load filename
|
97
126
|
end
|
98
127
|
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
128
|
+
def save(file)
|
129
|
+
file = File.open file if file.is_a? String
|
130
|
+
@serializer.save file
|
125
131
|
end
|
126
132
|
|
127
133
|
def find(*args)
|
128
134
|
Graph::Vertex.new
|
129
135
|
end
|
130
136
|
|
131
|
-
def nodes(xml, elm)
|
132
|
-
xml.xpath(".//xmlns:#{elm}", NAMESPACES)
|
133
|
-
end
|
134
|
-
|
135
137
|
def to_s
|
136
|
-
{
|
138
|
+
"redgrape[vertices:#{@vertices.size} edges:#{@edges.size}]"
|
137
139
|
end
|
138
140
|
end
|
139
141
|
end
|
data/lib/red_grape/path_group.rb
CHANGED
@@ -1,16 +1,12 @@
|
|
1
|
-
|
1
|
+
require 'forwardable'
|
2
|
+
|
2
3
|
module RedGrape
|
3
4
|
class PathGroup
|
5
|
+
extend Forwardable
|
6
|
+
def_delegators :@paths, :size, :to_s, :[], :first, :last
|
7
|
+
|
4
8
|
def initialize(ary)
|
5
9
|
@paths = ary.dup
|
6
10
|
end
|
7
|
-
|
8
|
-
def size
|
9
|
-
@paths.size
|
10
|
-
end
|
11
|
-
|
12
|
-
def to_s
|
13
|
-
@paths.to_s
|
14
|
-
end
|
15
11
|
end
|
16
12
|
end
|
@@ -16,16 +16,9 @@ module RedGrape
|
|
16
16
|
# TODO: why??
|
17
17
|
context.mark! label
|
18
18
|
obj
|
19
|
-
when Vertex
|
20
|
-
|
21
|
-
|
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
|
19
|
+
when Vertex, Array
|
20
|
+
context.mark! label, obj
|
21
|
+
obj.pass_through self.next, context
|
29
22
|
else
|
30
23
|
raise 'not yet'
|
31
24
|
end
|
data/lib/red_grape/pipe/base.rb
CHANGED
@@ -2,6 +2,16 @@ require 'red_grape/pipe/context'
|
|
2
2
|
|
3
3
|
module RedGrape
|
4
4
|
module Pipe
|
5
|
+
@@auto_take = false
|
6
|
+
|
7
|
+
def self.auto_take
|
8
|
+
@@auto_take
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.set_auto_take(val=true)
|
12
|
+
@@auto_take = val
|
13
|
+
end
|
14
|
+
|
5
15
|
class Base
|
6
16
|
attr_accessor :opts, :prev, :next, :value
|
7
17
|
|
@@ -30,15 +40,38 @@ module RedGrape
|
|
30
40
|
|
31
41
|
def take
|
32
42
|
if first?
|
33
|
-
|
43
|
+
context = Context.new
|
44
|
+
val = @prev.pass_through self, context
|
45
|
+
if context.aggregating?
|
46
|
+
context.resume_from_aggregating
|
47
|
+
elsif context.grouping?
|
48
|
+
context.resume_from_grouping
|
49
|
+
else
|
50
|
+
val
|
51
|
+
end
|
34
52
|
else
|
35
53
|
@prev.take
|
36
54
|
end
|
37
55
|
end
|
38
56
|
|
57
|
+
def pass_next(context, pushed_obj, next_obj=nil, &block)
|
58
|
+
next_obj ||= pushed_obj
|
59
|
+
if self.last?
|
60
|
+
block.call if block
|
61
|
+
next_obj
|
62
|
+
elsif pushed_obj.nil?
|
63
|
+
block.call if block
|
64
|
+
next_obj.pass_through self.next, context
|
65
|
+
else
|
66
|
+
context.push_history pushed_obj do |ctx|
|
67
|
+
block.call if block
|
68
|
+
next_obj.pass_through self.next, ctx
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
39
73
|
def to_s
|
40
|
-
|
41
|
-
take.to_s
|
74
|
+
Pipe.auto_take ? take.to_s : super
|
42
75
|
end
|
43
76
|
|
44
77
|
def to_a
|
@@ -54,7 +87,7 @@ module RedGrape
|
|
54
87
|
def size
|
55
88
|
len = 0
|
56
89
|
pipe = self
|
57
|
-
while pipe.is_a?(
|
90
|
+
while pipe.is_a?(Pipe::Base) and pipe.prev
|
58
91
|
len += 1
|
59
92
|
pipe = pipe.prev
|
60
93
|
end
|
@@ -66,15 +99,27 @@ module RedGrape
|
|
66
99
|
self.class.new nil, @opts, &@block
|
67
100
|
end
|
68
101
|
|
102
|
+
def copy(depth=nil)
|
103
|
+
obj = self.class.new nil, @opts, &@block
|
104
|
+
if depth.nil?
|
105
|
+
obj.prev = self.prev.copy
|
106
|
+
obj.prev.next = obj
|
107
|
+
elsif 0 < depth
|
108
|
+
obj.prev = self.prev.copy(depth - 1)
|
109
|
+
obj.prev.next = obj
|
110
|
+
end
|
111
|
+
obj
|
112
|
+
end
|
113
|
+
|
69
114
|
def method_missing(name, *args, &block)
|
70
115
|
class_name = "#{name.to_s.sub(/^./){$&.upcase}.gsub(/_(.)/){$1.upcase}}Pipe"
|
71
116
|
args.unshift block if block
|
72
117
|
pipe_class =
|
73
|
-
if
|
118
|
+
if Pipe.const_defined? class_name
|
74
119
|
eval "RedGrape::Pipe::#{class_name}"
|
75
120
|
else
|
76
121
|
args.unshift name
|
77
|
-
|
122
|
+
PropertyPipe
|
78
123
|
end
|
79
124
|
@next = pipe_class.new self, *args
|
80
125
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'red_grape/pipe/base'
|
2
|
+
|
3
|
+
module RedGrape
|
4
|
+
module Pipe
|
5
|
+
class CapPipe < Pipe::Base
|
6
|
+
def pass(obj, context)
|
7
|
+
ret = {}
|
8
|
+
context.grouping_items.each do |e|
|
9
|
+
ret[e.first] ||= 0
|
10
|
+
ret[e.first] += 1
|
11
|
+
end
|
12
|
+
ret
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -2,11 +2,14 @@ module RedGrape
|
|
2
2
|
module Pipe
|
3
3
|
class Context
|
4
4
|
attr_accessor :it, :loops
|
5
|
-
attr_reader :history
|
5
|
+
attr_reader :history, :grouping_items
|
6
6
|
|
7
7
|
def initialize
|
8
8
|
@history = []
|
9
9
|
@marks = {}
|
10
|
+
@aggregating_items = {}
|
11
|
+
@aggregated_items = {}
|
12
|
+
@grouping_items = []
|
10
13
|
@loops = 1
|
11
14
|
end
|
12
15
|
|
@@ -17,14 +20,72 @@ module RedGrape
|
|
17
20
|
ret
|
18
21
|
end
|
19
22
|
|
20
|
-
def mark!(label)
|
21
|
-
@marks[label] = @history.last
|
23
|
+
def mark!(label, val=nil)
|
24
|
+
@marks[label] = val || @history.last
|
22
25
|
end
|
23
26
|
|
24
27
|
def mark(label)
|
25
28
|
@marks[label]
|
26
29
|
end
|
27
30
|
|
31
|
+
def aggregated_items(key)
|
32
|
+
@aggregated_items[key.to_s.to_sym] ||= []
|
33
|
+
end
|
34
|
+
|
35
|
+
def aggregating_items(key)
|
36
|
+
@aggregating_items[key.to_s.to_sym] ||= []
|
37
|
+
end
|
38
|
+
|
39
|
+
def aggregate(key, val, next_pipe)
|
40
|
+
self.aggregating_items(key) << [val, next_pipe]
|
41
|
+
val
|
42
|
+
end
|
43
|
+
|
44
|
+
def aggregating?
|
45
|
+
not @aggregating_items.empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
def resume_from_aggregating
|
49
|
+
ret = []
|
50
|
+
aggregating = @aggregating_items
|
51
|
+
@aggregating_items = {}
|
52
|
+
aggregating.each do |key, obj_and_pipes|
|
53
|
+
obj_and_pipes.each do |obj_and_pipe|
|
54
|
+
(@aggregated_items[key] ||= []) << obj_and_pipe.first
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
aggregating.each do |key, obj_and_pipes|
|
59
|
+
obj_and_pipes.each do |obj_and_pipe|
|
60
|
+
obj, pipe = *obj_and_pipe
|
61
|
+
push_history obj do |ctx|
|
62
|
+
ret << obj.pass_through(pipe, ctx)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
ret.normalize_for_graph
|
67
|
+
end
|
68
|
+
|
69
|
+
def group(val, next_pipe)
|
70
|
+
@grouping_items << [val, next_pipe]
|
71
|
+
val
|
72
|
+
end
|
73
|
+
|
74
|
+
def grouping?
|
75
|
+
not @grouping_items.empty?
|
76
|
+
end
|
77
|
+
|
78
|
+
def resume_from_grouping
|
79
|
+
obj, pipe = *@grouping_items.first # TODO: is '.first' ok?
|
80
|
+
if pipe
|
81
|
+
push_history obj do |ctx|
|
82
|
+
obj.pass_through(pipe, ctx)
|
83
|
+
end
|
84
|
+
else
|
85
|
+
obj
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
28
89
|
def eval(args={}, &block)
|
29
90
|
args.each {|k, v| self.send "#{k}=", v}
|
30
91
|
instance_eval(&block)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'red_grape/pipe/base'
|
2
|
+
|
3
|
+
module RedGrape
|
4
|
+
module Pipe
|
5
|
+
module Fill
|
6
|
+
def fill(*opts)
|
7
|
+
FillPipe.new self, *opts
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class FillPipe < Pipe::Base
|
12
|
+
def pass(obj, context)
|
13
|
+
container = self.opts.first
|
14
|
+
container << obj
|
15
|
+
obj
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|