petri_net 0.8.0 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6280711333c42af2d42f9f72dc8d7e9addf491d0
4
- data.tar.gz: 0f5c88804ad2453ac2ac5f58ed48b402e6a46332
3
+ metadata.gz: 785d6ed616e72a8f4229c584b3625bd182dcc354
4
+ data.tar.gz: 7ae508f907786e328f3148ab86c5653bdc6e03c2
5
5
  SHA512:
6
- metadata.gz: d11cb20273590228cc3324d6e5483eb6f6d79fb2cf8e3217200fb86e4464d511126eb2c9a2843c11ec8f69a54ff061d63f3c4037fd9b7c637dabe749eda05d4c
7
- data.tar.gz: 4bf91f1daf2975b146139be896386cc1704d393f33d3d4936afc4e7e607b97ad214d09c674dd6c189c3b51b5a25a323308a78a8201bcc7e3ba5ad249c530f0b1
6
+ metadata.gz: 71ec4878553ee6f33000bf9901d2ba1b9707ad3ddb0e793812ec19bbc027ce2ea1a77ee2669f1f625210f82f13744ecd304614379c2348d76fa33288d4600ff0
7
+ data.tar.gz: 2eba62b83d60d28a62d14a67d07bb1981a2026a3f693b959f264bd7bd0c9e7f24fe6f556a7415c4db82edb9fd675aff38b8e5d28dee7a8e5634445d8691b4afa
@@ -5,7 +5,8 @@ rvm:
5
5
  - 2.0.0
6
6
 
7
7
  before_install:
8
- gem install net-sftp
8
+ - gem install net-sftp
9
+ - sudo apt-get install graphviz
9
10
 
10
11
  script:
11
- rake test
12
+ - rake test
data/Gemfile CHANGED
@@ -2,3 +2,6 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in petri.gemspec
4
4
  gemspec
5
+
6
+ #gem 'rgl', "~>0.4.1"
7
+ gem 'rgl', :git => 'https://github.com/monora/rgl.git'
@@ -31,6 +31,7 @@ class PetriNet::Graph::Edge < PetriNet::Base
31
31
 
32
32
  # Validates the data holded by this edge, this will be used while adding the edge to the graph
33
33
  def validate
34
+ return false unless ( @graph.nodes.has_key? @source.name and @graph.nodes.has_key? @destination.name )
34
35
  true
35
36
  end
36
37
 
@@ -43,7 +44,7 @@ class PetriNet::Graph::Edge < PetriNet::Base
43
44
  (@source == object.yource && @destination == oject.destination)
44
45
  end
45
46
  def to_s
46
- "#{@id}: #{@name} #{@source.id} -> #{@destination} )"
47
+ "#{@id}: #{@name} #{@source} -> #{@destination} )"
47
48
  end
48
49
 
49
50
  private
@@ -1,5 +1,7 @@
1
1
  require 'graphviz'
2
-
2
+ require 'graphviz/theory'
3
+ require 'rgl/adjacency'
4
+ require 'rgl/dijkstra'
3
5
  class PetriNet::InfiniteReachabilityGraphError < RuntimeError
4
6
  end
5
7
 
@@ -7,6 +9,10 @@ class PetriNet::Graph < PetriNet::Base
7
9
 
8
10
  # The PetriNet this graph belongs to
9
11
  attr_reader :net
12
+ # all nodes from this graph
13
+ attr_reader :nodes
14
+ # all edges of this graph
15
+ attr_reader :edges
10
16
 
11
17
  def initialize(net, options = Hash.new)
12
18
  @net = net
@@ -29,6 +35,10 @@ class PetriNet::Graph < PetriNet::Base
29
35
  node.graph = self
30
36
  return node.id
31
37
  end
38
+ if @objects.include? node
39
+ res = (@objects.index node) * -1
40
+ return res
41
+ end
32
42
  return false
33
43
  end
34
44
  alias_method :add_node!, :add_node
@@ -38,12 +48,24 @@ class PetriNet::Graph < PetriNet::Base
38
48
  @objects[edge.id] = edge
39
49
  @edges[edge.name] = edge.id
