lutaml-uml 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/macos.yml +36 -0
  3. data/.github/workflows/ubuntu.yml +38 -0
  4. data/.github/workflows/windows.yml +41 -0
  5. data/.gitignore +1 -0
  6. data/Gemfile +2 -1
  7. data/LUTAML.adoc +314 -0
  8. data/{README.md → README.adoc} +15 -16
  9. data/Rakefile +3 -1
  10. data/bin/console +1 -0
  11. data/bin/lutaml2dotpng +23 -0
  12. data/bin/yaml2lutaml +108 -0
  13. data/exe/lutaml-uml +4 -3
  14. data/lib/lutaml/layout/engine.rb +15 -0
  15. data/lib/lutaml/layout/graph_viz_engine.rb +16 -0
  16. data/lib/lutaml/uml.rb +3 -0
  17. data/lib/lutaml/uml/abstraction.rb +7 -5
  18. data/lib/lutaml/uml/activity.rb +7 -5
  19. data/lib/lutaml/uml/actor.rb +14 -12
  20. data/lib/lutaml/uml/association.rb +40 -14
  21. data/lib/lutaml/uml/behavior.rb +7 -5
  22. data/lib/lutaml/uml/class.rb +56 -16
  23. data/lib/lutaml/uml/classifier.rb +9 -6
  24. data/lib/lutaml/uml/connector.rb +16 -12
  25. data/lib/lutaml/uml/constraint.rb +8 -7
  26. data/lib/lutaml/uml/constructor_end.rb +11 -8
  27. data/lib/lutaml/uml/data_type.rb +9 -4
  28. data/lib/lutaml/uml/dependency.rb +16 -13
  29. data/lib/lutaml/uml/document.rb +71 -0
  30. data/lib/lutaml/uml/enum.rb +33 -0
  31. data/lib/lutaml/uml/event.rb +7 -5
  32. data/lib/lutaml/uml/final_state.rb +7 -5
  33. data/lib/lutaml/uml/formatter.rb +21 -0
  34. data/lib/lutaml/uml/formatter/base.rb +67 -0
  35. data/lib/lutaml/uml/formatter/graphviz.rb +335 -0
  36. data/lib/lutaml/uml/has_attributes.rb +14 -0
  37. data/lib/lutaml/uml/has_members.rb +30 -0
  38. data/lib/lutaml/uml/instance.rb +15 -10
  39. data/lib/lutaml/uml/interface/base.rb +28 -0
  40. data/lib/lutaml/uml/interface/command_line.rb +265 -0
  41. data/lib/lutaml/uml/model.rb +11 -8
  42. data/lib/lutaml/uml/node/base.rb +21 -0
  43. data/lib/lutaml/uml/node/class_node.rb +57 -0
  44. data/lib/lutaml/uml/node/class_relationship.rb +14 -0
  45. data/lib/lutaml/uml/node/document.rb +18 -0
  46. data/lib/lutaml/uml/node/field.rb +34 -0
  47. data/lib/lutaml/uml/node/has_name.rb +15 -0
  48. data/lib/lutaml/uml/node/has_type.rb +15 -0
  49. data/lib/lutaml/uml/node/method.rb +29 -0
  50. data/lib/lutaml/uml/node/method_argument.rb +16 -0
  51. data/lib/lutaml/uml/node/relationship.rb +28 -0
  52. data/lib/lutaml/uml/opaque_behavior.rb +7 -6
  53. data/lib/lutaml/uml/package.rb +16 -13
  54. data/lib/lutaml/uml/parsers/attribute.rb +70 -0
  55. data/lib/lutaml/uml/parsers/dsl.rb +375 -0
  56. data/lib/lutaml/uml/parsers/dsl_preprocessor.rb +44 -0
  57. data/lib/lutaml/uml/parsers/dsl_transform.rb +27 -0
  58. data/lib/lutaml/uml/parsers/yaml.rb +46 -0
  59. data/lib/lutaml/uml/port.rb +6 -4
  60. data/lib/lutaml/uml/primitive_type.rb +9 -4
  61. data/lib/lutaml/uml/property.rb +25 -15
  62. data/lib/lutaml/uml/pseudostate.rb +7 -6
  63. data/lib/lutaml/uml/realization.rb +7 -5
  64. data/lib/lutaml/uml/region.rb +7 -6
  65. data/lib/lutaml/uml/serializers/association.rb +58 -0
  66. data/lib/lutaml/uml/serializers/base.rb +16 -0
  67. data/lib/lutaml/uml/serializers/class.rb +29 -0
  68. data/lib/lutaml/uml/serializers/top_element_attribute.rb +14 -0
  69. data/lib/lutaml/uml/serializers/yaml_view.rb +18 -0
  70. data/lib/lutaml/uml/state.rb +8 -6
  71. data/lib/lutaml/uml/state_machine.rb +7 -5
  72. data/lib/lutaml/uml/top_element.rb +45 -35
  73. data/lib/lutaml/uml/top_element_attribute.rb +25 -0
  74. data/lib/lutaml/uml/transition.rb +8 -6
  75. data/lib/lutaml/uml/trigger.rb +8 -6
  76. data/lib/lutaml/uml/version.rb +3 -1
  77. data/lib/lutaml/uml/vertex.rb +7 -5
  78. data/lutaml-uml.gemspec +9 -2
  79. data/spec/fixtures/datamodel/models/AddressClassProfile.yml +90 -0
  80. data/spec/fixtures/datamodel/models/AddressComponentProfile.yml +63 -0
  81. data/spec/fixtures/datamodel/models/AddressComponentSpecification.yml +15 -0
  82. data/spec/fixtures/datamodel/models/AddressProfile.yml +36 -0
  83. data/spec/fixtures/datamodel/models/AttributeProfile.yml +32 -0
  84. data/spec/fixtures/datamodel/models/InterchangeAddressClassProfile.yml +79 -0
  85. data/spec/fixtures/datamodel/models/Localization copy.yml +23 -0
  86. data/spec/fixtures/datamodel/models/Localization.yml +23 -0
  87. data/spec/fixtures/datamodel/models/ProfileCompliantAddress.yml +36 -0
  88. data/spec/fixtures/datamodel/models/ProfileCompliantAddressComponent.yml +15 -0
  89. data/spec/fixtures/datamodel/models/Signature.yml +20 -0
  90. data/spec/fixtures/datamodel/models/SignatureBlankDefinition.yml +20 -0
  91. data/spec/fixtures/datamodel/models/TextDirectionCode copy.yml +16 -0
  92. data/spec/fixtures/datamodel/models/TextDirectionCode.yml +16 -0
  93. data/spec/fixtures/datamodel/models/Validity.yml +14 -0
  94. data/spec/fixtures/datamodel/models/iso19160-1/Address.yml +22 -0
  95. data/spec/fixtures/datamodel/models/iso19160-1/AddressComponent.yml +2 -0
  96. data/spec/fixtures/datamodel/style.uml.inc +37 -0
  97. data/spec/fixtures/datamodel/views/AddressClassProfile.yml +12 -0
  98. data/spec/fixtures/datamodel/views/AddressProfile.yml +3 -0
  99. data/spec/fixtures/datamodel/views/CommonModels.yml +9 -0
  100. data/spec/fixtures/datamodel/views/TopDown.yml +62 -0
  101. data/spec/fixtures/dsl/diagram.lutaml +3 -0
  102. data/spec/fixtures/dsl/diagram_attributes.lutaml +5 -0
  103. data/spec/fixtures/dsl/diagram_class_assocation.lutaml +29 -0
  104. data/spec/fixtures/dsl/diagram_class_fields.lutaml +19 -0
  105. data/spec/fixtures/dsl/diagram_comments.lutaml +28 -0
  106. data/spec/fixtures/dsl/diagram_concept_model.lutaml +132 -0
  107. data/spec/fixtures/dsl/diagram_data_types.lutaml +24 -0
  108. data/spec/fixtures/dsl/diagram_includes.lutaml +6 -0
  109. data/spec/fixtures/dsl/diagram_multiply_classes.lutaml +7 -0
  110. data/spec/fixtures/dsl/shared.lutaml +3 -0
  111. data/spec/fixtures/dsl/shared1.lutaml +4 -0
  112. data/spec/fixtures/generated_dot/AddressClassProfile.dot +170 -0
  113. data/spec/fixtures/generated_dot/AddressProfile.dot +34 -0
  114. data/spec/lutaml/uml/formatter/graphviz_spec.rb +41 -0
  115. data/spec/lutaml/uml/parsers/dsl_spec.rb +252 -0
  116. data/spec/lutaml/uml/parsers/yaml_spec.rb +18 -0
  117. data/spec/lutaml/uml/serializers/yaml_view_spec.rb +20 -0
  118. data/spec/lutaml/uml_spec.rb +2 -4
  119. data/spec/spec_helper.rb +11 -0
  120. metadata +161 -13
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Uml
5
+ module HasAttributes
6
+ def update_attributes(attributes = {})
7
+ attributes.to_h.each do |name, value|
8
+ value = value.respond_to?(:str) ? value.str : value
9
+ public_send("#{name}=", value)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Uml
5
+ module HasMembers
6
+ class UnknownMemberTypeError < StandardError; end
7
+
8
+ # TODO: move to Parslet::Transform
9
+ def members=(value)
10
+ value.group_by { |member| member.keys.first }
11
+ .each do |(type, group)|
12
+ attribute_value = group.map(&:values).flatten
13
+ if attribute_value.length == 1 && !attribute_value.first.is_a?(Hash)
14
+ next public_send("#{associtaion_type(type)}=", attribute_value.first)
15
+ end
16
+
17
+ public_send("#{associtaion_type(type)}=", attribute_value)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def associtaion_type(type)
24
+ return type if respond_to?("#{type}=")
25
+
26
+ raise(UnknownMemberTypeError, "Unknown member type: #{type}")
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,12 +1,17 @@
1
- module Lutaml::Uml
1
+ # frozen_string_literal: true
2
2
 
