lutaml-uml 0.1.0 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/macos.yml +38 -0
  3. data/.github/workflows/ubuntu.yml +40 -0
  4. data/.github/workflows/windows.yml +51 -0
  5. data/.gitignore +1 -0
  6. data/Gemfile +2 -1
  7. data/LUTAML.adoc +339 -0
  8. data/{README.md → README.adoc} +15 -16
  9. data/Rakefile +3 -1
  10. data/bin/console +1 -0
  11. data/bin/folder_yaml2lutaml.sh +6 -0
  12. data/bin/plantuml2lutaml +58 -0
  13. data/bin/yaml2lutaml +144 -0
  14. data/exe/lutaml-uml +4 -3
  15. data/lib/lutaml/layout/engine.rb +15 -0
  16. data/lib/lutaml/layout/graph_viz_engine.rb +19 -0
  17. data/lib/lutaml/uml.rb +4 -0
  18. data/lib/lutaml/uml/abstraction.rb +7 -5
  19. data/lib/lutaml/uml/activity.rb +7 -5
  20. data/lib/lutaml/uml/actor.rb +14 -12
  21. data/lib/lutaml/uml/association.rb +40 -14
  22. data/lib/lutaml/uml/behavior.rb +7 -5
  23. data/lib/lutaml/uml/class.rb +61 -16
  24. data/lib/lutaml/uml/classifier.rb +9 -6
  25. data/lib/lutaml/uml/connector.rb +16 -12
  26. data/lib/lutaml/uml/constraint.rb +8 -7
  27. data/lib/lutaml/uml/constructor_end.rb +11 -8
  28. data/lib/lutaml/uml/data_type.rb +12 -4
  29. data/lib/lutaml/uml/dependency.rb +16 -13
  30. data/lib/lutaml/uml/document.rb +71 -0
  31. data/lib/lutaml/uml/enum.rb +40 -0
  32. data/lib/lutaml/uml/event.rb +7 -5
  33. data/lib/lutaml/uml/final_state.rb +7 -5
  34. data/lib/lutaml/uml/formatter.rb +21 -0
  35. data/lib/lutaml/uml/formatter/base.rb +67 -0
  36. data/lib/lutaml/uml/formatter/graphviz.rb +334 -0
  37. data/lib/lutaml/uml/has_attributes.rb +14 -0
  38. data/lib/lutaml/uml/has_members.rb +30 -0
  39. data/lib/lutaml/uml/instance.rb +15 -10
  40. data/lib/lutaml/uml/interface/base.rb +28 -0
  41. data/lib/lutaml/uml/interface/command_line.rb +265 -0
  42. data/lib/lutaml/uml/lutaml_path/document_wrapper.rb +15 -0
  43. data/lib/lutaml/uml/model.rb +11 -8
  44. data/lib/lutaml/uml/node/base.rb +21 -0
  45. data/lib/lutaml/uml/node/class_node.rb +57 -0
  46. data/lib/lutaml/uml/node/class_relationship.rb +14 -0
  47. data/lib/lutaml/uml/node/document.rb +18 -0
  48. data/lib/lutaml/uml/node/field.rb +34 -0
  49. data/lib/lutaml/uml/node/has_name.rb +15 -0
  50. data/lib/lutaml/uml/node/has_type.rb +15 -0
  51. data/lib/lutaml/uml/node/method.rb +29 -0
  52. data/lib/lutaml/uml/node/method_argument.rb +16 -0
  53. data/lib/lutaml/uml/node/relationship.rb +28 -0
  54. data/lib/lutaml/uml/opaque_behavior.rb +7 -6
  55. data/lib/lutaml/uml/package.rb +16 -13
  56. data/lib/lutaml/uml/parsers/attribute.rb +70 -0
  57. data/lib/lutaml/uml/parsers/dsl.rb +399 -0
  58. data/lib/lutaml/uml/parsers/dsl_preprocessor.rb +44 -0
  59. data/lib/lutaml/uml/parsers/dsl_transform.rb +27 -0
  60. data/lib/lutaml/uml/parsers/yaml.rb +46 -0
  61. data/lib/lutaml/uml/port.rb +6 -4
  62. data/lib/lutaml/uml/primitive_type.rb +11 -3
  63. data/lib/lutaml/uml/property.rb +25 -15
  64. data/lib/lutaml/uml/pseudostate.rb +7 -6
  65. data/lib/lutaml/uml/realization.rb +7 -5
  66. data/lib/lutaml/uml/region.rb +7 -6
  67. data/lib/lutaml/uml/serializers/association.rb +58 -0
  68. data/lib/lutaml/uml/serializers/base.rb +16 -0
  69. data/lib/lutaml/uml/serializers/class.rb +29 -0
  70. data/lib/lutaml/uml/serializers/top_element_attribute.rb +14 -0
  71. data/lib/lutaml/uml/serializers/yaml_view.rb +18 -0
  72. data/lib/lutaml/uml/state.rb +8 -6
  73. data/lib/lutaml/uml/state_machine.rb +7 -5
  74. data/lib/lutaml/uml/top_element.rb +45 -35
  75. data/lib/lutaml/uml/top_element_attribute.rb +26 -0
  76. data/lib/lutaml/uml/transition.rb +8 -6
  77. data/lib/lutaml/uml/trigger.rb +8 -6
  78. data/lib/lutaml/uml/version.rb +3 -1
  79. data/lib/lutaml/uml/vertex.rb +7 -5
  80. data/lutaml-uml.gemspec +11 -3
  81. data/spec/fixtures/datamodel/models/AddressClassProfile.yml +90 -0
  82. data/spec/fixtures/datamodel/models/AddressComponentProfile.yml +63 -0
  83. data/spec/fixtures/datamodel/models/AddressComponentSpecification.yml +15 -0
  84. data/spec/fixtures/datamodel/models/AddressProfile.yml +36 -0
  85. data/spec/fixtures/datamodel/models/AttributeProfile.yml +32 -0
  86. data/spec/fixtures/datamodel/models/InterchangeAddressClassProfile.yml +79 -0
  87. data/spec/fixtures/datamodel/models/Localization copy.yml +23 -0
  88. data/spec/fixtures/datamodel/models/Localization.yml +23 -0
  89. data/spec/fixtures/datamodel/models/ProfileCompliantAddress.yml +36 -0
  90. data/spec/fixtures/datamodel/models/ProfileCompliantAddressComponent.yml +15 -0
  91. data/spec/fixtures/datamodel/models/Signature.yml +20 -0
  92. data/spec/fixtures/datamodel/models/SignatureBlankDefinition.yml +20 -0
  93. data/spec/fixtures/datamodel/models/TextDirectionCode copy.yml +16 -0
  94. data/spec/fixtures/datamodel/models/TextDirectionCode.yml +16 -0
  95. data/spec/fixtures/datamodel/models/Validity.yml +14 -0
  96. data/spec/fixtures/datamodel/models/iso19160-1/Address.yml +22 -0
  97. data/spec/fixtures/datamodel/models/iso19160-1/AddressComponent.yml +2 -0
  98. data/spec/fixtures/datamodel/style.uml.inc +37 -0
  99. data/spec/fixtures/datamodel/views/AddressClassProfile.yml +12 -0
  100. data/spec/fixtures/datamodel/views/AddressProfile.yml +3 -0
  101. data/spec/fixtures/datamodel/views/CommonModels.yml +9 -0
  102. data/spec/fixtures/datamodel/views/TopDown.yml +62 -0
  103. data/spec/fixtures/dsl/diagram.lutaml +3 -0
  104. data/spec/fixtures/dsl/diagram_attributes.lutaml +5 -0
  105. data/spec/fixtures/dsl/diagram_class_assocation.lutaml +29 -0
  106. data/spec/fixtures/dsl/diagram_class_fields.lutaml +19 -0
  107. data/spec/fixtures/dsl/diagram_comments.lutaml +28 -0
  108. data/spec/fixtures/dsl/diagram_concept_model.lutaml +132 -0
  109. data/spec/fixtures/dsl/diagram_data_types.lutaml +24 -0
  110. data/spec/fixtures/dsl/diagram_definitions.lutaml +20 -0
  111. data/spec/fixtures/dsl/diagram_includes.lutaml +6 -0
  112. data/spec/fixtures/dsl/diagram_multiply_classes.lutaml +7 -0
  113. data/spec/fixtures/dsl/shared.lutaml +3 -0
  114. data/spec/fixtures/dsl/shared1.lutaml +4 -0
  115. data/spec/fixtures/generated_dot/AddressClassProfile.dot +170 -0
  116. data/spec/fixtures/generated_dot/AddressProfile.dot +34 -0
  117. data/spec/lutaml/layout/graph_viz_engine_spec.rb +31 -0
  118. data/spec/lutaml/uml/formatter/graphviz_spec.rb +41 -0
  119. data/spec/lutaml/uml/parsers/dsl_spec.rb +276 -0
  120. data/spec/lutaml/uml/parsers/yaml_spec.rb +18 -0
  121. data/spec/lutaml/uml/serializers/yaml_view_spec.rb +20 -0
  122. data/spec/lutaml/uml_spec.rb +2 -4
  123. data/spec/spec_helper.rb +11 -0
  124. metadata +182 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b99520ee95878d541ac76755c71cfc9ff71a3551c27ba9391a774fd92273f502
