graphy 0.1.0 → 0.1.2

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
  SHA256:
3
- metadata.gz: 5855af538a3e42839fb6291076af3a81d7d6e117624e0ff912dfb537599b1d78
4
- data.tar.gz: 43f3be7f19de34cf97743b03bdab151ae7cbc3d8fbe970802434dc11618dca33
3
+ metadata.gz: 90422fd37ee6183c87ab531c8841bfea10d9da59385f10f9d440b4095aeb24a8
4
+ data.tar.gz: 8bcb771712386cbeb5f9d2fb2e9e76030c9a6a18e79d8550f3bf28610d7d4624
5
5
  SHA512:
6
- metadata.gz: 811d052517b6a4a32b354818976a62f6fd38dc695ad7df03b21601b58793f923909d7337aa1968547d55eb4e6c5a4f7f92213740210a63396376a1485de22bd1
7
- data.tar.gz: 51c60f6b786035b87ee4357358db75b7edab3d7f0ffd6f605412b3d221081fc598f12ac4261cc7e02cc80685f779f3eba53c0788830c9794c078b6746042229d
6
+ metadata.gz: 5980d5b6a4c0ef3ef5867a11cf91b8c927431803c8f2d74b6b091b2d789b06fa1715cce4b6b6db927708bafb61c405e47027945388a087fc4e1c35232401d102
7
+ data.tar.gz: 3526fbda93e7ad3a960eb4e722024a89d1633414f45eeac5309ca7f8f2d7e223c9a900954d9ef05db4de543e256fc0d437ed33ec9bb3084f0e8daa92a08ac3cd
data/.gitignore CHANGED
@@ -6,3 +6,4 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ Gemfile.lock
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Graphy
2
2
 
3
- Create any diagram as code
3
+ DSL to create graph diagrams as code
4
4
 
5
5
  ## Installation
6
6
 
@@ -23,7 +23,7 @@ Or install it yourself as:
23
23
  ### Module
24
24
 
25
25
  ```ruby
26
- Graphy.define 'UML View' do
26
+ Graphy.define 'Life View' do
27
27
  entity 'Friendly'
28
28
  namespace "Animal Kingdom" do
29
29
  entity 'Animal' do
@@ -31,12 +31,12 @@ Graphy.define 'UML View' do
31
31
  end
32
32
 
33
33
  entity 'Dog', 'Animal' do
34
- attributes :color
34
+ attrs :color
35
35
  uses 'Friendly'
36
36
  end
37
37
 
38
38
  entity 'Cat', 'Animal' do
39
- attributes :scratch
39
+ meths :scratch
40
40
  end
41
41
  end
42
42
 
@@ -44,10 +44,13 @@ Graphy.define 'UML View' do
44
44
  end
45
45
  ```
46
46
 