3
- class Instance < TopElement
4
- attr_accessor :classifier, :slot
5
- def initialize
6
- @name = nil
7
- @xmi_id = nil
8
- @xmi_uuid = nil
9
- @classifier = nil
10
- @slot = []
11
- end
3
+ module Lutaml
4
+ module Uml
5
+ class Instance < TopElement
6
+ attr_accessor :classifier, :slot
7
+
8
+ def initialize
9
+ @name = nil
10
+ @xmi_id = nil
11
+ @xmi_uuid = nil
12
+ @classifier = nil
13
+ @slot = []
14
+ end
15
+ end
16
+ end
12
17
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require "lutaml/uml/has_attributes"
5
+
6
+ module Lutaml
7
+ module Uml
8
+ module Interface
9
+ class Base
10
+ def self.run(attributes = {})
11
+ new(attributes).run
12
+ end
13
+
14
+ include HasAttributes
15
+
16
+ # rubocop:disable Rails/ActiveRecordAliases
17
+ def initialize(attributes = {})
18
+ update_attributes(attributes)
19
+ end
20
+ # rubocop:enable Rails/ActiveRecordAliases
21
+
22
+ def run
23
+ raise NotImplementedError
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,265 @@
1
+ require "optparse"
2
+ require "pathname"
3
+ require "lutaml/uml/interface/base"
4
+ require "lutaml/uml/parsers/dsl"
5
+ require "lutaml/uml/parsers/yaml"
6
+ require "lutaml/uml/parsers/attribute"
7
+ require "lutaml/uml/formatter"
8
+
9
+ module Lutaml
10
+ module Uml
11
+ module Interface
12
+ class CommandLine < Base
13
+ class Error < StandardError; end
14
+ class FileError < Error; end
15
+ class NotSupportedInputFormat < Error; end
16
+
17
+ SUPPORTED_FORMATS = %w[yaml dsl].freeze
18
+ DEFAULT_INPUT_FORMAT = "dsl".freeze
19
+
20
+ def initialize(attributes = {})
21
+ @formatter = Formatter::Graphviz.new
22
+ @verbose = false
23
+ @option_parser = OptionParser.new
24
+
25
+ setup_parser_options
26
+
27
+ super
28
+ end
29
+
30
+ def output_path=(value)
31
+ @output_path = determine_output_path_value(value)
32
+ end
33
+
34
+ def determine_output_path_value(value)
35
+ unless value.nil? || @output_path = value.is_a?(Pathname)
36
+ return Pathname.new(value.to_s)
37
+ end
38
+
39
+ value
40
+ end
41
+
42
+ def paths=(values)
43
+ @paths = values.to_a.map { |path| Pathname.new(path) }
44
+ end
45
+
46
+ def formatter=(value)
47
+ value = value.to_s.strip.downcase.to_sym
48
+ value = Formatter.find_by(name: value)
49
+ raise Error, "Formatter not found: #{value}" if value.nil?
50
+
51
+ @formatter = value
52
+ end
53
+
54
+ def input_format=(value)
55
+ if value.nil?
56
+ @input_format = DEFAULT_INPUT_FORMAT
57
+ return
58
+ end
59
+
60
+ @input_format = SUPPORTED_FORMATS.detect { |n| n == value }
61
+ raise(NotSupportedInputFormat, value) if @input_format.nil?
62
+ end
63
+
64
+ def run
65
+ args = ARGV.dup # TODO: This is hacky
66
+ begin
67
+ @option_parser.parse!(args)
68
+ rescue StandardError
69
+ nil
70
+ end
71
+ setup_parser_formatter_options
72
+ @option_parser.parse!
73
+
74
+ self.paths = ARGV
75
+ @formatter.type = @type
76
+
77
+ if @output_path&.file? && @paths.length > 1
78
+ raise Error,
79
+ 'Output path must be a directory \
80
+ if multiple input files are given'
81
+ end
82
+
83
+ @paths.each do |input_path|
84
+ unless input_path.exist?
85
+ raise FileError, "File does not exist: #{input_path}"
86
+ end
87
+
88
+ document = if @input_format == "yaml"
89
+ Parsers::Yaml.parse(input_path)
90
+ else
91
+ Parsers::Dsl.parse(File.new(input_path))
92
+ end
93
+ result = @formatter.format(document)
94
+
95
+ if @output_path
96
+ output_path = @output_path
97
+ if output_path.directory?
98
+ output_path = output_path.join(input_path
99
+ .basename(".*").to_s +
100
+ ".#{@formatter.type}")
101
+ end
102
+
103
+ output_path.open("w+") { |file| file.write(result) }
104
+ else
105
+ puts result
106
+ end
107
+ end
108
+ end
109
+
110
+ protected
111
+
112
+ def text_bold(body = nil)
113
+ text_effect(1, body)
114
+ end
115
+
116
+ def text_italic(body = nil)
117
+ text_effect(3, body)
118
+ end
119
+
120
+ def text_bold_italic(body = nil)
121
+ text_bold(text_italic(body))
122
+ end
123
+
124
+ def text_underline(body = nil)
125
+ text_effect(4, body)
126
+ end
127
+
128
+ def text_effect(num, body = nil)
129
+ result = "\e[#{num}m"
130
+ result << "#{body}#{text_reset}" unless body.nil?
131
+
132
+ result
133
+ end
134
+
135
+ def text_reset
136
+ "\e[0m"
137
+ end
138
+
139
+ def setup_parser_options
140
+ @option_parser.banner = ""
141
+ format_desc = "The output formatter (Default: '#{@formatter.name}')"
142
+ @option_parser
143
+ .on("-f",
144
+ "--formatter VALUE",
145
+ format_desc) do |value|
146
+ self.formatter = value
147
+ end
148
+ @option_parser
149
+ .on("-t", "--type VALUE", "The output format type") do |value|
150
+ @type = value
151
+ end
152
+ @option_parser
153
+ .on("-o", "--output VALUE", "The output path") do |value|
154
+ self.output_path = value
155
+ end
156
+ @option_parser
157
+ .on("-i", "--input-format VALUE", "The input format") do |value|
158
+ self.input_format = value
159
+ end
160
+ @option_parser
161
+ .on("-h", "--help", "Prints this help") do
162
+ print_help
163
+ exit
164
+ end
165
+ end
166
+
167
+ def setup_parser_formatter_options
168
+ case @formatter.name
169
+ when :graphviz
170
+ @option_parser.on("-g", "--graph VALUE") do |value|
171
+ Parsers::Attribute.parse(value).each do |key, attr_value|
172
+ @formatter.graph[key] = attr_value
173
+ end
174
+ end
175
+
176
+ @option_parser.on("-e", "--edge VALUE") do |value|
177
+ Parsers::Attribute.parse(value).each do |key, attr_value|
178
+ @formatter.edge[key] = attr_value
179
+ end
180
+ end
181
+
182
+ @option_parser.on("-n", "--node VALUE") do |value|
183
+ Parsers::Attribute.parse(value).each do |key, attr_value|
184
+ @formatter.node[key] = attr_value
185
+ end
186
+ end
187
+
188
+ @option_parser.on("-a", "--all VALUE") do |value|
189
+ Parsers::Attribute.parse(value).each do |key, attr_value|
190
+ @formatter.graph[key] = attr_value
191
+ @formatter.edge[key] = attr_value
192
+ @formatter.node[key] = attr_value
193
+ end
194
+ end
195
+ end
196
+ end
197
+
198
+ def print_help
199
+ puts <<~HELP
200
+ #{text_bold('Usage:')} lutaml-uml [options] PATHS
201
+
202
+ #{text_bold('Overview:')} Generate output from UML Class Diagram language files
203
+
204
+ #{text_bold('Options:')}
205
+ #{@option_parser}
206
+ #{text_bold('Paths:')}
207
+
208
+ UCD can accept multiple paths for parsing for easier batch processing.
209
+
210
+ The location of the output by default is standard output.
211
+
212
+ The output can be directed to a path with #{text_bold_italic('--output')}, which can be a file or a directory.
213
+ If the output path is a directory, then the filename will be the same as the input filename,
214
+ with it's file extension substituted with the #{text_bold_italic('--type')}.
215
+
216
+ #{text_underline('Examples')}
217
+
218
+ `lutaml-uml project.lutaml`
219
+
220
+ Produces DOT notation, sent to standard output
221
+
222
+ `lutaml-uml -o . project.lutaml`
223
+
224
+ Produces DOT notation, written to #{text_italic('./project.dot')}
225
+
226
+ `lutaml-uml -o ./diagram.dot project.lutaml`
227
+
228
+ Produces DOT notation, written to #{text_italic('./diagram.dot')}
229
+
230
+ `lutaml-uml -o ./diagram.png project.lutaml`
231
+
232
+ Produces PNG image, written to #{text_italic('./diagram.png')}
233
+
234
+ `lutaml-uml -t png -o . project.lutaml`
235
+
236
+ Produces PNG image, written to #{text_italic('./project.png')}
237
+
238
+ `lutaml-uml -t png -o . project.lutaml-uml core_ext.lutaml`
239
+
240
+ Produces PNG images, written to #{text_italic('./project.png')} and #{text_italic('./core_ext.png')}
241
+
242
+ #{text_bold('Formatters:')}
243
+
244
+ #{text_underline('Graphviz')}
245
+
246
+ Generates DOT notation and can use the DOT notation to generate any format Graphviz can produce.
247
+
248
+ The output format is based on #{text_bold_italic('--type')}, which by default is "dot".
249
+ If #{text_bold_italic('--type')} is not given and #{text_bold_italic('--output')} is, the file extension of the #{text_bold_italic('--output')} path will be used.
250
+
251
+ Valid types/extensions are: #{Formatter::Graphviz::VALID_TYPES.join(', ')}
252
+
253
+ #{text_bold('Options:')}
254
+
255
+ -g, --graph VALUE The graph attributes
256
+ -e, --edge VALUE The edge attributes
257
+ -n, --node VALUE The node attributes
258
+ -a, --all VALUE Set attributes for graph, edge, and node
259
+
260
+ HELP
261
+ end
262
+ end
263
+ end
264
+ end
265
+ end
@@ -1,10 +1,13 @@
1
- module Lutaml::Uml
1
+ # frozen_string_literal: true
2
2
 
