agreement-design-prototype 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/src/diagram.rb CHANGED
@@ -1,67 +1,13 @@
1
1
  require 'fileutils'
2
- require_relative 'doc'
2
+ require_relative 'transform'
3
+ include Transform
3
4
 
5
+ class Diagram < Output
4
6
 
5
- class TableElement < Element
6
- def initialize file, node
7
- super(file)
8
- @node = node
9
- pput %Q("#{@node}" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>#{@node}</TD></TH>)
10
- @items = []
7
+ def initialize dir, name
8
+ super dir, name, ".dot"
11
9
  end
12
10
 
13
- def item i
14
- @items << i
15
- self.pput %Q(<TR><TD ALIGN="LEFT">-#{i}</TD></TR>)
16
- end
17
-
18
- def finish
19
- # self.pput " " << @items.join( ",")
20
- self.pput "</table>"
21
- self.pput ">];\n"
22
- end
23
-
24
-
25
- end
26
-
27
- class Graph < Element
28
- def initialize file
29
- super(file)
30
- self.pput "strict digraph {\n"
31
- end
32
-
33
- def finish
34
- self.pput "}\n"
35
- end
36
-
37
- end
38
-
39
- class SubGraph < Element
40
- def initialize file, model
41
- super(file)
42
- self.pput "subgraph cluster_#{model} {\n"
43
- self.pput "node [shape=plaintext margin=0];\n"
44
- self.pput "label=#{model};\n"
45
- end
46
-
47
- def finish
48
- self.pput "}\n"
49
- end
50
-
51
- end
52
-
53
- class Links < Element
54
-
55
- def link el1, el2, label: el, arrowhead: nil, arrowtail: nil
56
- ah = arrowhead ? %Q!arrowhead = "#{arrowhead}"! : ""
57
- at = arrowtail ? %Q!arrowtail = "#{arrowtail}"! : ""
58
- pput %Q!"#{el1}" -> "#{el2}" [label="#{label}" #{ah} #{at} ];\n!
59
- end
60
-
61
- end
62
-
63
- class Diagram < Doc
64
-
65
11
  def dotfile
66
12
  File.join(diagram_path, "#{self.name}.dot")
67
13
  end
@@ -75,42 +21,56 @@ class Diagram < Doc
75
21
  FileUtils.mkpath diagram_path
76
22
  FileUtils.mkpath image_path
77
23
  File.open(self.dotfile, "w") do |file|
78
- graph = Graph.new(file)
79
- for model in models
80
- subgraph = SubGraph.new(file, modelname(model))
81
- for type in model.types.values
82
- table = TableElement.new(file, typename(type))
83
- for att in type.attributes(false).values
84
- table.item att_detail(att)
85
- end
86
- table.finish
87
- end
88
- subgraph.finish
89
- end
90
- for model in models
91
- links = Links.new(file)
92
- for type in model.types.values
93
- for att in type.attributes(false).keys
94
- if type.attributes[att][:links]
95
- contains = (type.attributes[att][:type] < DataType)
96
- links.link typename(type),
97
- typename(type.attributes[att][:links]),
98
- label: %Q!#{contains ? "{contains} " : ""}#{type.attributes[att][:name]}!,
99
- arrowhead: contains ? "none": "open",
100
- arrowtail: contains ? "diamond": "none"
101
- end
102
- end
103
- if type.superclass < DataType
104
- links.link typename(type),
105
- typename(type.superclass),
24
+ # nodes
25
+ file.print "strict digraph {\n"
26
+
27
+ transform_metamodel(
28
+ {
29
+ before_model: lambda do |model:|
30
+ file.print "subgraph cluster_#{model} {\n"
31
+ file.print "node [shape=plaintext margin=0];\n"
32
+ file.print "label=#{model};\n"
33
+ end,
34
+ after_model: lambda do |model:, before:|
35
+ file.print "}\n"
36
+ end,
37
+ before_type: lambda do |type:, depth:, index:, total:|
38
+ file.print %Q("#{type.typename}" [label=<<table BORDER="1" CELLBORDER="0" CELLSPACING="0"><TH><TD>#{type.typename}</TD></TH>)
39
+ end,
40
+ after_type: lambda do |type:, depth:, before:|
41
+ file.print "</table>"
42
+ file.print ">];\n"
43
+ end,
44
+ attribute: lambda do |id:, val:, depth:, type:, index:, total:|
45
+ file.print %Q(<TR><TD ALIGN="LEFT">-#{id}</TD></TR>)
46
+ end,
47
+ },
48
+ *models
49
+ )
50
+ # links
51
+ transform_metamodel(
52
+ {
53
+ before_type: lambda do |type:, depth:, index:, total:|
54
+ if type.superclass < DataType
55
+ link(file, type.typename, type.superclass.typename,
106
56
  label: "extends",
107
57
  arrowhead: "none",
108
- arrowtail: "normal"
109
- end
110
- end
111
- links.finish
112
- end
113
- graph.finish
58
+ arrowtail: "normal")
59
+ end
60
+ end,
61
+ :attribute => lambda do |id:, val:, depth: 0, type:, index:, total:|
62
+ if val[:links] || val[:type] < DataType
63
+ contains = (val[:type] < DataType)
64
+ link(file, type.typename, val[:links] ? val[:links].typename : val[:type].typename,
65
+ label: %Q!#{contains ? "{contains} " : ""}#{val[:name]}!,
66
+ arrowhead: contains ? "none" : "open",
67
+ arrowtail: contains ? "diamond" : "none")
68
+ end
69
+ end,
70
+ },
71
+ *models
72
+ )
73
+ file.print "}\n"
114
74
  end
115
75
 
116
76
  unless system("dot -Tjpg #{self.dotfile} > #{self.jpgfile}")
@@ -136,11 +96,17 @@ class Diagram < Doc
136
96
  private
137
97
 
138
98
  def image_path
139
- File.join(self.path, "images")
99
+ File.join(self.dir, "images")
140
100
  end
141
101
 
142
102
  def diagram_path
143
- File.join(self.path, "diagrams")
103
+ File.join(self.dir, "diagrams")
104
+ end
105
+
106
+ def link file, el1, el2, label: el2, arrowhead: nil, arrowtail: nil
107
+ ah = arrowhead ? %Q!arrowhead = "#{arrowhead}"! : ""
108
+ at = arrowtail ? %Q!arrowtail = "#{arrowtail}"! : ""
109
+ file.print %Q!"#{el1}" -> "#{el2}" [label="#{label}" #{ah} #{at} ];\n!
144
110
  end
145
111
 
146
112
  end
data/src/doc.rb CHANGED
@@ -1,114 +1,61 @@
1
- class Element
2
- def initialize file
3
- @file = file
4
- end
5
-
6
- def finish
7
- self
8
- end
9
-
10
- protected
11
-
12
- def pput(string)
13
- @file.print string
14
- self
15
- end
16
-
17
- end
18
-
19
- def cond_call(depth, id, value, lam, lambdas)
20
- if lambdas[lam]
21
- return lambdas[lam].(depth, id, value)
22
- end
23
- return 0
24
- end
25
-
26
- # each lambda takes the depth, id, value
27
- # Lambdas are:
28
- # :before_type_lambda,
29
- # :after_type_lambda,
30
- # :before_array_lambda,
31
- # :after_array_lambda,
32
- # :attribute_lambda
33
- # which takes a type and returns an object (the id)
34
- # depth is the number counting from zero, incremented with array elements and nested types.
35
- # ID is a nested int of the type a for types and a.b for array types,
36
- # and is the name of the attribute for attributes
37
- # decl is the type or attribute or array
1
+ require_relative 'transform'
2
+ include Transform
38
3
 
39
- def transform_type depth, id, decl, lambdas
4
+ class Document < Output
40
5
 
41
- if decl.class <= Array
42
- cond_call(depth, id, decl, :before_array_lambda, lambdas)
43
- j = 1
44
- for aa in decl
45
- transform_type(depth , "#{id}.#{j}", aa, lambdas)
46
- j = j + 1
47
- end
48
- cond_call(depth, id, decl, :after_array_lambda, lambdas)
49
- elsif decl.class <= DataType
50
- cond_call(depth, id, decl, :before_type_lambda, lambdas)
51
- for ak in decl.attributes.keys
52
- av = decl.attributes[ak]
53
- transform_type(depth + 1, ak, av, lambdas)
54
- end
55
- cond_call(depth, id, decl, :after_type_lambda, lambdas)
56
- else
57
- cond_call(depth, id, decl, :attribute_lambda, lambdas)
6
+ def initialize dir, name
7
+ super File.join( dir, "doc"), name, "md"
58
8
  end
59
- self
60
- end
61
-
62
- class ClassNameElement < Element
63
9
 
64
- def write cat
65
- pput %Q!\# #{cat.class}: #{cat.name}\n!
66
- if (cat.respond_to?(:description))
67
- pput %Q! #{cat.description}\n!
10
+ def document *models
11
+ file do |file|
12
+ transform_datamodel(
13
+ {
14
+ :before_type => lambda do |type:, depth: 0, index:,total:|
15
+ file.print %Q!####{'#' * depth} #{type.name} #{type.attributes[:id] || ""} \n!
16
+ end,
17
+ :attribute => lambda do |id:, val:, depth: 0, type: nil, index:, total:|
18
+ file.print %Q!#{" " * depth} - #{id} #{val}\n!
19
+ end
20
+ }, *models)
68
21
  end
69
22
  end
70
23
 
71
- end
72
-
73
- class GroupElement < Element
74
-
75
- def write grpname, level: 2
76
- pput %Q! #{'#' * level} #{grpname}\n!
77
- end
78
-
79
- end
80
-
81
- class TypeDeclElement < Element
82
-
83
- def write indent, id, decl
84
- transform_type(indent, id, decl,
85
- {
86
- :before_type_lambda => lambda do |indent, id, decl|
87
- pput %Q!####{'#' * indent} #{decl.name} #{decl.attributes[:id] || id} \n!
88
- end,
89
- :attribute_lambda => lambda do |indent, id, decl|
90
- pput %Q!#{" " * indent} - #{id} #{decl}\n!
91
- end
92
- })
93
- self
94
- end
95
-
96
- end
97
-
98
- class MetaTypeElement < Element
99
-
100
- def write type
101
- pput "## #{type.typename}"
102
- if type.extends
103
- pput " extends #{type.extends}"
104
- end
105
- pput "\n #{type.description}\n\n"
106
- pput "|attribute|type|multiplicity|description|\n"
107
- pput "|---------|----|------------|-----------|\n"
108
- for a in type.attributes.values
109
- pput "|#{a[:name]}|#{a[:type]}|#{self.multiplicity(a)}|#{a[:description]}|\n"
24
+ def document_metamodel *models
25
+ file do |file|
26
+ transform_metamodel(
27
+ {
28
+ before_model: lambda do |model:|
29
+ file.print %Q!\# Data model: #{model}\n!
30
+ end,
31
+ before_group: lambda do |group|
32
+ file.print %Q!\# #{cat.class}: #{cat.name}\n!
33
+ if (cat.respond_to?(:description))
34
+ file.print %Q! #{cat.description}\n!
35
+ end
36
+ end,
37
+ before_type: lambda do |type:, depth:, index:, total:|
38
+ file.print "## #{type.typename}"
39
+ if type.extends
40
+ file.print " extends #{type.extends}"
41
+ end
42
+ file.print "\n #{type.description}\n\n"
43
+ file.print "|attribute|type|multiplicity|description|\n"
44
+ file.print "|---------|----|------------|-----------|\n"
45
+ end,
46
+ attribute: lambda do |id:, val:, type:, depth:, index:, total:|
47
+ file.print "|#{val[:name]}|#{type_and_link(val)}|#{multiplicity(val)}|#{val[:description]}|\n"
48
+ end,
49
+ before_codes: lambda do |model:|
50
+ file.print "# Codes\n"
51
+ end,
52
+ code: lambda do |model:, code:|
53
+ file.print "## #{code[:id]} #{code[:title]}\n"
54
+ file.print "#{code[:description]}\n"
55
+ file.print "#{code[:uri]}\n"
56
+ end
57
+ }, *models)
110
58
  end
111
- self
112
59
  end
113
60
 
114
61
  def multiplicity m
@@ -125,57 +72,14 @@ class MetaTypeElement < Element
125
72
  end
126
73
  return m.to_s
127
74
  end
128
- end
129
-
130
- class Doc
131
- attr_accessor :path, :name
132
75
 
133
- def initialize path, name
134
- self.path = path
135
- self.name = name
136
- end
76
+ private
137
77
 
138
- def document *models
139
- FileUtils.mkpath doc_path
140
- File.open(self.docfile, "w") do |file|
141
- for model in models
142
- dom = ClassNameElement.new(file)
143
- dom.write model
144
- for typename in model.contents.keys
145
- grp = GroupElement.new(file)
146
- grp.write typename
147
- i = 1
148
- for decl in model.contents[typename]
149
- TypeDeclElement.new(file).write(0, i, decl).finish
150
- i = i + 1
151
- end
152
- grp.finish
153
- end
154
- dom.finish
155
- end
78
+ def type_and_link(val)
79
+ if val[:links]
80
+ return "#{val[:type]} -> #{val[:links]}"
156
81
  end
157
- end
158
-
159
- def document_metamodel *models
160
- FileUtils.mkpath doc_path
161
- File.open(self.docfile, "w") do |file|
162
- for model in models
163
- dom = GroupElement.new(file)
164
- dom.write model.name, level: 1
165
- for type in model.types.values
166
- MetaTypeElement.new(file).write(type).finish
167
- end
168
- dom.finish
169
- end
170
- end
171
- end
172
-
173
- def docfile
174
- File.join(doc_path, "#{self.name}.md")
175
- end
176
-
177
- def doc_path
178
- File.join(self.path, "doc")
82
+ val[:type]
179
83
  end
180
84
 
181
85
  end
data/src/transform.rb ADDED
@@ -0,0 +1,144 @@
1
+ module Transform
2
+
3
+ class Output
4
+ public
5
+
6
+ attr_accessor :dir, :name, :ext
7
+
8
+ def initialize dir, name, ext
9
+ self.dir = dir
10
+ self.name = name
11
+ self.ext = ext
12
+ end
13
+
14
+ def filepath
15
+ File.join(self.dir, "#{name}.#{ext}")
16
+ end
17
+
18
+ def file(&block)
19
+ FileUtils.mkpath dir
20
+ File.open(filepath, "w", &block)
21
+ end
22
+ end
23
+
24
+
25
+ def before_model_lambda model: nil
26
+ return [model: model]
27
+ end
28
+
29
+ def after_model_lambda model: nil, before: nil
30
+ return [model: model, before: before]
31
+ end
32
+
33
+ def before_codes_lambda model: nil
34
+ return [model: model]
35
+ end
36
+
37
+ def code_lambda model: nil, code: nil
38
+ return [model: model, code: code]
39
+ end
40
+
41
+ def before_group_lambda name: nil, depth: 0
42
+ return [name: name, depth: depth]
43
+ end
44
+
45
+ def after_group_lambda name: nil, before: nil, depth: 0
46
+ return [name: name, before: before, depth: depth]
47
+ end
48
+
49
+ def before_type_lambda type: nil, depth: 0, index: 0, total: 1
50
+ return [type: type, depth: depth, index: index, total: total]
51
+ end
52
+
53
+ def after_type_lambda type: nil, before: nil, depth: 0
54
+ return [type: type, before: before, depth: depth]
55
+ end
56
+
57
+ def before_array_lambda name: nil, decl: nil, depth: 0, total: 1
58
+ return [name: name, decl: decl, depth: depth, total: total]
59
+ end
60
+
61
+ def after_array_lambda index: 0, decl: nil, depth: 0, before: nil
62
+ return [index: index, decl: decl, depth: depth, before: before]
63
+ end
64
+
65
+ def attribute_lambda id:, val:, depth: 0, type: nil, index:, total:
66
+ return [id: id, val: val, depth: depth, type: type, index: index, total: total]
67
+ end
68
+
69
+ # @param models - the models to transform
70
+ # @lambdas - set of function callbacks to transform
71
+
72
+ def transform_datamodel lambdas, *models
73
+ for model in models
74
+ dom = cond_call(lambdas, :before_model, *before_model_lambda(model: model))
75
+ for typename in model.contents.keys
76
+ grp = cond_call(lambdas, :before_group, *before_group_lambda(name: typename))
77
+ t = 0
78
+ # there will be more than one of each type
79
+ for type in model.contents[typename]
80
+ transform_type(lambdas, decl: type, name: typename, depth: 0, index: 0, total: 1)
81
+ t = t + 1
82
+ end
83
+ grp = cond_call(lambdas, :after_group, *after_group_lambda(name: typename, before: grp))
84
+ end
85
+ cond_call(lambdas, :after_model, *after_model_lambda(model: model, before: dom))
86
+ end
87
+ end
88
+
89
+ def transform_metamodel lambdas, *models
90
+ for model in models
91
+ dom = cond_call(lambdas, :before_model, *before_model_lambda(model: model))
92
+ for type in model.types.values
93
+ before = cond_call(lambdas, :before_type, *before_type_lambda(type: type, index:0, total:1))
94
+ i = 0
95
+ for ak in type.attributes.keys
96
+ av = type.attributes[ak]
97
+ cond_call(lambdas, :attribute, *attribute_lambda(id: ak, val: av, type: type, index: i, total: type.attributes.keys.length))
98
+ i = i + 1
99
+ end
100
+ cond_call(lambdas, :after_type, *after_type_lambda(type: type, before: before))
101
+ end
102
+ cond_call(lambdas, :after_model, *after_model_lambda(model: model, before: dom))
103
+ if model.respond_to? :codes
104
+ cond_call(lambdas, :before_codes, *before_codes_lambda(model: model))
105
+ for code in model.codes.values
106
+ cond_call(lambdas, :code, *code_lambda(model: model, code: code))
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ def cond_call(lambdas, lam, *args)
113
+ if lambdas[lam]
114
+ return lambdas[lam].(*args)
115
+ end
116
+ return 0
117
+ end
118
+
119
+ def transform_type(lambdas, index:, name:, decl:, depth:, total:)
120
+
121
+ if decl.class <= Array
122
+ arrctx = cond_call(lambdas, :before_array, *before_array_lambda(name: name, decl: decl, depth: depth, total: decl.length))
123
+ j = 0
124
+ for aa in decl
125
+ transform_type(lambdas, index: j, decl: aa, name: name, depth: depth, total: decl.length)
126
+ j = j + 1
127
+ end
128
+ cond_call(lambdas, :after_array, *after_array_lambda(index: index, decl: decl, depth: depth, before: arrctx))
129
+ elsif decl.class <= DataType
130
+ before = cond_call(lambdas, :before_type, *before_type_lambda(type: decl, depth: depth, index: index, total: total))
131
+ i = 0
132
+ for ak in decl.attributes.keys
133
+ av = decl.attributes[ak]
134
+ transform_type(lambdas, name: ak, decl: av, depth: depth + 1, index: i, total: decl.attributes.keys.length)
135
+ i = i + 1
136
+ end
137
+ cond_call(lambdas, :after_type, *after_type_lambda(type: decl, depth: depth, before: before))
138
+ else
139
+ cond_call(lambdas, :attribute, *attribute_lambda(id: name, val: decl, index: index, depth: depth, total: total))
140
+ end
141
+ self
142
+ end
143
+
144
+ end