4
- data.tar.gz: f17e20b2e03e66df0efdcc33937b37657e963e7f2d19655e30ba665020ce7b00
3
+ metadata.gz: 79a4d0228330313679cd5545dd940cc978334821a8a8c3b37686cbba9e6b2bf0
4
+ data.tar.gz: 6c835a1f42bfa3b9d961c8924e020aaf0c41c8361c47032639db6dbc4349f1f8
5
5
  SHA512:
6
- metadata.gz: c6f48df0e2c0055b3bdb0b32c7abb135c72e94509e0fdce555f18df21dfa145a54ca77afd8e762152a17a12e8c835765ef47c85a982ed4e90ba8da9ee3ca714a
7
- data.tar.gz: d16ce0f4fd44ead1300aa28d3bfb07b86dbc5db3ca58aa5d2a45898552220118048f6f415eb212c2ad88d3c9826357d0bd74bfb9f159f3a02b992d49426a9b0f
6
+ metadata.gz: f219cfc72ffb715c66ff37a2d5f4b565c959ecde36ed9c1d872ef02a80113c2b8b4d0eb6dc8891f886ab5dc48a0cccb1e4ba54a0b16b5a2eb33634e05814b7fa
7
+ data.tar.gz: 258fb342974bdbcbd9f212bdfe329c5710784d43c10531b171384ea510ebb84ac2b39a165d5b572023c9517402594d790d818102de96f07715cce7e00e0f8343
@@ -0,0 +1,38 @@
1
+ name: macos
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ paths-ignore:
8
+ - .github/workflows/ubuntu.yml
9
+ - .github/workflows/windows.yml
10
+
11
+ jobs:
12
+ test-macos:
13
+ name: Test on Ruby ${{ matrix.ruby }} macOS
14
+ runs-on: macos-latest
15
+ continue-on-error: ${{ matrix.experimental }}
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ ruby: [ '2.6', '2.5', '2.4' ]
20
+ experimental: [false]
21
+ include:
22
+ - ruby: '2.7'
23
+ experimental: true
24
+ steps:
25
+ - uses: actions/checkout@master
26
+ - name: Use Ruby
27
+ uses: actions/setup-ruby@v1
28
+ with:
29
+ ruby-version: ${{ matrix.ruby }}
30
+ - name: Update gems
31
+ run: |
32
+ sudo gem install bundler --force
33
+ bundle install --jobs 4 --retry 3
34
+ - name: Install Grpahviz macOS
35
+ run: brew install graphviz
36
+ - name: Run specs
37
+ run: |
38
+ bundle exec rake
@@ -0,0 +1,40 @@
1
+ name: ubuntu
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ tags:
7
+ - '*'
8
+ pull_request:
9
+ paths-ignore:
10
+ - .github/workflows/macos.yml
11
+ - .github/workflows/windows.yml
12
+
13
+ jobs:
14
+ test-linux:
15
+ name: Test on Ruby ${{ matrix.ruby }} Ubuntu
16
+ runs-on: ubuntu-latest
17
+ continue-on-error: ${{ matrix.experimental }}
18
+ strategy:
19
+ fail-fast: false
20
+ matrix:
21
+ ruby: [ '2.6', '2.5', '2.4' ]
22
+ experimental: [false]
23
+ include:
24
+ - ruby: '2.7'
25
+ experimental: true
26
+ steps:
27
+ - uses: actions/checkout@master
28
+ - name: Use Ruby
29
+ uses: actions/setup-ruby@v1
30
+ with:
31
+ ruby-version: ${{ matrix.ruby }}
32
+ - name: Update gems
33
+ run: |
34
+ gem install bundler
35
+ bundle install --jobs 4 --retry 3
36
+ - name: Install Grpahviz Ubuntu
37
+ run: sudo apt-get install graphviz
38
+ - name: Run specs
39
+ run: |
40
+ bundle exec rake
@@ -0,0 +1,51 @@
1
+ name: windows
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ paths-ignore:
8
+ - .github/workflows/macos.yml
9
+ - .github/workflows/ubuntu.yml
10
+
11
+ jobs:
12
+ test-windows:
13
+ name: Test on Ruby ${{ matrix.ruby }} Windows
14
+ runs-on: windows-latest
15
+ continue-on-error: ${{ matrix.experimental }}
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ ruby: [ '2.6', '2.5', '2.4' ]
20
+ experimental: [false]
21
+ # Does not supported yet:
22
+ # Ruby (< 2.7.dev, >= 2.3), which is required by gem 'nokogiri (~> 1.10)', is not
23
+ # available in the local ruby installation
24
+ # include:
25
+ # - ruby: '2.7'
26
+ # experimental: true
27
+ steps:
28
+ - uses: actions/checkout@master
29
+ - name: Use Ruby
30
+ uses: actions/setup-ruby@v1
31
+ with:
32
+ ruby-version: ${{ matrix.ruby }}
33
+ - name: Update gems
34
+ shell: pwsh
35
+ run: |
36
+ gem install bundler
37
+ bundle config --local path vendor/bundle
38
+ bundle install --jobs 4 --retry 3
39
+ - name: Install graphviz
40
+ uses: nick-invision/retry@v1
41
+ with:
42
+ polling_interval_seconds: 5
43
+ timeout_minutes: 5
44
+ max_attempts: 3
45
+ command: choco install --no-progress graphviz --version 2.38.0.20190211
46
+ - name: Check dot command
47
+ run: |
48
+ dot -?
49
+ - name: Run specs
50
+ run: |
51
+ bundle exec rake
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+ Gemfile.lock
data/Gemfile CHANGED
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  # Specify your gem's dependencies in lutaml-uml.gemspec
4
6
  gemspec