3
- class Model < Package
4
- attr_accessor :viewpoint
5
- def initialize
6
- @contents = []
7
- end
8
- end
3
+ module Lutaml
4
+ module Uml
5
+ class Model < Package
6
+ attr_accessor :viewpoint
9
7
 
10
- end
8
+ def initialize
9
+ @contents = []
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/uml/has_attributes"
4
+
5
+ module Lutaml
6
+ module Uml
7
+ module Node
8
+ class Base
9
+ include HasAttributes
10
+
11
+ # rubocop:disable Rails/ActiveRecordAliases
12
+ def initialize(attributes = {})
13
+ update_attributes(attributes)
14
+ end
15
+ # rubocop:enable Rails/ActiveRecordAliases
16
+
17
+ attr_accessor :parent
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/uml/node/base"
4
+ require "lutaml/uml/node/field"
5
+ require "lutaml/uml/node/method"
6
+ require "lutaml/uml/node/relationship"
7
+ require "lutaml/uml/node/class_relationship"
8
+ require "lutaml/uml/node/has_name"
9
+
10
+ module Lutaml
11
+ module Uml
12
+ module Node
13
+ class ClassNode < Base
14
+ include HasName
15
+
16
+ attr_reader :modifier
17
+
18
+ def modifier=(value)
19
+ @modifier = value.to_s # TODO: Validate?
20
+ end
21
+
22
+ attr_reader :members
23
+
24
+ def members=(value)
25
+ @members = value.to_a.map do |member|
26
+ type = member.to_a[0][0] # TODO: This is dumb
27
+ attributes = member.to_a[0][1]
28
+ attributes[:parent] = self
29
+
30
+ case type
31
+ when :field then Field.new(attributes)
32
+ when :method then Method.new(attributes)
33
+ when :relationship then Relationship.new(attributes)
34
+ when :class_relationship then ClassRelationship.new(attributes)
35
+ end
36
+ end
37
+ end
38
+
39
+ def fields
40
+ @members.select { |member| member.class == Field }
41
+ end
42
+
43
+ def methods
44
+ @members.select { |member| member.class == Method }
45
+ end
46
+
47
+ def relationships
48
+ @members.select { |member| member.class == Relationship }
49
+ end
50
+
51
+ def class_relationships
52
+ @members.select { |member| member.class == ClassRelationship }
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end