40
50
  edge.graph = self
51
+ edge.source.outputs << edge.id
52
+ edge.destination.inputs << edge.id
41
53
  return edge.id
42
54
  end
43
55
  return false
44
56
  end
45
57
 
46
- # Add an object to the Petri Net.
58
+ def get_edge(source, dest)
59
+ res = nil
60
+ @edges.each_value do |edge|
61
+ if @objects[edge].source == source && @objects[edge].destination == dest
62
+ res = @objects[edge]
63
+ end
64
+ end
65
+ res
66
+ end
67
+
68
+ # Add an object to the Graph.
47
69
  def <<(object)
48
70
  case object.class.to_s
49
71
  when "Array"
@@ -59,7 +81,16 @@ class PetriNet::Graph < PetriNet::Base
59
81
  end
60
82
  alias_method :add_object, :<<
61
83
 
62
- def get_node(id)
84
+ def get_node(node)
85
+ if node.class.to_s == "Fixnum"
86
+ return @objects[node]
87
+ end
88
+ if node.class.to_s == "Array"
89
+ return @objects.select{|o| o.class.to_s == "PetriNet::ReachabilityGraph::Node" && o.markings == node}.first
90
+ end
91
+ end
92
+
93
+ def get_object(id)
63
94
  @objects[id]
64
95
  end
65
96
 
@@ -71,6 +102,13 @@ class PetriNet::Graph < PetriNet::Base
71
102
  res
72
103
  end
73
104
 
105
+ def infinite?
106
+ @nodes.each_value do |node|
107
+ return true if @objects[node].infinite?
108
+ end
109
+ false
110
+ end
111
+
74
112
  def to_gv(output = 'png', filename = '')
75
113
  g = generate_gv
76
114
  if filename.empty?
@@ -95,7 +133,18 @@ class PetriNet::Graph < PetriNet::Base
95
133
  e.label = @objects[edge].transition
96
134
  end
97
135
  end
98
- g
136
+ @gv = g
137
+ end
138
+
139
+ def generate_rgl
140
+ g = RGL::DirectedAdjacencyGraph.new
141
+ @nodes.each_value do |node|
142
+ g.add_vertex @objects[node].markings.to_s
143
+ end
144
+ @edges.each_value do |edge|
145
+ g.add_edge @objects[edge].source.markings.to_s, @objects[edge].destination.markings.to_s
146
+ end
147
+ @rgl = g
99
148
  end
100
149
 
101
150
  def to_s
@@ -118,4 +167,103 @@ class PetriNet::Graph < PetriNet::Base
118
167
  return str
119
168
  end
120
169
 
