Skip to content

Instantly share code, notes, and snippets.

@tenderlove
Created March 8, 2011 01:53
Show Gist options
  • Save tenderlove/859696 to your computer and use it in GitHub Desktop.
Save tenderlove/859696 to your computer and use it in GitHub Desktop.
diff --git a/lib/middleware/visitor.rb b/lib/middleware/visitor.rb
new file mode 100644
index 0000000..fdfb96d
--- /dev/null
+++ b/lib/middleware/visitor.rb
@@ -0,0 +1,100 @@
+module Middleware
+ class Visitor
+ class Node < Struct.new(:id, :name)
+ end
+
+ class Edge < Struct.new(:from, :to, :name)
+ end
+
+ def initialize
+ @nodes = []
+ @edges = []
+ @edge_stack = []
+ @node_stack = []
+ @seen = {}
+ end
+
+ def accept o
+ visit o
+ nodes = @nodes.map { |node|
+ "#{node.id} [label=\"#{node.name}\"];"
+ }.join("\n")
+ edges = @edges.map { |edge|
+ "#{edge.from.id} -> #{edge.to.id} [label=\"#{edge.name}\"];"
+ }.join("\n")
+
+ "digraph \"rack middleware\" {\n
+
+node [
+shape = \"box\"
+];
+ #{nodes}\n#{edges}\n}"
+ end
+
+ private
+
+ def visit_Rack_Mount_RouteSet o
+ named_routes = o.instance_variable_get(:@named_routes)
+ named_routes.each do |k,v|
+ @edge_stack.push "@named_routes[#{k}]"
+ visit v
+ @edge_stack.pop
+ end
+
+ routes = o.instance_variable_get(:@routes)
+ routes.each_with_index do |k,i|
+ @edge_stack.push "@routes[#{i}]"
+ visit k
+ @edge_stack.pop
+ end
+ end
+
+ def visit_Rack_Mount_Route o
+ app = o.instance_variable_get(:@app)
+ @edge_stack.push :@app
+ visit app
+ @edge_stack.pop
+ end
+
+ def visit o
+ method = "visit_#{o.class.name.gsub('::', '_')}"
+
+ return if !o.respond_to?(:call) && !respond_to?(method, true)
+
+ if @seen.key?(o)
+ unless @edge_stack.empty?
+ @edges << Edge.new(@node_stack.last, @seen[o], @edge_stack.last)
+ end
+ return
+ end
+
+ node = Node.new(o.object_id, o.class.name)
+
+ @seen[o] = node
+ @nodes << node
+
+ unless @edge_stack.empty?
+ @edges << Edge.new(@node_stack.last, node, @edge_stack.last)
+ end
+
+ @node_stack.push node
+
+ if respond_to?(method, true)
+ send(method, o)
+ else
+ generic o
+ end
+
+ @node_stack.pop
+ end
+
+ def generic o
+ o.instance_variables.each do |ivar|
+ child = o.instance_variable_get(ivar)
+ @edge_stack.push ivar
+ visit child
+ @edge_stack.pop
+ end
+ end
+ end
+end
diff --git a/lib/rack/server.rb b/lib/rack/server.rb
index 6bf8771..f4bef58 100644
--- a/lib/rack/server.rb
+++ b/lib/rack/server.rb
@@ -1,4 +1,5 @@
require 'optparse'
+require 'middleware/visitor'
module Rack
class Server
@@ -228,6 +229,10 @@ module Rack
end
end
+ viz = Middleware::Visitor.new
+ ::File.open('/tmp/foo.dot', 'wb') { |f|
+ f.write viz.accept wrapped_app
+ }
server.run wrapped_app, options
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment