lib/vizier.rb in davidlee-state-fu-0.3.1 vs lib/vizier.rb in davidlee-state-fu-0.10.0

- old
+ new

@@ -2,47 +2,47 @@ begin require File.join(File.dirname(__FILE__), '/state_fu/core_ext' ) rescue LoadError require 'activesupport' -end - -# sorry, there's only Heisendocumentation (if I realize anyone's looking for -# them, I might write some) - -# temporary dirty hack - -module Vizier - +end + +# Vizier is a simple library to help generate dot output for graphviz. It is used by StateFu's rake +# tasks to generate graphs of state machines. +# +# Sorry, there's only Heisendocumentation (if I realize anyone's looking for docs, I might write some) +# +module Vizier #:nodoc:all + module Support LEGAL_CHARS = 'a-zA-Z0-9_' - + def attributes=( attrs ) @attributes = attrs.symbolize_keys!.extend( Attributes ) end - + def attributes (@attributes ||= {}).extend( Attributes ) end - + def legal?( str ) str =~ /^[#{LEGAL_CHARS}]+$/ && str == str.split end - + def sanitize(str) sanitize( str ) end - + def quote( str ) return str if legal?( str ) '"' + str.to_s.gsub(/"/,'\"') + '"' end - + def self.included( klass ) klass.extend( ClassMethods ) end - + module Finder def []( idx ) begin super( idx ) rescue TypeError => e @@ -54,82 +54,82 @@ raise e end end end end - + module ClassMethods def sanitize( str ) str.to_s.gsub(/[^#{LEGAL_CHARS}]/,'_').gsub(/__+/,'_') end - + def finder( name ) class_eval do define_method name do instance_variable_get( "@#{name}" ).extend( Finder ) end end end end end - + module Attributes include Support - + def to_s return '[]' if empty? '[ ' + self.map do |k,v| "#{quote k} = #{quote v}" end.join(" ") + ' ]' end - + end - + class Base def [](k) attributes[k.to_sym] end - + def []=(k,v) attributes[k.to_sym] = v end end - + class Link < Base include Support attr_accessor :from attr_accessor :to - + def initialize( from, to, attrs={} ) self.attributes = attrs @from = extract_name( from ) @to = extract_name( to ) end - + def extract_name( o ) o.is_a?(String) ? o : o.name end - + def to_str "#{quote from} -> #{quote to} #{attributes};" end end - + # TODO .. module Label def []( i ) - + end end - + class Node < Base include Support - + attr_accessor :object attr_accessor :fields attr_accessor :name - + def initialize( name = nil, attrs={} ) self.attributes = attrs if name.is_a?( String ) self.name = name @label = attrs.delete(:label) || name @@ -137,97 +137,97 @@ @object = name self.name = Node.make_name( @object ) @label = attrs.delete(:label) || Node.first_response( @object, :name, :identifier, :label ) || name end end - + def self.make_name( obj ) sanitize [ obj.class, first_response( obj, :name, :identifier, :id, :hash)].join('_') end - + def self.first_response obj, *method_names responder = method_names.flatten.detect { |m| obj.respond_to?(m) } obj.send( responder ) unless responder.nil? end - + def name=( str ) @name = str.to_s.gsub(/[^a-zA-Z0-9_]/,'_').gsub(/__+/,'_') end - + def to_str "#{quote name} #{attributes.to_s};" end - + def to_s quote( name ) end end - + class SubGraph < Base include Support - + finder :nodes - + attr_accessor :links attr_accessor :name - + def initialize( name, attrs={} ) self.attributes = attrs @node = {} @edge = {} - + @name = name @nodes = [] @links = [] end - + def node(attrs={}) (@node ||= {}).merge!(attrs).extend(Attributes) end - + def graph(attrs={}) self.attributes.merge!(attrs).extend(Attributes) end - + def edge(attrs={}) (@edge ||= {}).merge!(attrs).extend(Attributes) end - + def add_node( n, a={} ) returning Node.new(n,a) do |n| @nodes << n end end - + def add_link(from, to, a={}) returning Link.new( from, to, a) do |l| @links << l end end alias_method :connect, :add_link alias_method :add_edge, :add_link - + def build(lines = [], indent = 0) lines.map do |line| if line.is_a?( Array ) build( line, indent + 1) else (" " * (indent * 4) ) + line.to_s end end.join("\n") end - + def write_comment( str, j = 0 ) l = 40 - (j * 4) i = ' ' * (j * 4) "\n#{i}/*#{'*'*(l-2)}\n#{i}** #{ str.ljust((l - (6) - (j*4)),' ') }#{i} **\n#{i}#{'*'*(l-1)}/" end - + def comment(str) write_comment(str, 2) end - + def to_str build( ["subgraph #{quote name} {", [ # attributes.map {|k,v| "#{quote k} = #{quote v};" }, ["graph #{attributes};", "node #{node};", @@ -238,20 +238,20 @@ "}" ], ]) end alias_method :generate!, :to_str - + end - + class Graph < SubGraph finder :subgraphs - + def comment( str ) write_comment( str, 1 ) end - + def to_str build(["digraph #{quote name} {", [ comment("global options"), "graph #{graph};", @@ -265,36 +265,36 @@ comment("subgraphs"), subgraphs.map(&:to_str), "}"]) end alias_method :generate!, :to_str - + def publish!( a = {} ) generate! # -> png end - + def subgraph(name, a = {}) returning( SubGraph.new(name, a)) do |g| @subgraphs << g yield g if block_given? end end - + def cluster(name = nil, a = {}, &block) if name && name = "cluster_#{name}" subgraph( name, a, &block ) else clusters end end - + def clusters @subgraphs.select {|s| s.name =~ /^cluster_/ }.extend( Finder ) end - + def initialize(name = 'my_graph', attrs = {}) @subgraphs = [] super( name, attrs ) yield self if block_given? end end -end \ No newline at end of file +end