170
+ def get_rgl
171
+ if @rgl.nil?
172
+ generate_rgl
173
+ end
174
+ @rgl
175
+ end
176
+
177
+ def cycles
178
+ get_rgl.cycles
179
+ end
180
+
181
+ def shortest_path(start, destination)
182
+ g = get_rgl
183
+ weights = lambda { |edge| 1 }
184
+ g.dijkstra_shortest_path(weights, start.to_s, destination.to_s)
185
+ end
186
+
187
+
188
+ def path_probability(path)
189
+ sanitize_probabilities
190
+ prob = 1
191
+ counter = 0
192
+ path.each do |node|
193
+ edge = get_edge(path[counter+1], node)
194
+ prob = prob * edge.probability unless edge.nil? # last node has no pre-edge
195
+ counter = counter += 1
196
+ end
197
+ prob
198
+ end
199
+
200
+ def node_probability(start, node)
201
+ paths = get_paths_without_loops(start, node)
202
+ prob = 0
203
+ paths.each do |path|
204
+ prob = prob + (path_probability path)
205
+ end
206
+ prob
207
+ end
208
+
209
+ def best_path(start, node)
210
+ paths = get_paths_without_loops(start, node)
211
+ prob = 0
212
+ res_path = nil
213
+ paths.each do |path|
214
+ if (path_probability path) >= prob
215
+ prob = (path_probability path)
216
+ res_path = path
217
+ end
218
+ end
219
+ [res_path,prob]
220
+ end
221
+
222
+ def worst_path(start, node)
223
+ paths = get_paths_without_loops(start, node)
224
+ prob = 1
225
+ res_path = nil
226
+ paths.each do |path|
227
+ if (path_probability path) <= prob
228
+ prob = (path_probability path)
229
+ res_path = path
230
+ end
231
+ end
232
+ [res_path,prob]
233
+ end
234
+
235
+ def get_paths_without_loops(start, goal)
236
+ get_paths_without_loops_helper(get_node(start), get_node(goal))
237
+ end
238
+
239
+ def sanitize_probabilities
240
+ @nodes.each_value do |node|
241
+ prob = 1.0
242
+ @objects[node].outputs.each do |edge|
243
+ prob = prob + @objects[edge].probability unless @objects[edge].probability.nil?
244
+ end
245
+ @objects[node].outputs.each do |edge|
246
+ @objects[edge].probability = prob/@objects[node].outputs.size if @objects[edge].probability.nil?
247
+ end
248
+ end
249
+ end
250
+
251
+
252
+ private
253
+ def get_paths_without_loops_helper(start, goal, reverse_paths = Array.new, reverse_path = Array.new)
254
+ if goal == start
255
+ reverse_path << goal
256
+ reverse_paths << reverse_path
257
+ return nil
258
+ end
259
+ if reverse_path.include? goal
260
+ return nil
261
+ end
262
+ path = Array.new
263
+ goal.inputs.each do |input|
264
+ get_paths_without_loops_helper(start, @objects[input].source, reverse_paths, reverse_path.clone << goal)
265
+ end
266
+ reverse_paths
267
+ end
268
+
121
269
  end
@@ -42,6 +42,10 @@ class PetriNet::Graph::Node < PetriNet::Base
42
42
  yield self unless block.nil?
43
43
  end
44
44
 
45
+ def infinite?
46
+ @omega_marked
47
+ end
48
+
45
49
  # Add an omega-marking to a specified place
46
50
  def add_omega object
47
51
  ret = Array.new
@@ -1,6 +1,10 @@
1
1
  require 'yaml'
2
+ require 'graphviz'
3
+ require 'matrix'
4
+ require 'bigdecimal/ludcmp'
2
5
 
3
6
  class PetriNet::Net < PetriNet::Base
7
+ include LUSolve
4
8
  # Human readable name
5
9
  attr_accessor :name
6
10
  # Storage filename
@@ -240,6 +244,9 @@ Arcs
240
244
  end
241
245
 
242
246
  def reachability_graph
247
+ if !@up_to_date
248
+ update
249
+ end
243
250
  generate_reachability_graph unless (@graph && @up_to_date)
244
251
  @graph
245
252
  end
@@ -300,6 +307,18 @@ Arcs
300
307
  @places.map{|key,pid| @objects[pid].markings.size}
301
308
  end
302
309
 
310
+ def get_marking(places)
311
+ unless places.class.to_s == "Array"
312
+ places = [places]
313
+ end
314
+ if places.first.class.to_s == "Fixnum"
315
+ places.map!{|p| get_place p}
316
+ end
317
+ res = Array.new
318
+ get_place_list.map{|place| if places.include? place.name then res << 1 else res << 0 end}
319
+ res
320
+ end
321
+
303
322
  def set_markings(markings)
304
323
  i = 0
305
324
  @places.each_value do |pid|
@@ -313,6 +332,11 @@ Arcs
313
332
  @places.map{|key,pid| @objects[pid]}
314
333
  end
315
334
 
335
+ def get_place_from_marking(marking)
336
+ return marking if marking.count(1) != 1
337
+ get_place_list[marking.index(1)].name
338
+ end
339
+
316
340
 
317
341
  def objects_size
318
342
  @objects.count{|o| !o.nil?}
@@ -346,8 +370,46 @@ Arcs
346
370
  get_transition(transition).fire
347
371
  end
348
372
 