47
+ ![example02.png](https://github.com/3zcurdia/graphy/blob/master/examples/example02.rb.png)
48
+
49
+
47
50
  ### State machine
48
51
 
49
52
  ```ruby
50
- Graphy.define 'State machine' do
53
+ Graphy.define 'State machine', orientation: :horizontal do
51
54
  node 'A'
52
55
  node 'B', shape: 'diamond'
53
56
  node 'C'
@@ -62,6 +65,7 @@ Graphy.define 'State machine' do
62
65
  write(png: 'steps.png')
63
66
  end
64
67
  ```
68
+ ![example01.png](https://github.com/3zcurdia/graphy/blob/master/examples/example01.rb.png)
65
69
 
66
70
  ## Development
67
71
 
@@ -2,12 +2,12 @@ require 'bundler/setup'
2
2
  require 'graphviz/dsl'
3
3
 
4
4
  digraph :example do
5
- cluster_0 do
6
- graph[label: 'Animal Kingdom']
7
- animal << dog
8
- animal << cat
9
- end
10
- friendly << dog
5
+ cluster_0 do
6
+ graph[label: 'Animal Kingdom']
7
+ animal << dog
8
+ animal << cat
9
+ end
10
+ friendly << dog
11
11
 
12
- output(png: "#{$0}.png")
12
+ output(png: "#{$0}.png")
13
13
  end
Binary file
@@ -1,7 +1,7 @@
1
1
  require 'bundler/setup'
2
2
  require 'graphy'
3
3
 
4
- Graphy.define do
4
+ Graphy.define 'State machine', orientation: :horizonal do
5
5
  node 'A'
6
6
  node 'B', shape: 'diamond'
7
7
  node 'C'
Binary file
@@ -2,18 +2,17 @@ require 'bundler/setup'
2
2
  require 'graphy'
3
3
 
4
4
  Graphy.define 'Life View' do
5
- entity 'Friendly'
6
5
  entity 'Animal' do
7
- attributes :name, :other
6
+ attrs :name, :other
8
7
  end
9
8
 
10
9
  entity 'Dog', 'Animal' do
11
- attributes :color
10
+ attrs :color
12
11
  uses 'Friendly'
13
12
  end
14
13
 
15
14
  entity 'Cat', 'Animal' do
16
- attributes :scratch
15
+ meths :scratch
17
16
  end
18
17
 
19
18
  write png: "#{$0}.png"
Binary file
@@ -0,0 +1,15 @@
1
+ require 'bundler/setup'
2
+ require 'graphy'
3
+
4
+ Graphy.define 'Life View' do
5
+ entity 'Friendly'
6
+ namespace 'Animal Kingdom' do
7
+ entity 'Animal'
8
+ entity 'Dog', 'Animal' do
9
+ uses 'Friendly'
10
+ end
11
+ entity 'Cat', 'Animal'
12
+ end
13
+
14
+ write png: "#{$0}.png"
15
+ end
Binary file
@@ -1,8 +1,9 @@
1
1
  require 'graphviz'
2
2
  require 'graphy/node'
3
3
  require 'graphy/entity'
4
- require 'graphy/dependency'
4
+ require 'graphy/diagram'
5
5
  require 'graphy/dsl'
6
+ require 'graphy/registry'
6
7
  require 'graphy/version'
7
8
 
8
9
  module Graphy
@@ -0,0 +1,75 @@
1
+ module Graphy
2
+ ## GraphViz wrapper
3
+ class Diagram
4
+ attr_accessor :graph, :edges
5
+
6
+ GRAPH_ATTRIBUTES = {
7
+ ranksep: 0.5,
8
+ nodesep: 0.4,
9
+ pad: '0.4,0.4',
10
+ margin: '0,0',
11
+ concentrate: true,
12
+ labelloc: :t,
13
+ fontsize: 14,
14
+ fontname: 'Arial BoldMT',
15
+ splines: 'spline'
16
+ }
17
+
18
+ NODE_ATTRIBUTES = {
19
+ shape: "Mrecord",
20
+ fontsize: 12,
21
+ fontname: "ArialMT",
22
+ margin: "0.07,0.05",
23
+ penwidth: 1.0
24
+ }
25
+
26
+ EDGE_ATTRIBUTES = {
27
+ fontname: "ArialMT",
28
+ fontsize: 10,
29
+ arrowsize: 0.9,
30
+ penwidth: 1.0,
31
+ labelangle: 32,
32
+ labeldistance: 1.8,
33
+ }
34
+
35
+ def initialize(name, options = {})
36
+ graph_opts = {
37
+ parent: options[:parent],
38
+ type: options[:parent]&.type
39
+ }.compact
40
+ @graph = GraphViz.new(name, **graph_opts)
41
+
42
+ GRAPH_ATTRIBUTES.each { |attribute, value| @graph[attribute] = value }
43
+ NODE_ATTRIBUTES.each { |attribute, value| @graph.node[attribute] = value }
44
+ EDGE_ATTRIBUTES.each { |attribute, value| @graph.edge[attribute] = value }
45
+
46
+ @graph[:rankdir] = options[:orientation] == :horizonal ? :LR : :TB
47
+ @graph[:label] = "#{name}\\n\\n"
48
+ @edges = []
49
+ end
50
+
51
+ def get_node(name)
52
+ graph.search_node(name)
53
+ end
54
+
55
+ def node_exists?(name)
56
+ !!get_node(name)
57
+ end
58
+
59
+ def draw_node(name, options = {})
60
+ graph.add_nodes(name, options)
61
+ end
62
+
63
+ def draw_edge(from, to, options)
64
+ graph.add_edges(from, to, options)
65
+ end
66
+
67
+ def draw_graph(diagram)
68
+ graph.add_graph(diagram.graph)
69
+ end
70
+
71
+ def write(options = {})
72
+ graph.output(options)
73
+ end
74
+ end
75
+ end
@@ -1,24 +1,14 @@
1
1
  module Graphy
2
2
  class Dsl
3
- @registry = {}
4
- @edges = []
5
-
6
- def self.registry
7
- @registry
8
- end
9
-
10
- def self.edges
11
- @edges
12
- end
3
+ attr_accessor :diagram
13
4
 
14
5
  def initialize(name, options = {}, &block)
15
- @graph = GraphViz.new(name, map_options(options.merge(label: name)))
16
- instance_eval(&block) if block
6
+ @diagram = Diagram.new(name, options, &block)
7
+ instance_eval(&block) if block_given?
17
8
  end
18
9
 
19
10
  def namespace(name, &block)
20
- opts = { parent: graph, type: graph.type }
21
- graph.add_graph(Dsl.new(name, opts, &block).graph)
11
+ diagram.draw_graph(Dsl.new(name, { parent: diagram.graph }, &block).diagram)
22
12
  end
23
13
 
24
14
  def component(name, &block)
@@ -30,55 +20,22 @@ module Graphy
30
20
  end
31
21
 
32
22
  def node(name, shape: 'circle', &block)
33
- return graph.get_node(name) unless graph.get_node(name.to_s).nil?
34
-
35
- node = Node.new(name, graph: graph, shape: shape)
36
- node.instance_eval(&block) if block_given?
37
- Dsl.registry[name] = node
23
+ options = {diagram: diagram, shape: shape}
24
+ Node.for(name, **options).build(&block)
38
25
  end
39
26
 
40
27
  def entity(name, parent = nil, &block)
41
- return graph.get_node(name) unless graph.get_node(name.to_s).nil?
42
-
43
- entity = Entity.new(name, graph: graph)
44
- entity.add_dependency(parent, type: :parent) if parent
45
- entity.instance_eval(&block) if block_given?
46
- Dsl.registry[name] = entity
47
- end
48
-
49
- def step(from, to:, options: {})
50
- return if Dsl.edges.any? { |x| [from, to] == x }
51
- source = Dsl.registry[from].graph_node
52
- dest = Dsl.registry[to].graph_node
53
- return if source.nil? || dest.nil?
54
-
55
- Dsl.edges << [from, to]
56
- graph.add_edge(source, dest, options)
28
+ entity = Entity.new(name: name, diagram: diagram)
29
+ entity.build(&block)
30
+ entity.add_dependency(parent, color: 'blue') if parent
57
31
  end
58
32
 
59
- def graph
60
- build
61
- @graph
33
+ def step(from, to:, **options)
34
+ Node.for(from, diagram: diagram).add_dependency(to, **options)
62
35
  end
63
36
 
64
37
  def write(options = {})
65
- graph.output(options)
66
- end
67
-
68
- private
69
-
70
- def build
71
- Dsl.registry.each do |name, node|
72
- node.dependencies.each do |dep|
73
- step(dep.ref, to: name, options: dep.edge_attributes)
74
- end
75
- end
76
- end
77
-
78
- def map_options(options = {})
79
- align = options.delete(:align)
80
- rankdir = align == :horizontal ? 'LR' : nil
81
- options.merge(rankdir: rankdir).compact
38
+ diagram.write(options)
82
39
  end
83
40
  end
84
41
  end
@@ -1,16 +1,24 @@
1
1
  module Graphy
2
2
  class Entity < Node
3
- def initialize(name, graph:)
4
- super(name, graph: graph, shape: 'record')
3
+ def initialize(**params)
4
+ super(**params.merge(shape: 'Mrecord'))
5
5
  @attributes = []
6
6
  end
7
7
 
8
- def attributes(*values)
8
+ def attrs(*values)
9
9
  @attributes = values
10
10
  end
11
11
 
12
+ def meths(*values)
13
+ @attributes << values.map { |x| "#{x}()"}
14
+ end
15
+
12
16
  protected
13
17
 
18
+ def draw_edge(dependency, **options)
19
+ diagram.draw_edge(dependency.gnode, gnode, options)
20
+ end
21
+
14
22
  def label
15
23
  "{ #{name} #{attributes_label} }"
16
24
  end
@@ -19,7 +27,7 @@ module Graphy
19
27
 
20
28
  def attributes_label
21
29
  return if @attributes.empty?
22
- "| #{@attributes.join("\n")}"
30
+ "| #{@attributes.join("\n")}"
23
31
  end
24
32
  end
25
33
  end
@@ -1,38 +1,68 @@
1
1
  module Graphy
2
2
  class Node
3
- attr_accessor :graph, :name, :shape, :penwidth, :fontsize, :style, :fillcolor, :dependencies
4
- def initialize(name, graph:, shape: 'circle')
5
- @name = name
6
- @graph = graph
7
- @shape = shape
8
- @dependencies = []
3
+ attr_accessor :name, :shape, :diagram
4
+
5
+ def self.for(node, **params)
6
+ case node
7
+ when Entity, Node
8
+ node
9
+ when String
10
+ self.new(**params.merge(name: node))
11
+ else
12
+ raise 'Invalid node class'
13
+ end
9
14
  end
10
15
 
11
- def graph_node
12
- @graph_node ||= graph.add_nodes(name, node_attributes)
16
+ def initialize(**params)
17
+ @name = params[:name].to_s
18
+ @diagram = params[:diagram]
19
+ @shape = params.fetch(:shape, 'circle')
13
20
  end
14
21
 
15
- def add_dependency(dependency, type: nil)
16
- @dependencies << Dependency.for(dependency, type: type)
22
+ def add_dependency(dependency, **options)
23
+ dependency = self.class.for(dependency, diagram: diagram)
24
+ return if Registry.edge?(self, dependency)
25
+
26
+ Registry.add_edge(self, dependency)
27
+ draw_edge(dependency, **options)
17
28
  end
18
29
  alias :uses :add_dependency
19
30
 
31
+ def build(&block)
32
+ instance_eval(&block) if block_given?
33
+ gnode
34
+ end
35
+
36
+ def gnode
37
+ @gnode ||= find_or_draw
38
+ end
39
+
40
+ def to_s
41
+ "#{self.class}<#{name}>"
42
+ end
43
+
20
44
  protected
21
45
 
22
- def node_attributes
46
+ def draw_edge(dependency, **options)
47
+ diagram.draw_edge(gnode, dependency.gnode, options)
48
+ end
49
+
50
+ def node_params
23
51
  {
24
- shape: shape,
25
- penwidth: penwidth,
26
- fontsize: fontsize,
27
- fontname: 'Helvetica',
28
- style: style,
29
- fillcolor: fillcolor,
30
- label: label
31
- }.compact
52
+ label: label,
53
+ shape: shape
54
+ }
32
55
  end
33
56
 
34
57
  def label
35
58
  "#{name}\n"
36
59
  end
60
+
61
+ private
62
+
63
+ def find_or_draw
64
+ return diagram.get_node(name) if diagram.node_exists?(name)
65
+ diagram.draw_node(name, node_params)
66
+ end
37
67
  end
38
68
  end
@@ -0,0 +1,28 @@
1
+ require 'singleton'
2
+
3
+ module Graphy
4
+ class Registry
5
+ include Singleton
6
+ attr_accessor :edges
7
+
8
+ def self.edge?(source, destiny)
9
+ instance.edge?(source, destiny)
10
+ end
11
+
12
+ def self.add_edge(source, destiny)
13
+ instance.add_edge(source, destiny)
14
+ end
15
+
16
+ def initialize
17
+ @edges = {}
18
+ end
19
+
20
+ def edge?(source, destiny)
21
+ edges.key?("#{source}->#{destiny}")
22
+ end
23
+
24
+ def add_edge(source, destiny, options = {})
25
+ self.edges["#{source}->#{destiny}"] = options
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module Graphy
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.1.2'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luis Ezcurdia Razo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-21 00:00:00.000000000 Z
11
+ date: 2020-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-graphviz
@@ -35,7 +35,6 @@ files:
35
35
  - ".travis.yml"
36
36
  - CODE_OF_CONDUCT.md
37
37
  - Gemfile
38
- - Gemfile.lock
39
38
  - LICENSE.txt
40
39
  - README.md
41
40
  - Rakefile
@@ -47,12 +46,15 @@ files:
47
46
  - examples/example01.rb.png
48
47
  - examples/example02.rb
49
48
  - examples/example02.rb.png
49
+ - examples/example03.rb
50
+ - examples/example03.rb.png
50
51
  - graphy.gemspec
51
52
  - lib/graphy.rb
52
- - lib/graphy/dependency.rb
53
+ - lib/graphy/diagram.rb
53
54
  - lib/graphy/dsl.rb
54
55
  - lib/graphy/entity.rb
55
56
  - lib/graphy/node.rb
57
+ - lib/graphy/registry.rb
56
58
  - lib/graphy/version.rb
57
59
  homepage: https://github.com/3zcurdia/graphy
58
60
  licenses:
@@ -1,25 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- arq (0.1.0)
5
- ruby-graphviz (~> 1.2.0)
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- minitest (5.14.2)
11
- rake (12.3.3)
12
- rexml (3.2.4)
13
- ruby-graphviz (1.2.5)
14
- rexml
15
-
16
- PLATFORMS
17
- ruby
18
-
19
- DEPENDENCIES
20
- arq!
21
- minitest (~> 5.0)
22
- rake (~> 12.0)
23
-
24
- BUNDLED WITH
25
- 2.1.4
@@ -1,29 +0,0 @@
1
- module Graphy
2
- class Dependency
3
- def self.for(reference, type: nil)
4
- case type
5
- when :parent
6
- Parent
7
- else
8
- Default
9
- end.new(reference)
10
- end
11
-
12
- class Default
13
- attr_accessor :ref
14
- def initialize(ref)
15
- @ref = ref
16
- end
17
-
18
- def edge_attributes
19
- {}
20
- end
21
- end
22
-
23
- class Parent < Default
24
- def edge_attributes
25
- { style: 'dotted', color: 'blue' }
26
- end
27
- end
28
- end
29
- end