archimate 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,51 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ class Conflicts
5
+ class PathConflict < BaseConflict
6
+ def initialize(base_local_diffs, base_remote_diffs)
7
+ super
8
+ @associative = true
9
+ end
10
+
11
+ def describe
12
+ "Checking for Differences in one change set that conflict with changes in other change set at the same path"
13
+ end
14
+
15
+ def diff_conflicts(diff1, diff2)
16
+ same_path_but_diff(diff1, diff2) && in_conflict?(diff1, diff2)
17
+ end
18
+
19
+ private
20
+
21
+ def same_path_but_diff(a, b)
22
+ a.path == b.path && a != b
23
+ end
24
+
25
+ # I'm in conflict if:
26
+ # 1. ld and rd are both changes to the same path (but not the same change)
27
+ # 2. one is a change, the other a delete and changed_from is the same
28
+ # 3. both are inserts of the different identifyable nodes with the same id
29
+ #
30
+ # If I'm not an identifyable node and my parent is an array, then two inserts are ok
31
+ def in_conflict?(local_diff, remote_diff)
32
+ return true if
33
+ local_diff.target.parent.is_a?(Array) &&
34
+ local_diff.target.value.is_a?(DataModel::Referenceable) &&
35
+ local_diff.target.value.id == remote_diff.target.value.id &&
36
+ local_diff != remote_diff
37
+
38
+ case [local_diff, remote_diff].map { |d| d.class.name.split('::').last }.sort
39
+ when %w(Change Change)
40
+ local_diff.changed_from.value == remote_diff.changed_from.value &&
41
+ local_diff.target.value != remote_diff.target.value
42
+ when %w(Change Delete)
43
+ true
44
+ else
45
+ false
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'parallel'
5
+ require 'archimate/diff/conflicts/base_conflict'
6
+ require 'archimate/diff/conflicts/deleted_items_child_updated_conflict'
7
+ require 'archimate/diff/conflicts/deleted_items_referenced_conflict'
8
+ require 'archimate/diff/conflicts/path_conflict'
9
+
10
+ module Archimate
11
+ module Diff
12
+ class Conflicts
13
+ extend Forwardable
14
+
15
+ attr_reader :base_local_diffs
16
+ attr_reader :base_remote_diffs
17
+
18
+ def_delegator :@conflicts, :empty?
19
+ def_delegator :@conflicts, :size
20
+ def_delegator :@conflicts, :first
21
+ def_delegator :@conflicts, :map
22
+ def_delegator :@conflicts, :each
23
+
24
+ include Archimate::Logging
25
+
26
+ def initialize(base_local_diffs, base_remote_diffs)
27
+ @base_local_diffs = base_local_diffs
28
+ @base_remote_diffs = base_remote_diffs
29
+ @conflict_finders = [PathConflict, DeletedItemsChildUpdatedConflict, DeletedItemsReferencedConflict]
30
+ @conflicts = nil
31
+ @conflicting_diffs = nil
32
+ @unconflicted_diffs = nil
33
+ # TODO: consider making this an argument
34
+ @conflict_resolver = Cli::ConflictResolver.new
35
+ end
36
+
37
+ # TODO: refactor this method elsewhere
38
+ # resolve iterates through the set of conflicting diffs asking the user
39
+ # (if running interactively) and return the set of diffs that can be applied.
40
+ #
41
+ # To keep diffs reasonably human readable in logs, the local diffs should
42
+ # be applied first followed by the remote diffs.
43
+ def resolve
44
+ debug do
45
+ <<~MSG
46
+ Filtering out #{conflicts.size} conflicts from #{base_local_diffs.size + base_remote_diffs.size} diffs
47
+ Remaining diffs #{unconflicted_diffs.size}
48
+ MSG
49
+ end
50
+
51
+ conflicts.each_with_object(unconflicted_diffs) do |conflict, diffs|
52
+ # TODO: this will result in diffs being out of order from their
53
+ # original order. diffs should be flagged as conflicted and
54
+ # this method should instead remove the conflicted flag.
55
+ diffs.concat(@conflict_resolver.resolve(conflict))
56
+ # TODO: if the conflict is resolved, it should be removed from the
57
+ # @conflicts array.
58
+ end
59
+ end
60
+
61
+ def conflicts
62
+ @conflicts ||= find_conflicts
63
+ end
64
+
65
+ def conflicting_diffs
66
+ @conflicting_diffs ||= conflicts.map(&:diffs).flatten
67
+ end
68
+
69
+ def unconflicted_diffs
70
+ @unconflicted_diffs ||=
71
+ (base_local_diffs + base_remote_diffs) - conflicting_diffs
72
+ end
73
+
74
+ def to_s
75
+ "Conflicts:\n\n#{conflicts.map(&:to_s).join("\n\n")}\n"
76
+ end
77
+
78
+ private
79
+
80
+ def find_conflicts
81
+ # TODO: Running this in parallel breaks currently.
82
+ # @conflicts = Parallel.map(@conflict_finders, in_processes: 3) { |cf_class|
83
+ @conflicts = @conflict_finders.map { |cf_class|
84
+ cf_class.new(base_local_diffs, base_remote_diffs).conflicts
85
+ }.flatten
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ class Delete < Difference
5
+ using DataModel::DiffablePrimitive
6
+ using DataModel::DiffableArray
7
+
8
+ # Create a new Delete difference
9
+ #
10
+ # @param target [ArchimateNodeReference] Element that was deleted
11
+ def initialize(target)
12
+ super
13
+ end
14
+
15
+ def to_s
16
+ # Note - the explicit to_s is required to access the DiffableArray
17
+ # implementation if the parent is an Array.
18
+ "#{diff_type} #{target} from #{target.parent.to_s}"
19
+ end
20
+
21
+ def apply(el)
22
+ target.delete(el)
23
+ el
24
+ end
25
+
26
+ def delete?
27
+ true
28
+ end
29
+
30
+ def kind
31
+ "Delete"
32
+ end
33
+
34
+ private
35
+
36
+ def diff_type
37
+ Color.color('DELETE:', :delete)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module Archimate
6
+ module Diff
7
+ class Difference
8
+ using DataModel::DiffableArray
9
+ using DataModel::DiffablePrimitive
10
+ extend Forwardable
11
+
12
+ ARRAY_RE = Regexp.compile(/\[(\d+)\]/)
13
+ PATH_ROOT_SORT_ORDER = %w[elements relationships diagrams organizations].freeze
14
+
15
+ # delete: something responds to parent, child attribute (which is thing deleted - and could be sym for archimate nodes or index for array), value
16
+ # insert: something responds to parent, child attribute (or index), value, after value (to help with inserts)
17
+ # change: something responds to parent, child attribute (or index), value, changed from value
18
+ # move: something responds to parent, child index, value, after value) move after a particular value
19
+ attr_reader :target
20
+ attr_reader :changed_from
21
+
22
+ def_delegator :@target, :path
23
+
24
+ # Re-thinking.
25
+ #
26
+ # Requirements:
27
+ #
28
+ # 1. User friendly display of what is different in context
29
+ # 2. Able to apply the diff to another model (which was based on the "base" of the diff)
30
+ #
31
+ # Delete: example
32
+ # ArchimateNode child.bounds
33
+ # ArchimateNode, attribute model, "name"
34
+ # DiffableArray, ArchimateNode model.elements, element
35
+ # bendpoint attributes under connection
36
+ # documentation
37
+ # properties
38
+ # child/style/fill_color
39
+ # child/style/font/name
40
+ #
41
+ # @param target [Dry::Struct with id attribute] the element operated on (why is array treated as a special case?)
42
+ # @param changed_from [same class as target] (optional) for change this is the previous value
43
+ # def initialize(changed_from, target)
44
+ def initialize(target, changed_from = nil)
45
+ # raise TypeError, "Expected target to be an ArchimateNodeReference" unless target.is_a?(ArchimateNodeReference)
46
+ @target = target
47
+ @changed_from = changed_from
48
+ end
49
+
50
+ def ==(other)
51
+ other.is_a?(self.class) &&
52
+ @target == other.target &&
53
+ @changed_from == other.changed_from
54
+ end
55
+
56
+ # Difference sorting is based on the path.
57
+ # Top level components are sorted in this order: (elements, relationships, diagrams, organizations)
58
+ # Array entries are sorted by numeric order
59
+ # Others are sorted alphabetically
60
+ # TODO: this isn't complete
61
+ def <=>(other)
62
+ a = path_to_array
63
+ b = other.path_to_array
64
+
65
+ part_a = a.shift
66
+ part_b = b.shift
67
+ res = PATH_ROOT_SORT_ORDER.index(part_a) <=> PATH_ROOT_SORT_ORDER.index(part_b)
68
+ return res unless res.zero?
69
+
70
+ until a.empty? || b.empty?
71
+ part_a = a.shift
72
+ part_b = b.shift
73
+
74
+ return part_a <=> part_b unless (part_a <=> part_b).zero?
75
+ end
76
+
77
+ return -1 if a.empty?
78
+ return 1 if b.empty?
79
+ part_a <=> part_b
80
+ end
81
+
82
+ def delete?
83
+ false
84
+ end
85
+
86
+ def change?
87
+ false
88
+ end
89
+
90
+ def insert?
91
+ false
92
+ end
93
+
94
+ def move?
95
+ false
96
+ end
97
+
98
+ def path_to_array
99
+ path(force_array_index: :index).split("/").map do |p|
100
+ md = ARRAY_RE.match(p)
101
+ md ? md[1].to_i : p
102
+ end
103
+ end
104
+
105
+ def summary_element
106
+ summary_elements = [DataModel::Element, DataModel::Organization, DataModel::Relationship, DataModel::Diagram, DataModel::Model]
107
+ se = target.value.primitive? ? target.parent : target.value
108
+ se = se.parent while summary_elements.none? { |c| se.is_a?(c) }
109
+ se
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ class Insert < Difference
5
+ using DataModel::DiffableArray
6
+
7
+ # Create a new Insert difference
8
+ #
9
+ # @param inserted_element [Archimate::DataModel::ArchimateNode] Element
10
+ # that was inserted
11
+ # @param sub_path [str] Path under inserted_element for primitive values
12
+ def initialize(target)
13
+ super
14
+ end
15
+
16
+ def to_s
17
+ # Note - the explicit to_s is required to access the DiffableArray
18
+ # implementation if the parent is an Array.
19
+ "#{diff_type} #{target} into #{target.parent.to_s}"
20
+ end
21
+
22
+ def apply(to_model)
23
+ throw TypeError, "Expected a Archimate::DataModel::Model, was a #{to_model.class}" unless to_model.is_a?(DataModel::Model)
24
+ target.insert(to_model)
25
+ to_model
26
+ end
27
+
28
+ def insert?
29
+ true
30
+ end
31
+
32
+ def kind
33
+ "Insert"
34
+ end
35
+
36
+ private
37
+
38
+ def diff_type
39
+ Color.color('INSERT:', :insert)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parallel"
4
+
5
+ module Archimate
6
+ module Diff
7
+ class Merge
8
+ include Archimate::Logging
9
+ using DataModel::DiffableArray
10
+
11
+ def three_way(base, local, remote)
12
+ debug { "Computing base:local & base:remote diffs" }
13
+ base_local_diffs, base_remote_diffs = Parallel.map([[base, local], [base, remote]],
14
+ in_processes: 2) do |base_model, other_model|
15
+ base_model.diff(other_model)
16
+ end
17
+
18
+ debug "Finding Conflicts in #{base_local_diffs.size + base_remote_diffs.size} diffs"
19
+ conflicts = Conflicts.new(base_local_diffs, base_remote_diffs)
20
+ resolved_diffs = conflicts.resolve
21
+
22
+ [apply_diffs(resolved_diffs, base.clone), conflicts]
23
+ end
24
+
25
+ # Applies the set of diffs to the model returning a
26
+ # new model with the diffs applied.
27
+ def apply_diffs(diffs, model)
28
+ debug { "Applying #{diffs.size} diffs" }
29
+ progressbar = ProgressIndicator.new(total: diffs.size, title: "Applying diffs")
30
+ diffs
31
+ .inject(model) do |model_a, diff|
32
+ progressbar.increment
33
+ diff.apply(model_a)
34
+ end
35
+ .rebuild_index
36
+ .organize
37
+ ensure
38
+ progressbar&.finish
39
+ end
40
+
41
+ # # TODO: not currently used
42
+ # def find_merged_duplicates
43
+ # [@base_local_diffs, @base_remote_diffs].map do |diffs|
44
+ # deleted_element_diffs = diffs.select(&:delete?).select(&:element?)
45
+ # deleted_element_diffs.each_with_object({}) do |diff, a|
46
+ # element = diff.from_model.elements[diff.element_idx]
47
+ # found = diff.from_model.elements.select do |el|
48
+ # el != element && el.type == element.type && el.name == element.name
49
+ # end
50
+ # next if found.empty?
51
+ # a[diff] = found
52
+ # debug { "\nFound potential de-duplication:" }
53
+ # debug { "\t#{diff}" }
54
+ # debug { "Might be replaced with:\n\t#{found.map(&:to_s).join(" }\n\t")}\n\n"
55
+ # end
56
+ # end
57
+ # end
58
+
59
+ # # TODO: not currently used
60
+ # def filter_path_conflicts(diffs)
61
+ # diffs.sort { |a, b| a.path_to_array.size <=> b.path_to_array.size }.each_with_object([]) do |i, e|
62
+ # diffs.delete(i)
63
+ # path_conflicts = diffs.select { |d| d.path.start_with?(i.path) }
64
+ # path_conflicts.each { |d| diffs.delete(d) }
65
+ # e << i
66
+ # end
67
+ # end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ class Move < Difference
5
+ using DataModel::DiffablePrimitive
6
+ using DataModel::DiffableArray
7
+
8
+ # Create a new Move difference
9
+ #
10
+ # @param target [Archimate::Diff::ArchimateNodeReference] reference to
11
+ # ArchimateNode that was changed
12
+ # @param changed_from [Archimate::Diff::ArchimateNodeReference] Element
13
+ # that was changed
14
+ def initialize(target, changed_from)
15
+ super(target, changed_from)
16
+ end
17
+
18
+ def to_s
19
+ # Note - the explicit to_s is required to access the DiffableArray
20
+ # implementation if the parent is an Array.
21
+ "#{diff_type} #{changed_from.parent&.to_s} #{Color.color(target.to_s, :change)} moved to #{target.array_index}"
22
+ end
23
+
24
+ # TODO: patch is a better name than apply
25
+ def apply(to_model)
26
+ unless to_model.is_a?(DataModel::Model)
27
+ throw(
28
+ TypeError,
29
+ "Expected a Archimate::DataModel::Model, was a #{to_model.class}"
30
+ )
31
+ end
32
+ target.move(to_model, changed_from)
33
+ to_model
34
+ end
35
+
36
+ def move?
37
+ true
38
+ end
39
+
40
+ def kind
41
+ "Move"
42
+ end
43
+
44
+ private
45
+
46
+ def diff_type
47
+ Color.color('MOVE:', :move)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ module Archimate
3
+ module Diff
4
+ require 'archimate/diff/difference'
5
+ require 'archimate/diff/archimate_node_reference'
6
+ require 'archimate/diff/archimate_identified_node_reference'
7
+ require 'archimate/diff/archimate_array_reference'
8
+ require 'archimate/diff/archimate_node_attribute_reference'
9
+ require 'archimate/diff/change'
10
+ require 'archimate/diff/conflict'
11
+ require 'archimate/diff/conflicts'
12
+ require 'archimate/diff/delete'
13
+ require 'archimate/diff/insert'
14
+ require 'archimate/diff/merge'
15
+ require 'archimate/diff/move'
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ require "csv"
3
+
4
+ # This module takes an ArchiMate model and builds a set of CSV files representing it.
5
+ module Archimate
6
+ module Export
7
+ class CSVExport
8
+ attr_reader :model
9
+
10
+ def initialize(model)
11
+ @model = model
12
+ end
13
+
14
+ def to_csv(output_dir: ".")
15
+ (model.relationships + model.elements)
16
+ .group_by(&:type).each do |type, elements|
17
+ CSV.open(
18
+ File.join(output_dir, "#{type}.csv"),
19
+ "wb",
20
+ force_quotes: true
21
+ ) do |csv|
22
+ headers = elements.first.struct_instance_variables
23
+ csv << headers.map(&:to_s)
24
+ elements.each do |element|
25
+ csv << headers.map { |attr| element[attr] }
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This module takes an ArchiMate model and builds GraphML representation of it.
4
+ #
5
+ # Node Schema:
6
+ # Properties:
7
+ # name: Element.name
8
+ # nodelId: Element.id
9
+ # layer: Element.layer
10
+ # documentation: Element.documentation
11
+ # Element.properties
12
+ # ("prop:#{Property.key}"): Property.value
13
+ # Labels:
14
+ # Element.type
15
+ #
16
+ # Edge Schema:
17
+ # Properties:
18
+ # weight (of Relationship Type as in table below)
19
+ # relationshipId: Relationship.id
20
+ # name: Relationship.name
21
+ # documentation: Relationship.documentation
22
+ # accessType: Relationship.access_type
23
+ # Labels:
24
+ # Relationship.type
25
+ #
26
+ # | Weight | Name |
27
+ # |--------+----------------|
28
+ # | 1 | Association |
29
+ # | 2 | Access |
30
+ # | 3 | Used by |
31
+ # | 4 | Realization |
32
+ # | 5 | Assignment |
33
+ # | 6 | Aggregation |
34
+ # | 7 | Composition |
35
+ # | 8 | Flow |
36
+ # | 9 | Triggering |
37
+ # | 10 | Grouping |
38
+ # | 11 | Junction |
39
+ # | 12 | Specialization |
40
+ # Structural stronger than Dependency Relationships
41
+ # ServingRelationship == UsedByRelationship
42
+
43
+
44
+ module Archimate
45
+ module Export
46
+ class Cypher
47
+ attr_reader :output_io
48
+
49
+ WEIGHTS = {
50
+ 'GroupingRelationship' => 0,
51
+ 'JunctionRelationship' => 0,
52
+ 'AssociationRelationship' => 0,
53
+ 'SpecialisationRelationship' => 1,
54
+ 'FlowRelationship' => 2,
55
+ 'TriggeringRelationship' => 3,
56
+ 'InfluenceRelationship' => 4,
57
+ 'AccessRelationship' => 5,
58
+ 'UsedByRelationship' => 6,
59
+ 'RealisationRelationship' => 7,
60
+ 'AssignmentRelationship' => 8,
61
+ 'AggregationRelationship' => 9,
62
+ 'CompositionRelationship' => 10
63
+ }
64
+
65
+ def initialize(output_io)
66
+ @output_io = output_io
67
+ end
68
+
69
+ def to_cypher(model)
70
+ write_cypher_header(model)
71
+ write_nodes(model.elements)
72
+ create_indexes(model.elements)
73
+ write_relationships(model.relationships)
74
+ # TODO: write properties
75
+ end
76
+
77
+ def write_cypher_header(model)
78
+ write "// Cypher import script of ArchiMate model #{model.name}. Produced #{DateTime.now}\n"
79
+ end
80
+
81
+ def write_nodes(elements)
82
+ write "\n// Nodes\n"
83
+ elements.each do |element|
84
+ props = add_docs(
85
+ {
86
+ layer: element.layer.delete(" "),
87
+ name: element.name,
88
+ nodeId: element.id
89
+ }.merge(
90
+ element.properties.each_with_object({}) do |prop, memo|
91
+ memo["prop:#{prop.key}"] = prop.value unless prop.value.nil?
92
+ end
93
+ ), element.documentation
94
+ )
95
+
96
+ write(
97
+ node(
98
+ element.type,
99
+ props
100
+ )
101
+ )
102
+ end
103
+ end
104
+
105
+ def create_indexes(elements)
106
+ write "\n// Indexes\n"
107
+ elements.map(&:type).uniq.each do |label|
108
+ write "CREATE INDEX ON :#{label}(name);"
109
+ write "CREATE INDEX ON :#{label}(nodeId);"
110
+ end
111
+ end
112
+
113
+ def write_relationships(relationships)
114
+ write "\n// Relationships\n"
115
+ relationships.each do |rel|
116
+ write relationship(rel)
117
+ end
118
+ end
119
+
120
+ private
121
+
122
+ def node(label, properties = {})
123
+ "CREATE (n:#{label} #{props(properties)});"
124
+ end
125
+
126
+ def relationship(rel)
127
+ "MATCH #{source(rel)},#{target(rel)} " \
128
+ "CREATE (s)-#{relationship_def(rel)}->(t);"
129
+ end
130
+
131
+ def props(properties)
132
+ "{ #{properties.reject { |_k, v| v.nil? }.map { |k, v| "`#{k}`: #{v.inspect}" }.join(', ')} }"
133
+ end
134
+
135
+ def source(rel)
136
+ "(s #{props(nodeId: rel.source)})"
137
+ end
138
+
139
+ def weight(t)
140
+ return 0 unless WEIGHTS.include?(t)
141
+ WEIGHTS[t]
142
+ end
143
+
144
+ def add_docs(h, l)
145
+ t = l.map(&:text).join("\n").strip
146
+ return h if t.empty?
147
+ h.merge(documentation: t)
148
+ end
149
+
150
+ def relationship_def(rel)
151
+ h = add_docs(
152
+ {
153
+ name: rel.name,
154
+ relationshipId: rel.id,
155
+ accessType: rel.access_type,
156
+ weight: weight(rel.type)
157
+ }, rel.documentation
158
+ )
159
+ "[r:#{rel.type} #{props(h)}]"
160
+ end
161
+
162
+ def target(rel)
163
+ "(t #{props(nodeId: rel.target)})"
164
+ end
165
+
166
+ def write(str)
167
+ @output_io.puts(str)
168
+ end
169
+ end
170
+ end
171
+ end