373
+ def delta
374
+ if @delta.nil?
375
+ generate_delta
376
+ end
377
+ @delta
378
+ end
379
+
380
+ def t_invariants
381
+ delta = self.delta
382
+ zero_vector = Array.new
383
+ delta.row_count.times { zero_vector << 0 }
384
+ zero = BigDecimal("0.0")
385
+ one = BigDecimal("1.0")
386
+
387
+ ps = ludecomp(delta.t.to_a.flatten.map{|i|BigDecimal(i,16)},delta.row_count, zero, one)
388
+ x = lusolve(delta.t.to_a.flatten.map{|i|BigDecimal(i,16)},zero_vector.map{|i|BigDecimal(i,16)},ps, zero)
389
+
390
+ x
391
+ end
392
+
393
+ def s_invariant
394
+ raise "Not jet implemented"
395
+ end
396
+
349
397
  private
350
398
 
399
+ def generate_delta
400
+ d = Array.new(@places.size){Array.new(@transitions.size)}
401
+ i = 0
402
+ @places.each do |p_key,p_value|
403
+ j = 0
404
+ @transitions.each do |t_key,t_value|
405
+ d[i][j] = w0(t_value, p_value) - w0(p_value,t_value)
406
+ j += 1
407
+ end
408
+ i += 1
409
+ end
410
+ @delta = Matrix[d]
411
+ end
412
+
351
413
  def changed_structure
352
414
  @w_up_to_date = false
353
415
  @up_to_date = false
@@ -356,7 +418,6 @@ Arcs
356
418
  def changed_state
357
419
  @up_to_date = false
358
420
  end
359
-
360
421
  def reachability_helper(markings, source)
361
422
  @transitions.each_value do |tid|
362
423
  raise PetriNet::ReachabilityGraph::InfinityGraphError if @objects[tid].inputs.empty? && !@objects[tid].outputs.empty?
@@ -373,8 +434,11 @@ Arcs
373
434
  @graph.add_edge PetriNet::ReachabilityGraph::Edge.new(@graph, source: current_node, destination: infinity_node)
374
435
  next
375
436
  end
376
- @graph.add_edge PetriNet::ReachabilityGraph::Edge.new(@graph, source: source, destination: current_node)# if node_id
377
- reachability_helper get_markings, current_node if node_id
437
+ if node_id < 0
438
+ current_node = @graph.get_node node_id.abs
439
+ end
440
+ @graph.add_edge PetriNet::ReachabilityGraph::Edge.new(@graph, source: source, destination: current_node, probability: @objects[tid].probability)# if node_id
441
+ reachability_helper get_markings, current_node if node_id >= 0
378
442
  end
379
443
  set_markings markings
380
444
  end
@@ -1,6 +1,6 @@
1
1
  module PetriNet
2
2
  # Current Version
3
- VERSION = "0.8.0"
3
+ VERSION = "0.9.4"
4
4
  # True if this is a debug-version and should not be used in productive systems
5
5
  DEBUG = false
6
6
  end
@@ -17,6 +17,6 @@ Gem::Specification.new do |gem|
17
17
 
18
18
  gem.license = 'MIT'
19
19
  gem.add_dependency "ruby-graphviz"
20
- gem.add_dependency "graphviz"
20
+ # gem.add_dependency "rgl"
21
21
  gem.add_development_dependency "net-sftp"
22
22
  end
@@ -71,7 +71,7 @@ Edges
71
71
  @net << PetriNet::Arc.new(source:@net.get_transition('T'), destination:@net.get_place('B'))
72
72
  @net.get_place('A').add_marking
73
73
  rn = @net.generate_reachability_graph
74
- assert_equal "Reachability Graph [SimpleNet1]\n----------------------------\nDescription: \nFilename: \n\nNodes\n----------------------------\n6: Node6 ([1, 0])\n7: Node7 ([0, 1])\n\nEdges\n----------------------------\n8: Edge8 6 -> 7: Node7 ([0, 1]) )\n\n", rn.to_s
74
+ assert_equal "Reachability Graph [SimpleNet1]\n----------------------------\nDescription: \nFilename: \n\nNodes\n----------------------------\n6: Node6 ([1, 0])\n7: Node7 ([0, 1])\n\nEdges\n----------------------------\n8: Edge8 6: Node6 ([1, 0]) -> 7: Node7 ([0, 1]) )\n\n", rn.to_s
75
75
 
