archimate 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (211) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.projectile +18 -0
  4. data/.rubocop.yml +13 -0
  5. data/.travis.yml +4 -0
  6. data/.yardocs +15 -0
  7. data/Gemfile +5 -0
  8. data/Guardfile +27 -0
  9. data/LICENSE +201 -0
  10. data/README.md +94 -0
  11. data/Rakefile +29 -0
  12. data/TODOs.org +286 -0
  13. data/archimate.gemspec +51 -0
  14. data/bin/archimate +17 -0
  15. data/bin/console +13 -0
  16. data/bin/setup +8 -0
  17. data/exe/archidiff +7 -0
  18. data/exe/archidiff-summary +7 -0
  19. data/exe/archimate +7 -0
  20. data/exe/archimerge +7 -0
  21. data/exe/fmtxml +7 -0
  22. data/lib/archimate/cli/archi.rb +189 -0
  23. data/lib/archimate/cli/cleanup.rb +54 -0
  24. data/lib/archimate/cli/conflict_resolver.rb +39 -0
  25. data/lib/archimate/cli/convert.rb +39 -0
  26. data/lib/archimate/cli/diff.rb +31 -0
  27. data/lib/archimate/cli/diff_summary.rb +101 -0
  28. data/lib/archimate/cli/duper.rb +85 -0
  29. data/lib/archimate/cli/lint.rb +16 -0
  30. data/lib/archimate/cli/mapper.rb +82 -0
  31. data/lib/archimate/cli/merge.rb +49 -0
  32. data/lib/archimate/cli/merger.rb +109 -0
  33. data/lib/archimate/cli/stats.rb +42 -0
  34. data/lib/archimate/cli/svger.rb +40 -0
  35. data/lib/archimate/color.rb +53 -0
  36. data/lib/archimate/config.rb +35 -0
  37. data/lib/archimate/data_model/any_attribute.rb +13 -0
  38. data/lib/archimate/data_model/any_element.rb +15 -0
  39. data/lib/archimate/data_model/archimate_node.rb +181 -0
  40. data/lib/archimate/data_model/bounds.rb +72 -0
  41. data/lib/archimate/data_model/color.rb +47 -0
  42. data/lib/archimate/data_model/concept.rb +14 -0
  43. data/lib/archimate/data_model/concern.rb +17 -0
  44. data/lib/archimate/data_model/connection.rb +107 -0
  45. data/lib/archimate/data_model/constants.rb +82 -0
  46. data/lib/archimate/data_model/container.rb +17 -0
  47. data/lib/archimate/data_model/diagram.rb +40 -0
  48. data/lib/archimate/data_model/diffable_array.rb +213 -0
  49. data/lib/archimate/data_model/diffable_primitive.rb +83 -0
  50. data/lib/archimate/data_model/documentation.rb +14 -0
  51. data/lib/archimate/data_model/element.rb +87 -0
  52. data/lib/archimate/data_model/font.rb +54 -0
  53. data/lib/archimate/data_model/label.rb +19 -0
  54. data/lib/archimate/data_model/lang_string.rb +41 -0
  55. data/lib/archimate/data_model/location.rb +31 -0
  56. data/lib/archimate/data_model/metadata.rb +14 -0
  57. data/lib/archimate/data_model/model.rb +217 -0
  58. data/lib/archimate/data_model/modeling_note.rb +13 -0
  59. data/lib/archimate/data_model/named_referenceable.rb +14 -0
  60. data/lib/archimate/data_model/organization.rb +40 -0
  61. data/lib/archimate/data_model/property.rb +25 -0
  62. data/lib/archimate/data_model/property_definition.rb +16 -0
  63. data/lib/archimate/data_model/referenceable.rb +29 -0
  64. data/lib/archimate/data_model/relationship.rb +85 -0
  65. data/lib/archimate/data_model/schema_info.rb +18 -0
  66. data/lib/archimate/data_model/style.rb +32 -0
  67. data/lib/archimate/data_model/view.rb +12 -0
  68. data/lib/archimate/data_model/view_concept.rb +18 -0
  69. data/lib/archimate/data_model/view_node.rb +114 -0
  70. data/lib/archimate/data_model/viewpoint.rb +87 -0
  71. data/lib/archimate/data_model.rb +54 -0
  72. data/lib/archimate/diff/archimate_array_reference.rb +113 -0
  73. data/lib/archimate/diff/archimate_identified_node_reference.rb +41 -0
  74. data/lib/archimate/diff/archimate_node_attribute_reference.rb +70 -0
  75. data/lib/archimate/diff/archimate_node_reference.rb +80 -0
  76. data/lib/archimate/diff/change.rb +49 -0
  77. data/lib/archimate/diff/conflict.rb +31 -0
  78. data/lib/archimate/diff/conflicts/base_conflict.rb +53 -0
  79. data/lib/archimate/diff/conflicts/deleted_items_child_updated_conflict.rb +30 -0
  80. data/lib/archimate/diff/conflicts/deleted_items_referenced_conflict.rb +63 -0
  81. data/lib/archimate/diff/conflicts/path_conflict.rb +51 -0
  82. data/lib/archimate/diff/conflicts.rb +89 -0
  83. data/lib/archimate/diff/delete.rb +41 -0
  84. data/lib/archimate/diff/difference.rb +113 -0
  85. data/lib/archimate/diff/insert.rb +43 -0
  86. data/lib/archimate/diff/merge.rb +70 -0
  87. data/lib/archimate/diff/move.rb +51 -0
  88. data/lib/archimate/diff.rb +17 -0
  89. data/lib/archimate/export/csv_export.rb +32 -0
  90. data/lib/archimate/export/cypher.rb +171 -0
  91. data/lib/archimate/export/graph_ml.rb +131 -0
  92. data/lib/archimate/export/n_quads.rb +142 -0
  93. data/lib/archimate/file_format.rb +30 -0
  94. data/lib/archimate/file_formats/archi_file_format.rb +151 -0
  95. data/lib/archimate/file_formats/archi_file_reader.rb +252 -0
  96. data/lib/archimate/file_formats/archi_file_writer.rb +230 -0
  97. data/lib/archimate/file_formats/archimate_v2.rb +461 -0
  98. data/lib/archimate/file_formats/model_exchange_file/xml_lang_string.rb +35 -0
  99. data/lib/archimate/file_formats/model_exchange_file/xml_metadata.rb +115 -0
  100. data/lib/archimate/file_formats/model_exchange_file/xml_property_definitions.rb +28 -0
  101. data/lib/archimate/file_formats/model_exchange_file/xml_property_defs.rb +27 -0
  102. data/lib/archimate/file_formats/model_exchange_file_reader.rb +237 -0
  103. data/lib/archimate/file_formats/model_exchange_file_reader_21.rb +73 -0
  104. data/lib/archimate/file_formats/model_exchange_file_reader_30.rb +134 -0
  105. data/lib/archimate/file_formats/model_exchange_file_writer.rb +157 -0
  106. data/lib/archimate/file_formats/model_exchange_file_writer_21.rb +143 -0
  107. data/lib/archimate/file_formats/model_exchange_file_writer_30.rb +153 -0
  108. data/lib/archimate/file_formats/writer.rb +56 -0
  109. data/lib/archimate/lint/duplicate_entities.rb +121 -0
  110. data/lib/archimate/lint/linter.rb +146 -0
  111. data/lib/archimate/logging.rb +55 -0
  112. data/lib/archimate/maybe_io.rb +34 -0
  113. data/lib/archimate/progress_indicator.rb +29 -0
  114. data/lib/archimate/svg/archimate.css +232 -0
  115. data/lib/archimate/svg/child.rb +29 -0
  116. data/lib/archimate/svg/connection.rb +184 -0
  117. data/lib/archimate/svg/css_style.rb +31 -0
  118. data/lib/archimate/svg/diagram.rb +87 -0
  119. data/lib/archimate/svg/entity/and_junction.rb +14 -0
  120. data/lib/archimate/svg/entity/application_collaboration.rb +14 -0
  121. data/lib/archimate/svg/entity/application_component.rb +41 -0
  122. data/lib/archimate/svg/entity/application_event.rb +13 -0
  123. data/lib/archimate/svg/entity/application_function.rb +13 -0
  124. data/lib/archimate/svg/entity/application_interaction.rb +13 -0
  125. data/lib/archimate/svg/entity/application_interface.rb +13 -0
  126. data/lib/archimate/svg/entity/application_process.rb +13 -0
  127. data/lib/archimate/svg/entity/application_service.rb +13 -0
  128. data/lib/archimate/svg/entity/artifact.rb +39 -0
  129. data/lib/archimate/svg/entity/assessment.rb +14 -0
  130. data/lib/archimate/svg/entity/base_entity.rb +128 -0
  131. data/lib/archimate/svg/entity/business_actor.rb +14 -0
  132. data/lib/archimate/svg/entity/business_collaboration.rb +14 -0
  133. data/lib/archimate/svg/entity/business_event.rb +10 -0
  134. data/lib/archimate/svg/entity/business_function.rb +13 -0
  135. data/lib/archimate/svg/entity/business_interaction.rb +13 -0
  136. data/lib/archimate/svg/entity/business_interface.rb +13 -0
  137. data/lib/archimate/svg/entity/business_object.rb +13 -0
  138. data/lib/archimate/svg/entity/business_process.rb +13 -0
  139. data/lib/archimate/svg/entity/business_role.rb +14 -0
  140. data/lib/archimate/svg/entity/business_service.rb +13 -0
  141. data/lib/archimate/svg/entity/capability.rb +14 -0
  142. data/lib/archimate/svg/entity/communication_network.rb +14 -0
  143. data/lib/archimate/svg/entity/communication_path.rb +14 -0
  144. data/lib/archimate/svg/entity/constraint.rb +15 -0
  145. data/lib/archimate/svg/entity/contract.rb +13 -0
  146. data/lib/archimate/svg/entity/course_of_action.rb +14 -0
  147. data/lib/archimate/svg/entity/data_entity.rb +29 -0
  148. data/lib/archimate/svg/entity/data_object.rb +13 -0
  149. data/lib/archimate/svg/entity/deliverable.rb +13 -0
  150. data/lib/archimate/svg/entity/device.rb +48 -0
  151. data/lib/archimate/svg/entity/diagram_model_reference.rb +22 -0
  152. data/lib/archimate/svg/entity/diagram_object.rb +30 -0
  153. data/lib/archimate/svg/entity/distribution_network.rb +14 -0
  154. data/lib/archimate/svg/entity/driver.rb +14 -0
  155. data/lib/archimate/svg/entity/equipment.rb +16 -0
  156. data/lib/archimate/svg/entity/event_entity.rb +36 -0
  157. data/lib/archimate/svg/entity/facility.rb +16 -0
  158. data/lib/archimate/svg/entity/function_entity.rb +14 -0
  159. data/lib/archimate/svg/entity/gap.rb +21 -0
  160. data/lib/archimate/svg/entity/goal.rb +14 -0
  161. data/lib/archimate/svg/entity/group.rb +24 -0
  162. data/lib/archimate/svg/entity/implementation_event.rb +13 -0
  163. data/lib/archimate/svg/entity/infrastructure_function.rb +13 -0
  164. data/lib/archimate/svg/entity/infrastructure_interface.rb +13 -0
  165. data/lib/archimate/svg/entity/infrastructure_service.rb +13 -0
  166. data/lib/archimate/svg/entity/interaction_entity.rb +14 -0
  167. data/lib/archimate/svg/entity/interface_entity.rb +35 -0
  168. data/lib/archimate/svg/entity/junction.rb +19 -0
  169. data/lib/archimate/svg/entity/location.rb +14 -0
  170. data/lib/archimate/svg/entity/material.rb +14 -0
  171. data/lib/archimate/svg/entity/meaning.rb +40 -0
  172. data/lib/archimate/svg/entity/motivation_entity.rb +40 -0
  173. data/lib/archimate/svg/entity/network.rb +14 -0
  174. data/lib/archimate/svg/entity/node.rb +33 -0
  175. data/lib/archimate/svg/entity/node_shape.rb +64 -0
  176. data/lib/archimate/svg/entity/note.rb +30 -0
  177. data/lib/archimate/svg/entity/or_junction.rb +14 -0
  178. data/lib/archimate/svg/entity/outcome.rb +15 -0
  179. data/lib/archimate/svg/entity/path.rb +14 -0
  180. data/lib/archimate/svg/entity/plateau.rb +15 -0
  181. data/lib/archimate/svg/entity/principle.rb +15 -0
  182. data/lib/archimate/svg/entity/process_entity.rb +63 -0
  183. data/lib/archimate/svg/entity/product.rb +20 -0
  184. data/lib/archimate/svg/entity/rect.rb +13 -0
  185. data/lib/archimate/svg/entity/rect_entity.rb +25 -0
  186. data/lib/archimate/svg/entity/representation.rb +31 -0
  187. data/lib/archimate/svg/entity/requirement.rb +15 -0
  188. data/lib/archimate/svg/entity/resource.rb +14 -0
  189. data/lib/archimate/svg/entity/rounded_rect_entity.rb +23 -0
  190. data/lib/archimate/svg/entity/service_entity.rb +51 -0
  191. data/lib/archimate/svg/entity/sketch_model_sticky.rb +14 -0
  192. data/lib/archimate/svg/entity/stakeholder.rb +15 -0
  193. data/lib/archimate/svg/entity/system_software.rb +14 -0
  194. data/lib/archimate/svg/entity/technology_collaboration.rb +14 -0
  195. data/lib/archimate/svg/entity/technology_event.rb +13 -0
  196. data/lib/archimate/svg/entity/technology_function.rb +13 -0
  197. data/lib/archimate/svg/entity/technology_interaction.rb +13 -0
  198. data/lib/archimate/svg/entity/technology_interface.rb +13 -0
  199. data/lib/archimate/svg/entity/technology_process.rb +13 -0
  200. data/lib/archimate/svg/entity/technology_service.rb +13 -0
  201. data/lib/archimate/svg/entity/value.rb +27 -0
  202. data/lib/archimate/svg/entity/work_package.rb +14 -0
  203. data/lib/archimate/svg/entity.rb +93 -0
  204. data/lib/archimate/svg/entity_factory.rb +17 -0
  205. data/lib/archimate/svg/extents.rb +27 -0
  206. data/lib/archimate/svg/point.rb +7 -0
  207. data/lib/archimate/svg/svg_template.rb +27 -0
  208. data/lib/archimate/svg/svg_template.svg.erb +169 -0
  209. data/lib/archimate/version.rb +4 -0
  210. data/lib/archimate.rb +114 -0
  211. metadata +623 -0
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Archimate
4
+ module DataModel
5
+ # Basic Viewpoints
6
+ # Category:Composition Viewpoints that defines internal compositions and aggregations of elements.
7
+ COMPOSITION_VIEWPOINTS = [
8
+ "Organization",
9
+ "Application Platform",
10
+ "Information Structure",
11
+ "Technology",
12
+ "Layered",
13
+ "Physical"
14
+ ].freeze
15
+
16
+ # Category:Support Viewpoints where you are looking at elements that are supported by other elements. Typically from one layer and upwards to an above layer.
17
+ SUPPORT_VIEWPOINTS = [
18
+ "Product",
19
+ "Application Usage",
20
+ "Technology Usage"
21
+ ].freeze
22
+
23
+ # Category:Cooperation Towards peer elements which cooperate with each other. Typically across aspects.
24
+ COOPERATION_VIEWPOINTS = [
25
+ "Business Process Cooperation",
26
+ "Application Cooperation"
27
+ ].freeze
28
+
29
+ # Category:Realization Viewpoints where you are looking at elements that realize other elements. Typically from one layer and downwards to a below layer.
30
+ REALIZATION_VIEWPOINTS = [
31
+ "Service Realization",
32
+ "Implementation and Deployment",
33
+ "Goal Realization",
34
+ "Goal Contribution",
35
+ "Principles",
36
+ "Requirements Realization",
37
+ "Motivation"
38
+ ].freeze
39
+
40
+ # Strategy Viewpoints
41
+ STRATEGY_VIEWPOINTS = [
42
+ "Strategy",
43
+ "Capability Map",
44
+ "Outcome Realization",
45
+ "Resource Map"
46
+ ].freeze
47
+
48
+ # Implementation and Migration Viewpoints
49
+ IMPLEMENTATION_AND_MIGRATION_VIEWPOINTS = [
50
+ "Project",
51
+ "Migration",
52
+ "Implementation and Migration"
53
+ ].freeze
54
+
55
+ # Other Viewpoints
56
+ Other_Viewpoints = %w[Stakeholder].freeze
57
+
58
+ VIEWPOINTS_ENUM = [].concat(
59
+ [COMPOSITION_VIEWPOINTS, SUPPORT_VIEWPOINTS, COOPERATION_VIEWPOINTS,
60
+ REALIZATION_VIEWPOINTS, STRATEGY_VIEWPOINTS,
61
+ IMPLEMENTATION_AND_MIGRATION_VIEWPOINTS].flatten
62
+ )
63
+
64
+ ViewpointType = Strict::String.enum(*VIEWPOINTS_ENUM).optional
65
+
66
+ ViewpointContentEnum = Strict::String.enum(%w[Details Coherence Overview])
67
+ ViewpointContent = Strict::Array.member(ViewpointContentEnum).default([])
68
+
69
+ ViewpointPurposeEnum = Strict::String.enum(%w[Designing Deciding Informing])
70
+ ViewpointPurpose = Strict::Array.member(ViewpointPurposeEnum).default([])
71
+
72
+ class Viewpoint < NamedReferenceable
73
+ using DataModel::DiffableArray
74
+ using DataModel::DiffablePrimitive
75
+
76
+ attribute :concern, ConcernList
77
+ attribute :viewpointPurpose, ViewpointPurpose.optional
78
+ attribute :viewpointContent, ViewpointContent.optional
79
+ attribute :allowedElementTypes, AllowedElementTypes
80
+ attribute :allowedRelationshipTypes, AllowedRelationshipTypes
81
+ attribute :modelingNotes, Strict::Array.member(ModelingNote).default([])
82
+ end
83
+
84
+ Dry::Types.register_class(Viewpoint)
85
+ ViewpointList = Strict::Array.member(Viewpoint).default([])
86
+ end
87
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Archimate
4
+ module DataModel
5
+ include Dry::Types.module
6
+
7
+ Coercible = Archimate::DataModel::Coercible
8
+ Strict = Archimate::DataModel::Strict
9
+
10
+ # Identifiers in the ArchiMate exchange format standard are of
11
+ Identifier = Strict::String # .constrained(format: /[[[:alpha:]]_][\w\-\.]*/)
12
+
13
+ # An enumeration of data types.
14
+ DataType = Strict::String.default("string").enum("string", "boolean", "currency", "date", "time", "number")
15
+
16
+ # Enumeration of Influence Strength types. These are suggestions.
17
+ InfluenceStrengthEnum = Strict::String.enum(%w[+ ++ - -- 0 1 2 3 4 5 6 7 8 9 10])
18
+
19
+ require 'archimate/data_model/diffable_primitive'
20
+ require 'archimate/data_model/diffable_array'
21
+ require 'archimate/data_model/constants'
22
+ require 'archimate/data_model/archimate_node'
23
+ require 'archimate/data_model/any_attribute'
24
+ require 'archimate/data_model/any_element'
25
+ require 'archimate/data_model/lang_string'
26
+ require 'archimate/data_model/schema_info'
27
+ require 'archimate/data_model/metadata'
28
+ require 'archimate/data_model/color'
29
+ require 'archimate/data_model/font'
30
+ require 'archimate/data_model/style'
31
+ require 'archimate/data_model/bounds'
32
+ require 'archimate/data_model/documentation'
33
+ require 'archimate/data_model/modeling_note'
34
+ require 'archimate/data_model/referenceable'
35
+ require 'archimate/data_model/named_referenceable'
36
+ require 'archimate/data_model/property_definition'
37
+ require 'archimate/data_model/property'
38
+ require 'archimate/data_model/concept'
39
+ require 'archimate/data_model/organization'
40
+ require 'archimate/data_model/element'
41
+ require 'archimate/data_model/relationship'
42
+ require 'archimate/data_model/concern'
43
+ require 'archimate/data_model/viewpoint'
44
+ require 'archimate/data_model/view'
45
+ require 'archimate/data_model/view_concept'
46
+ require 'archimate/data_model/location'
47
+ require 'archimate/data_model/connection'
48
+ require 'archimate/data_model/view_node'
49
+ require 'archimate/data_model/container'
50
+ require 'archimate/data_model/diagram'
51
+ require 'archimate/data_model/label'
52
+ require 'archimate/data_model/model'
53
+ end
54
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Archimate
4
+ module Diff
5
+ class ArchimateArrayReference < ArchimateNodeReference
6
+ using DataModel::DiffableArray
7
+ using DataModel::DiffablePrimitive
8
+
9
+ attr_reader :array_index
10
+
11
+ def initialize(array, array_index)
12
+ unless array.is_a?(Array)
13
+ raise(
14
+ TypeError,
15
+ "array argument must be an Array, was #{array.class}"
16
+ )
17
+ end
18
+ unless array_index.is_a?(Integer)
19
+ raise(
20
+ TypeError,
21
+ "array_index argument must be a Integer, was #{array_index.class} #{array_index.inspect}"
22
+ )
23
+ end
24
+ unless array_index >= 0 && array_index < array.size
25
+ raise(
26
+ ArgumentError,
27
+ "array_index argument a valid index for array #{array_index.inspect}"
28
+ )
29
+ end
30
+ super(array)
31
+ @array_index = array_index
32
+ end
33
+
34
+ def value
35
+ archimate_node[array_index]
36
+ end
37
+
38
+ def to_s
39
+ value.to_s
40
+ end
41
+
42
+ def lookup_in_model(model)
43
+ result = lookup_parent_in_model(model)
44
+ raise TypeError, "result was #{result.class} expected Array" unless result.is_a?(Array)
45
+ result[array_index]
46
+ end
47
+
48
+ def path(options = {})
49
+ @array_ref_path ||= [
50
+ super,
51
+ case value
52
+ when DataModel::Referenceable
53
+ value.id
54
+ else
55
+ array_index
56
+ end
57
+ ].map(&:to_s).reject(&:empty?).join("/")
58
+ end
59
+
60
+ # For inserts - we can't be sure of what is available (without an expensive sort)
61
+ # So lookup the first previous value that exists in to_model and insert it after that
62
+ # value instead of a fixed index.
63
+ def find_insert_index_in_ary(ary)
64
+ return -1 if array_index.zero?
65
+ my_idx = (array_index - 1).downto(0).find(-1) do |idx|
66
+ ary.smart_include?(archimate_node[idx])
67
+ end
68
+ ary.smart_find(archimate_node[my_idx])
69
+ end
70
+
71
+ def insert(to_model)
72
+ ary_in_model = lookup_parent_in_model(to_model)
73
+ insert_idx = find_insert_index_in_ary(ary_in_model) + 1
74
+ ary_in_model.insert(insert_idx, value)
75
+ end
76
+
77
+ def delete(to_model)
78
+ ary_in_model = lookup_parent_in_model(to_model)
79
+ if ary_in_model.nil?
80
+ $stderr.puts "lookup parent in model failed for path #{path}"
81
+ return nil
82
+ end
83
+ idx = ary_in_model.smart_find(value)
84
+ if idx
85
+ ary_in_model.delete_at(idx)
86
+ else
87
+ $stderr.puts "Couldn't find item #{value.inspect} in path #{path} to delete in to_model"
88
+ end
89
+ end
90
+
91
+ def change(to_model, from_value)
92
+ ary_in_model = lookup_parent_in_model(to_model)
93
+ idx = ary_in_model.smart_find(from_value)
94
+ if idx.nil?
95
+ $stderr.puts "Couldn't find value #{from_value.inspect} in path #{path} to change in to_model, adding to end of list"
96
+ idx = ary_in_model.size
97
+ end
98
+ ary_in_model[idx] = value
99
+ end
100
+
101
+ def move(to_model, _from_ref)
102
+ ary_in_model = lookup_parent_in_model(to_model)
103
+ insert_idx = parent.previous_item_index(ary_in_model, value) + 1
104
+ current_idx = ary_in_model.smart_find(value)
105
+ deleted_value = ary_in_model.delete_at(current_idx)
106
+ ary_in_model.insert(
107
+ insert_idx,
108
+ deleted_value
109
+ )
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Archimate
4
+ module Diff
5
+ class ArchimateReferenceableReference < ArchimateNodeReference
6
+ using DataModel::DiffableArray
7
+
8
+ def initialize(archimate_node)
9
+ unless archimate_node.is_a?(DataModel::Referenceable)
10
+ raise(
11
+ TypeError,
12
+ "archimate_node is a #{archimate_node.class}, Referenceable was expected"
13
+ )
14
+ end
15
+ super
16
+ end
17
+
18
+ def lookup_in_model(model)
19
+ raise TypeError unless model.is_a?(DataModel::Model)
20
+ # There can be only one Model so return the model argument if
21
+ # this node reference is a Model. This escape is required in case
22
+ # the Model this is being applied to has a different id than the
23
+ # model of the attribute this reference refers to.
24
+ return model if archimate_node.is_a?(DataModel::Model)
25
+ model.lookup(archimate_node.id)
26
+ end
27
+
28
+ def to_s
29
+ archimate_node.to_s
30
+ end
31
+
32
+ def value
33
+ archimate_node
34
+ end
35
+
36
+ def parent
37
+ archimate_node.parent
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Archimate
4
+ module Diff
5
+ class ArchimateNodeAttributeReference < ArchimateNodeReference
6
+ attr_reader :attribute
7
+
8
+ def initialize(archimate_node, attribute)
9
+ unless archimate_node.is_a?(DataModel::ArchimateNode)
10
+ raise(
11
+ TypeError,
12
+ "archimate_node must be an ArchimateNode, was #{archimate_node.class}"
13
+ )
14
+ end
15
+ unless attribute.is_a?(Symbol)
16
+ raise(
17
+ TypeError,
18
+ "Node #{archimate_node.class} attribute must be a sym, was a #{attribute.class} value #{attribute.inspect}"
19
+ )
20
+ end
21
+ unless archimate_node.class.schema.keys.include?(attribute)
22
+ raise(
23
+ ArgumentError,
24
+ "Attribute #{attribute} invalid for class #{archimate_node.class}"
25
+ )
26
+ end
27
+ super(archimate_node)
28
+ @attribute = attribute
29
+ end
30
+
31
+ def ==(other)
32
+ super && attribute == other.attribute
33
+ end
34
+
35
+ def lookup_in_model(model)
36
+ recurse_lookup_in_model(archimate_node, model)[attribute]
37
+ end
38
+
39
+ def to_s
40
+ attribute.to_s
41
+ end
42
+
43
+ def value
44
+ archimate_node[attribute]
45
+ end
46
+
47
+ def path(options = {})
48
+ @node_attribute_ref_path ||= [
49
+ super, @attribute
50
+ ].map(&:to_s).reject(&:empty?).join("/")
51
+ end
52
+
53
+ def insert(to_model)
54
+ lookup_parent_in_model(to_model).set(attribute, value)
55
+ end
56
+
57
+ def delete(to_model)
58
+ lookup_parent_in_model(to_model).delete(attribute)
59
+ end
60
+
61
+ def change(to_model, _from_value)
62
+ lookup_parent_in_model(to_model).set(attribute, value)
63
+ end
64
+
65
+ def move(_to_model)
66
+ raise "Move is not valid for ArchimateNodes"
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Archimate
4
+ module Diff
5
+ class ArchimateNodeReference
6
+ using DataModel::DiffablePrimitive
7
+ using DataModel::DiffableArray
8
+
9
+ attr_reader :archimate_node
10
+
11
+ # There should be only a few things that are valid here:
12
+ # 1. An archimate node and a attribute name sym
13
+ # 2. An array and index
14
+ # Produces a NodeReference instance for the given parameters
15
+ def self.for_node(node, child_node)
16
+ case node
17
+ when DataModel::ArchimateNode
18
+ ArchimateNodeAttributeReference.new(node, child_node)
19
+ when Array
20
+ ArchimateArrayReference.new(node, child_node)
21
+ else
22
+ raise TypeError, "Node references need to be either an ArchimateNode or an Array"
23
+ end
24
+ end
25
+
26
+ def initialize(archimate_node)
27
+ unless archimate_node.is_a?(DataModel::ArchimateNode) || archimate_node.is_a?(Array)
28
+ raise(
29
+ TypeError,
30
+ "archimate_node must be an ArchimateNode or Array, was #{archimate_node.class}"
31
+ )
32
+ end
33
+ raise "new WTF? parent at path #{archimate_node.path} is a #{archimate_node.class} but isn't assigned a model" if archimate_node.in_model.nil? && !archimate_node.is_a?(DataModel::Model)
34
+ @archimate_node = archimate_node
35
+ end
36
+
37
+ def ==(other)
38
+ other.is_a?(self.class) &&
39
+ value == other.value
40
+ end
41
+
42
+ def to_s
43
+ value.to_s
44
+ end
45
+
46
+ def lookup_in_model(model)
47
+ recurse_lookup_in_model(archimate_node, model)
48
+ end
49
+
50
+ def recurse_lookup_in_model(node, model)
51
+ return nil if node.nil?
52
+ raise TypeError, "node argument must be ArchimateNode or Array, was a #{node.class}" unless node.is_a?(Array) || node.is_a?(DataModel::ArchimateNode)
53
+ raise TypeError, "model argument must be a Model, was a #{model.class}" unless model.is_a?(DataModel::Model)
54
+ if node.is_a?(DataModel::Model)
55
+ return model
56
+ elsif node.is_a?(DataModel::Referenceable)
57
+ return model.lookup(node.id)
58
+ else
59
+ node_parent_in_model = recurse_lookup_in_model(node.parent, model)
60
+ node_parent_in_model[node.parent_attribute_name] unless node_parent_in_model.nil?
61
+ end
62
+ end
63
+
64
+ def lookup_parent_in_model(model)
65
+ raise "WTF? parent at path #{path} is a #{parent.class} but isn't assigned a model" if parent.in_model.nil? && !parent.is_a?(DataModel::Model)
66
+ result = recurse_lookup_in_model(parent, model)
67
+ $stderr.puts "Unable to lookup parent with path #{path}" if result.nil?
68
+ result
69
+ end
70
+
71
+ def parent
72
+ archimate_node
73
+ end
74
+
75
+ def path(options = {})
76
+ @node_ref_path ||= archimate_node.path(options)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ class Change < Difference
5
+ using DataModel::DiffableArray
6
+
7
+ # Create a new Change difference
8
+ #
9
+ # @param target [Archimate::Diff::ArchimateNodeReference] reference to
10
+ # ArchimateNode that was changed
11
+ # @param changed_from [Archimate::Diff::ArchimateNodeReference] Element
12
+ # that was changed
13
+ def initialize(target, changed_from)
14
+ super(target, changed_from)
15
+ end
16
+
17
+ def to_s
18
+ # Note - the explicit to_s is required to access the DiffableArray
19
+ # implementation if the parent is an Array.
20
+ "#{diff_type} #{changed_from.parent&.to_s} #{Color.color(target.to_s, :change)} changed to #{target.value}"
21
+ end
22
+
23
+ def apply(to_model)
24
+ unless to_model.is_a?(DataModel::Model)
25
+ throw(
26
+ TypeError,
27
+ "Expected a Archimate::DataModel::Model, was a #{to_model.class}"
28
+ )
29
+ end
30
+ target.change(to_model, changed_from.value)
31
+ to_model
32
+ end
33
+
34
+ def change?
35
+ true
36
+ end
37
+
38
+ def kind
39
+ "Change"
40
+ end
41
+
42
+ private
43
+
44
+ def diff_type
45
+ Color.color('CHANGE:', :change)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ class Conflict
5
+ attr_reader :base_local_diffs
6
+ attr_reader :base_remote_diffs
7
+ attr_reader :reason
8
+ attr_reader :diffs
9
+
10
+ def initialize(base_local_diffs, base_remote_diffs, reason)
11
+ @base_local_diffs = Array(base_local_diffs)
12
+ @base_remote_diffs = Array(base_remote_diffs)
13
+ @diffs = @base_local_diffs + @base_remote_diffs
14
+ @reason = reason
15
+ end
16
+
17
+ def to_s
18
+ "#{Color.color('CONFLICT:', [:black, :on_red])} #{reason}\n" \
19
+ "\tBase->Local Diff(s):\n\t\t#{base_local_diffs.map(&:to_s).join("\n\t\t")}" \
20
+ "\n\tBase->Remote Diffs(s):\n\t\t#{base_remote_diffs.map(&:to_s).join("\n\t\t")}"
21
+ end
22
+
23
+ def ==(other)
24
+ other.is_a?(self.class) &&
25
+ base_local_diffs == other.base_local_diffs &&
26
+ base_remote_diffs == other.base_remote_diffs &&
27
+ reason == other.reason
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ class Conflicts
5
+ # BaseConflict
6
+ # @abstract
7
+ class BaseConflict
8
+ def initialize(base_local_diffs, base_remote_diffs)
9
+ @base_local_diffs = base_local_diffs
10
+ @base_remote_diffs = base_remote_diffs
11
+ @associative = false
12
+ @diff_iterations = nil
13
+ end
14
+
15
+ def filter1
16
+ ->(_diff) { true }
17
+ end
18
+
19
+ def filter2
20
+ ->(_diff) { true }
21
+ end
22
+
23
+ def conflicts
24
+ progressbar = ProgressIndicator.new(total: diff_iterations.size, title: "Analyzing Conflicts")
25
+ diff_iterations.each_with_object([]) do |(md1, md2), conflicts|
26
+ progressbar.increment
27
+ conflicts.concat(
28
+ md1.map { |diff1| [diff1, md2.select(&method(:diff_conflicts).curry[diff1])] }
29
+ .reject { |_diff1, diff2| diff2.empty? }
30
+ .map { |diff1, diff2_ary| Conflict.new(diff1, diff2_ary, describe) }
31
+ )
32
+ end
33
+ ensure
34
+ progressbar.finish
35
+ end
36
+
37
+ def diff_combinations
38
+ combos = [@base_local_diffs, @base_remote_diffs]
39
+ @associative ? [combos] : combos.permutation(2)
40
+ end
41
+
42
+ # By default our conflict tests are not associative to we need to run
43
+ # [local, remote] and [remote, local] through the tests.
44
+ def diff_iterations
45
+ @diff_iterations ||=
46
+ diff_combinations.map do |local_diffs, remote_diffs|
47
+ [local_diffs.select(&filter1), remote_diffs.select(&filter2)]
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ class Conflicts
5
+ class DeletedItemsChildUpdatedConflict < BaseConflict
6
+ def describe
7
+ "Checking for Deleted items in one change set have children that are inserted or changed in the other change set"
8
+ end
9
+
10
+ def filter1
11
+ ->(diff) { diff.delete? }
12
+ end
13
+
14
+ def filter2
15
+ ->(diff) { !diff.delete? }
16
+ end
17
+
18
+ # TODO: This is simple, but might be slow.
19
+ def diff_conflicts(diff1, diff2)
20
+ da1 = diff1.path.split("/")
21
+ da2 = diff2.path.split("/")
22
+
23
+ cmp_size = [da1, da2].map(&:size).min - 1
24
+ return false if da2.size == cmp_size + 1
25
+ da1[0..cmp_size] == da2[0..cmp_size]
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ class Conflicts
5
+ # DeletedItemsReferencedConflict
6
+ #
7
+ # This sort of conflict occurs when one change set has deleted an element
8
+ # that is referenced by id in the other change set.
9
+ #
10
+ # For example:
11
+ #
12
+ # In the local change set, an element with id "abc123" is deleted.
13
+ # In the remote change set, a child is inserted into a diagram with
14
+ # archimate_element = "abc123". These two changes are in conflict.
15
+ class DeletedItemsReferencedConflict < BaseConflict
16
+ using DataModel::DiffableArray
17
+ using DataModel::DiffablePrimitive
18
+
19
+ def describe
20
+ "Checking for Deleted items in one change set are referenced in the other change set"
21
+ end
22
+
23
+ # Filters a changeset to potentially conflicting diffs (making the set
24
+ # of combinations to check smaller)
25
+ #
26
+ # @return [lambda] a filter to limit diffs to Delete type
27
+ def filter1
28
+ ->(diff) { diff.delete? && diff.target.value.is_a?(DataModel::Referenceable) }
29
+ end
30
+
31
+ # Filters a changeset to potentially conflicting diffs (making the set
32
+ # of combinations to check smaller)
33
+ #
34
+ # @return [lambda] a filter to limit diffs to other
35
+ # than Delete type
36
+ def filter2
37
+ ->(diff) { !diff.delete? }
38
+ end
39
+
40
+ # Determine the set of conflicts between the given diffs
41
+ # def conflicts
42
+ # progressbar = ProgressIndicator.new(total: diff_iterations.size)
43
+ # diff_iterations.each_with_object([]) do |(md1, md2), a|
44
+ # progressbar.increment
45
+ # a.concat(
46
+ # md1.map { |diff1| [diff1, md2.select(&method(:diff_conflicts).curry[diff1])] }
47
+ # .reject { |_diff1, diff2| diff2.empty? }
48
+ # .map { |diff1, diff2_ary| Conflict.new(diff1, diff2_ary, describe) }
49
+ # )
50
+ # end
51
+ # ensure
52
+ # progressbar&.finish
53
+ # end
54
+
55
+ # TODO: This is simple, but might be slow. If it is, then override
56
+ # the conflicts method to prevent calculating referenced_identified_nodes methods
57
+ def diff_conflicts(diff1, diff2)
58
+ diff2.target.value.referenced_identified_nodes.include?(diff1.target.value.id)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end