5
7
 
6
8
  gem "rake", "~> 12.0"
7
- gem "rspec", "~> 3.0"
@@ -0,0 +1,339 @@
1
+ = LutaML syntax
2
+
3
+ == `diagram` syntax
4
+
5
+ `diagram` is a root element for each diagram.
6
+
7
+ [source,java]
8
+ ----
9
+ diagram MyView {
10
+ import Relationship, Element
11
+ render_option typed_as_associations
12
+ file "my_view.png"
13
+ fontname "Helvetica"
14
+ }
15
+ ----
16
+
17
+ == DataTypes
18
+
19
+ Lutaml supports 3 types of data_types: `data_type`, `primitive` and `enum`. Example of data types declaration:
20
+
21
+ [source,java]
22
+ ----
23
+ diagram MyView {
24
+ enum MyEnum {}
25
+
26
+ enum AddressClassProfile {
27
+ +addressClassProfile: CharacterString
28
+ }
29
+
30
+ data_type "Banking Information" {
31
+ "art code"
32
+ "CCT Number"
33
+ }
34
+
35
+ primitive Integer
36
+
37
+ enum Profile {
38
+ imlicistAttributeProfile: CharacterString
39
+ +attributeProfile: CharacterString
40
+ -privateAttributeProfile: CharacterString
41
+ ~friendlyAttributeProfile: CharacterString
42
+ #protectedAttributeProfile: CharacterString
43
+ }
44
+ }
45
+ ----
46
+
47
+ == Associations
48
+
49
+ === Explicit declaration
50
+
51
+ Syntax:
52
+
53
+ [source,java]
54
+ ----
55
+ association name {
56
+ owned_type association|composition|aggregation|generalization|uses
57
+ member_type association|composition|aggregation|generalization|uses
58
+ owned association_name[#attribute_name] [{property_string}][cardinality]
59
+ member association_name[#attribute_name] [{property_string}][cardinality]
60
+ }
61
+ ----
62
+
63
+ where:
64
+
65
+ * `owned_type` - optional, use to define bidirectional association(association|composition|aggregation|generalization|uses)
66
+ * `member_type` - association type(association|composition|aggregation|generalization|uses)
67
+ * `owned|member` - end of association, use `\#attribute_name` to set a role name
68
+ * `property_string` - property string for attibutes associations
69
+ * `cardinality` - examples: '1..*', '*'
70
+
71
+ Example:
72
+
73
+ [source,java]
74
+ ----
75
+ class Association {
76
+ +association:
77
+ }
78
+ class Type {
79
+ +endType:
80
+ }
81
+
82
+ association AssociatingTypeAndAssociation {
83
+ type uses
84
+ from Association#+association {subsets relationship}[*]
85
+ to Type#+/endType {readOnly, subsets relatedElement} [1..*]
86
+ }
87
+ ----
88
+
89
+ === Undirected associations
90
+
91
+ The simplest way to define relationship between two classes is to use `generalize` keyword:
92
+
93
+ [source,java]
94
+ ----
95
+ class Pet {}
96
+ class Cat {
97
+ generalize Pet
98
+ }
99
+ ----
100
+
101
+ === Attribute relationship
102
+
103
+ Derived attribute `relatedElement` can have 1 to many `Element` associated with it through `union`
104
+
105
+ [source,java]
106
+ ----
107
+ class Relationship {
108
+ +/relatedElement: Element[1..*] {union}
109
+ }
110
+ class Element {}
111
+ ----
112
+
113
+ == Class' multiline "definition" property
114
+
115
+ === Definition
116
+
117
+ Full syntax:
118
+
119
+ [source,java]
120
+ ----
121
+ definition
122
+ inner text
123
+ end definition
124
+ ----
125
+
126
+
127
+ == Attributes/entries
128
+
129
+ === Definition
130
+
131
+ Full syntax:
132
+
133
+ [source,java]
134
+ ----
135
+ [visibility][/][attribute] name [:type][multiplicity][=initial value][{attribute body}]
136
+ ----
137
+
138
+ where:
139
+
140
+ * `visibility` can be equal to `-` - private, `+` - public, `#` - protected, `~` - friendly
141
+ * `attribute` - attrbute keyword
142
+ * `/` - symbolizes a derived attribute.
143
+ * `multiplicity` - Multiplicity is in square brackets (e.g. [1..*]).
144
+ * `initial value` - Default value specifies the initial value of the attribute.
145
+ * `{attribute body}` - Body of attribute, additional properties for attribute
146
+
147
+
148
+ One can use explicit or implicit syntax for attribute definition
149
+
150
+ explicit syntax:
151
+
152
+ [source,java]
153
+ ----
154
+ class A {
155
+ attribute my_attribute
156
+ }
157
+
158
+ enum A {
159
+ entry my_val2
160
+ }
161
+ ----
162
+
163
+ implicit syntax:
164
+
165
+ [source,java]
166
+ ----
167
+ class A {
168
+ my_attribute
169
+ }
170
+
171
+ enum A {
172
+ my_val2
173
+ }
174
+ ----
175
+
176
+ === Attribute visibility
177
+
178
+ Syntax for defining visibility: [+|-|#|~] [attribute] attribute_name. LutaML uses these modificators to define attribute(entry) visbility:
179
+
180
+ + => public
181
+ - => private
182
+ # => protected
183
+ ~ => package
184
+
185
+ example:
186
+
187
+ [source,java]
188
+ ----
189
+ class Figure {
190
+ // Public attribute `radius`
191
+ + radius
192
+ // private attribute `filled`
193
+ - filled
194
+ // protected attribute `length`
195
+ # length
196
+ }
197
+ ----
198
+
199
+ === Additional attribute' properties
200
+
201
+ example:
202
+
203
+ [source,java]
204
+ ----
205
+ class Figure {
206
+ + radius {
207
+ definition
208
+ Radius of the Figure
209
+ end definition
210
+ }
211
+ }
212
+ ----
213
+
214
+ == Methods
215
+
216
+ Syntax for defining methods:
217
+
218
+ [source,java]
219
+ ----
220
+ [visbility] method-name (parameter-list): return type {property-modifier}
221
+ ----
222
+
223
+ where:
224
+ - `visibility` can be equal to `-` - private, `+` - public, `#` - protected, `~` - friendly
225
+ - `parameter-list` - parameter list
226
+ - `property-modifier` - can be equal to `redefines`, `query`, `ordered`(`unordered`), `unique`(`nonunique`)
227
+
228
+ Syntax for a `parameter-list`:
229
+
230
+ [source,java]
231
+ ----
232
+ [direction] name:type [multiplicity] [=default] [{property string}]
233
+ ----
234
+
235
+ where:
236
+ - `direction` - can be equal to `in`, `out`, `inout` or `return`
237
+
238
+ == import files
239
+
240
+ Use `include` special word:
241
+
242
+ [source,java]
243
+ ----
244
+ include path/to/file
245
+ ----
246
+
247
+ == Package syntax
248
+
249
+ Namespaces
250
+ Def.-A named element is an element that can have a name and a defined visibility (public, private, protected, package):
251
+
252
+ [source,java]
253
+ ----
254
+ + => public
255
+ - => private
256
+ # => protected
257
+ ~ => package
258
+ ----
259
+
260
+ The name of the element and its visibility are optional.
261
+
262
+ [source,java]
263
+ ----
264
+ package Customers {
265
+ class Insurance {}
266
+ - class PrivateInsurance {}
267
+ # class ProtectedInsurance {}
268
+ }
269
+ ----
270
+
271
+ == Code comments
272
+
273
+ Use `//` notation for LutaML comments skipped by parser, example:
274
+
275
+ [source,java]
276
+ ----
277
+ // TODO: implement
278
+ abstract class Pet {}
279
+ ----
280
+
281
+ == Comment objects diagram
282
+
283
+ Use `\**`(one line comment) or `*| |*`(multiline comment) to create comment object for diagram entry.
284
+ If this syntax used inside class/enum/association block it will be created for owner of this block.
285
+
286
+ [source,java]
287
+ ----
288
+ ** I am a document comment
289
+
290
+ *|
291
+ This is a
292
+ multiply
293
+ lines document comment.
294
+ *|
295
+
296
+ class A
297
+ enum B {
298
+ ** one line enum comment
299
+ foo
300
+ *|
301
+ This is a
302
+ multiply
303
+ lines class comment.
304
+ *|
305
+ bar
306
+ }
307
+ ----
308
+
309
+ == Syntax comments
310
+
311
+ Use `//` to create syntax comment, chars after // will be ignored during processing
312
+
313
+ [source,java]
314
+ ----
315
+ // TODO: attributes
316
+ class A
317
+ enum B {
318
+ // Write docs
319
+ foo
320
+ bar
321
+ }
322
+ ----
323
+
324
+ == Value specification
325
+
326
+ A value specification indicates one or several values in a model. Examples for value specifications include simple, mathematical expressions, such as 4+2, and expressions with values from the object model, Integer::MAX_INT-1
327
+
328
+ [source,java]
329
+ ----
330
+ class {Class name, if any} {as ref name, optional} {
331
+ {attribute name} = {attribute value}
332
+ {attribute name}:{attribute class} = {attribute value}
333
+ }
334
+
335
+ instance :{Class name, if any} {as ref name, optional} {
336
+ {attribute name} = {attribute value}
337
+ {attribute name}:{attribute class} = {attribute value}
338
+ }
339
+ ----