76
76
  end
77
77
 
@@ -88,7 +88,7 @@ Edges
88
88
  @net << PetriNet::Arc.new(source:@net.get_transition('T2'), destination:@net.get_place('C'))
89
89
  @net.get_place('A').add_marking
90
90
  rn = @net.generate_reachability_graph
91
- assert_equal "Reachability Graph [SimpleNet2]\n----------------------------\nDescription: \nFilename: \n\nNodes\n----------------------------\n10: Node10 ([1, 0, 0])\n11: Node11 ([0, 1, 0])\n13: Node13 ([0, 0, 1])\n\nEdges\n----------------------------\n12: Edge12 10 -> 11: Node11 ([0, 1, 0]) )\n14: Edge14 10 -> 13: Node13 ([0, 0, 1]) )\n\n", rn.to_s
91
+ assert_equal "Reachability Graph [SimpleNet2]\n----------------------------\nDescription: \nFilename: \n\nNodes\n----------------------------\n10: Node10 ([1, 0, 0])\n11: Node11 ([0, 1, 0])\n13: Node13 ([0, 0, 1])\n\nEdges\n----------------------------\n12: Edge12 10: Node10 ([1, 0, 0]) -> 11: Node11 ([0, 1, 0]) )\n14: Edge14 10: Node10 ([1, 0, 0]) -> 13: Node13 ([0, 0, 1]) )\n\n", rn.to_s
92
92
 
93
93
  end
94
94
 
@@ -102,7 +102,7 @@ Edges
102
102
  @net << PetriNet::Arc.new(source:@net.get_transition('T'), destination:@net.get_place('A'))
103
103
  @net.get_place('A').add_marking
104
104
  rn = @net.generate_reachability_graph
105
- assert_equal "Reachability Graph [SimpleNet3]\n----------------------------\nDescription: \nFilename: \n\nNodes\n----------------------------\n7: Node7 ([1, 0])\n8: Node8 ([1, 1])\n10: Node10 ([Infinity])\n\nEdges\n----------------------------\n9: Edge9 7 -> 8: Node8 ([1, 1]) )\n11: Edge11 8 -> 10: Node10 ([Infinity]) )\n\n", rn.to_s
105
+ assert_equal "Reachability Graph [SimpleNet3]\n----------------------------\nDescription: \nFilename: \n\nNodes\n----------------------------\n7: Node7 ([1, 0])\n8: Node8 ([1, 1])\n10: Node10 ([Infinity])\n\nEdges\n----------------------------\n9: Edge9 7: Node7 ([1, 0]) -> 8: Node8 ([1, 1]) )\n11: Edge11 8: Node8 ([1, 1]) -> 10: Node10 ([Infinity]) )\n\n", rn.to_s
106
106
 
107
107
  end
108
108
 
@@ -119,7 +119,7 @@ Edges
119
119
  @net << PetriNet::Arc.new(source:@net.get_transition('T2'), destination:@net.get_place('A'))
120
120
  @net.get_place('A').add_marking
121
121
  rn = @net.generate_reachability_graph
122
- assert_equal "Reachability Graph [SimpleNet4]\n----------------------------\nDescription: \nFilename: \n\nNodes\n----------------------------\n10: Node10 ([1, 0])\n11: Node11 ([1, 1])\n13: Node13 ([Infinity])\n15: Node15 ([2, 0])\n17: Node17 ([Infinity])\n\nEdges\n----------------------------\n12: Edge12 10 -> 11: Node11 ([1, 1]) )\n14: Edge14 11 -> 13: Node13 ([Infinity]) )\n16: Edge16 10 -> 15: Node15 ([2, 0]) )\n18: Edge18 15 -> 17: Node17 ([Infinity]) )\n\n", rn.to_s
122
+ assert_equal "Reachability Graph [SimpleNet4]\n----------------------------\nDescription: \nFilename: \n\nNodes\n----------------------------\n10: Node10 ([1, 0])\n11: Node11 ([1, 1])\n13: Node13 ([Infinity])\n15: Node15 ([2, 0])\n17: Node17 ([Infinity])\n\nEdges\n----------------------------\n12: Edge12 10: Node10 ([1, 0]) -> 11: Node11 ([1, 1]) )\n14: Edge14 11: Node11 ([1, 1]) -> 13: Node13 ([Infinity]) )\n16: Edge16 10: Node10 ([1, 0]) -> 15: Node15 ([2, 0]) )\n18: Edge18 15: Node15 ([2, 0]) -> 17: Node17 ([Infinity]) )\n\n", rn.to_s
123
123
 
