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.
Files changed (45) hide show
  1. data/Manifest.txt +25 -10
  2. data/README.txt +54 -5
  3. data/bin/redgrape +77 -0
  4. data/data/graph-example-2.xml +26380 -0
  5. data/lib/ext/array_ext.rb +41 -0
  6. data/lib/ext/object_ext.rb +16 -0
  7. data/lib/red_grape/edge.rb +4 -17
  8. data/lib/red_grape/{propertied_object.rb → element.rb} +26 -4
  9. data/lib/red_grape/graph.rb +75 -73
  10. data/lib/red_grape/path_group.rb +5 -9
  11. data/lib/red_grape/pipe/aggregate_pipe.rb +13 -0
  12. data/lib/red_grape/pipe/as_pipe.rb +3 -10
  13. data/lib/red_grape/pipe/back_pipe.rb +1 -8
  14. data/lib/red_grape/pipe/base.rb +51 -6
  15. data/lib/red_grape/pipe/cap_pipe.rb +16 -0
  16. data/lib/red_grape/pipe/context.rb +64 -3
  17. data/lib/red_grape/pipe/except_pipe.rb +15 -0
  18. data/lib/red_grape/pipe/fill_pipe.rb +19 -0
  19. data/lib/red_grape/pipe/filter_pipe.rb +1 -7
  20. data/lib/red_grape/pipe/group_count_pipe.rb +17 -0
  21. data/lib/red_grape/pipe/has_pipe.rb +24 -0
  22. data/lib/red_grape/pipe/if_then_else_pipe.rb +1 -7
  23. data/lib/red_grape/pipe/in_pipe.rb +12 -15
  24. data/lib/red_grape/pipe/in_v_pipe.rb +12 -0
  25. data/lib/red_grape/pipe/loop_pipe.rb +16 -25
  26. data/lib/red_grape/pipe/out_e_pipe.rb +25 -0
  27. data/lib/red_grape/pipe/out_pipe.rb +6 -15
  28. data/lib/red_grape/pipe/paths_pipe.rb +21 -9
  29. data/lib/red_grape/pipe/property_pipe.rb +2 -11
  30. data/lib/red_grape/pipe/retain_pipe.rb +16 -0
  31. data/lib/red_grape/pipe/transform_pipe.rb +3 -11
  32. data/lib/red_grape/property_description.rb +6 -0
  33. data/lib/red_grape/serializer/graphml_serializer.rb +73 -0
  34. data/lib/red_grape/vertex.rb +7 -20
  35. data/lib/red_grape.rb +15 -6
  36. data/test/{test_propertied_object.rb → test_element.rb} +5 -5
  37. data/test/test_graph.rb +10 -19
  38. data/test/test_on_the_nature_of_pipes.rb +13 -13
  39. data/test/test_pipe.rb +16 -0
  40. data/test/test_pipe_base.rb +17 -0
  41. data/test/test_traversal_patterns.rb +69 -0
  42. metadata +38 -22
  43. data/bin/red_grape +0 -3
  44. data/lib/red_grape/vertex_group.rb +0 -72
  45. 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
@@ -1,8 +1,8 @@
1
- require 'red_grape/propertied_object'
1
+ require 'red_grape/element'
2
2
 
3
3
  module RedGrape
4
- class Edge < PropertiedObject
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
- #"e[id(#{_id}):v[#{@source._id}]-v[#{@target._id}]:#{@property}]"
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 PropertiedObject
4
+ class Element
5
5
  def initialize(graph, opts={})
6
6
  @graph = graph
7
7
  @property = {}
8
- @property_description = opts[:property_description] || {}
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
@@ -1,57 +1,89 @@
1
- require 'stringio'
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
- # 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
9
+ class << self
17
10
  def load(filename)
18
- if filename =~ /^<\?xml/
19
- self.new.load StringIO.new(filename)
20
- else
21
- self.new.load filename
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
- def initialize
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 vertex(*id)
33
- if 1 < id.size
34
- VertexGroup.new(id.map{|i| @vertices[i.to_s]})
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
- VertexGroup.new @vertices.values
57
+ items.values
37
58
  else
38
59
  case id.first
39
60
  when Array
40
- VertexGroup.new(id.first.map{|i| @vertices[i.to_s]})
61
+ id.first.map{|i| items[i.to_s]}
41
62
  when :all
42
- VertexGroup.new @vertices.values
63
+ items.values
43
64
  else
44
- @vertices[id.first.to_s]
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 edge(id)
52
- @edges[id.to_s]
70
+ def vertex(*id)
71
+ items :vertex, *id
72
+ end
73
+
74
+ def v(*id)
75
+ vertex(*id)._
53
76
  end
54
- alias e edge
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
- else
97
+ elsif id.respond_to?(:id)
66
98
  v = id
67
- id = v._id
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._id
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, label, from, to)
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._id)
84
- add_vertex to unless self.vertex(to._id)
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._id] = edge
121
+ @edges[edge.id] = edge
88
122
  end
89
123
 
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
124
+ def load(filename)
125
+ @serializer.load filename
97
126
  end
98
127
 
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
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
- {:vertices => @vertices, :edges => @edges}.to_s
138
+ "redgrape[vertices:#{@vertices.size} edges:#{@edges.size}]"
137
139
  end
138
140
  end
139
141
  end
@@ -1,16 +1,12 @@
1
- #TODO delegate to an array
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
@@ -0,0 +1,13 @@
1
+ require 'red_grape/pipe/base'
2
+
3
+ module RedGrape
4
+ module Pipe
5
+ class AggregatePipe < Pipe::Base
6
+ def pass(obj, context)
7
+ key = self.opts.first
8
+ context.aggregate key, obj, self.next
9
+ obj
10
+ end
11
+ end
12
+ end
13
+ end
@@ -16,16 +16,9 @@ module RedGrape
16
16
  # TODO: why??
17
17
  context.mark! label
18
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
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
@@ -11,14 +11,7 @@ module RedGrape
11
11
  else
12
12
  context.mark label
13
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
14
+ pass_next context, obj
22
15
  end
23
16
  end
24
17
  end
@@ -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
- @prev.pass_through self, Pipe::Context.new
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
- #TODO: flag
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?(RedGrape::Pipe::Base) and pipe.prev
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 RedGrape::Pipe.const_defined? class_name
118
+ if Pipe.const_defined? class_name
74
119
  eval "RedGrape::Pipe::#{class_name}"
75
120
  else
76
121
  args.unshift name
77
- RedGrape::Pipe::PropertyPipe
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,15 @@
1
+ require 'red_grape/pipe/base'
2
+
3
+ module RedGrape
4
+ module Pipe
5
+ class ExceptPipe < Pipe::Base
6
+ def pass(obj, context)
7
+ if context.aggregated_items(self.opts.first).include? obj
8
+ nil
9
+ else
10
+ obj
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -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