124
124
  end
125
125
 
@@ -136,7 +136,7 @@ Edges
136
136
  @net.get_place('A').add_marking
137
137
  @net.to_gv_new
138
138
  rn = @net.generate_reachability_graph
139
- assert_equal "Reachability Graph [SimpleNet5]\n----------------------------\nDescription: \nFilename: \n\nNodes\n----------------------------\n9: Node9 ([1, 0])\n10: Node10 ([0, 1])\n\nEdges\n----------------------------\n11: Edge11 9 -> 10: Node10 ([0, 1]) )\n\n", rn.to_s
139
+ assert_equal "Reachability Graph [SimpleNet5]\n----------------------------\nDescription: \nFilename: \n\nNodes\n----------------------------\n9: Node9 ([1, 0])\n10: Node10 ([0, 1])\n\nEdges\n----------------------------\n11: Edge11 9: Node9 ([1, 0]) -> 10: Node10 ([0, 1]) )\n13: Edge13 10: Node10 ([0, 1]) -> 9: Node9 ([1, 0]) )\n\n", rn.to_s
140
140
 
141
141
  rn.to_gv
142
142
  end
@@ -182,4 +182,21 @@ Edges
182
182
  @net << PetriNet::Transition.new(name:'T1')
183
183
  @net.generate_reachability_graph # Don't know what to test here, bit this crashed with an Error before...
184
184
  end
185
+
186
+ def test_looped_net1
187
+ @net = PetriNet::Net.new(:name => 'LoopedNet1', :description => 'Should be looped')
188
+ @net << PetriNet::Place.new(name: 'A')
189
+ @net << PetriNet::Place.new(name: 'B')
190
+ @net << PetriNet::Transition.new(name:'T1')
191
+ @net << PetriNet::Transition.new(name:'T2')
192
+ @net << PetriNet::Arc.new(source:@net.get_place('A'), destination:@net.get_transition('T1'))
193
+ @net << PetriNet::Arc.new(source:@net.get_place('B'), destination:@net.get_transition('T2'))
194
+ @net << PetriNet::Arc.new(source:@net.get_transition('T1'), destination:@net.get_place('B'))
195
+ @net << PetriNet::Arc.new(source:@net.get_transition('T2'), destination:@net.get_place('A'))
196
+ @net.get_place('A').add_marking
197
+ @net.to_gv_new
198
+ rg = @net.generate_reachability_graph
199
+ rg.to_gv
200
+ assert_equal "Reachability Graph [LoopedNet1]\n----------------------------\nDescription: \nFilename: \n\nNodes\n----------------------------\n9: Node9 ([1, 0])\n10: Node10 ([0, 1])\n\nEdges\n----------------------------\n11: Edge11 9: Node9 ([1, 0]) -> 10: Node10 ([0, 1]) )\n13: Edge13 10: Node10 ([0, 1]) -> 9: Node9 ([1, 0]) )\n\n", rg.to_s
201
+ end
185
202
  end
@@ -279,6 +279,11 @@ Edges
279
279
  assert_equal weight, @net.generate_weight_function
280
280
  end
281
281
 
282
+ def test_t_invariants
283
+ fill_net
284
+ assert BigDecimal("0.0") == @net.t_invariants.first
285
+ end
286
+
282
287
  def test_get_markings
283
288
  fill_net
284
289
  @net << PetriNet::Place.new(name: 'place2')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: petri_net
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - cclausen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-17 00:00:00.000000000 Z
11
+ date: 2014-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-graphviz
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: graphviz
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - '>='
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - '>='
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: net-sftp
43
29
  requirement: !ruby/object:Gem::Requirement