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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.projectile +18 -0
- data/.rubocop.yml +13 -0
- data/.travis.yml +4 -0
- data/.yardocs +15 -0
- data/Gemfile +5 -0
- data/Guardfile +27 -0
- data/LICENSE +201 -0
- data/README.md +94 -0
- data/Rakefile +29 -0
- data/TODOs.org +286 -0
- data/archimate.gemspec +51 -0
- data/bin/archimate +17 -0
- data/bin/console +13 -0
- data/bin/setup +8 -0
- data/exe/archidiff +7 -0
- data/exe/archidiff-summary +7 -0
- data/exe/archimate +7 -0
- data/exe/archimerge +7 -0
- data/exe/fmtxml +7 -0
- data/lib/archimate/cli/archi.rb +189 -0
- data/lib/archimate/cli/cleanup.rb +54 -0
- data/lib/archimate/cli/conflict_resolver.rb +39 -0
- data/lib/archimate/cli/convert.rb +39 -0
- data/lib/archimate/cli/diff.rb +31 -0
- data/lib/archimate/cli/diff_summary.rb +101 -0
- data/lib/archimate/cli/duper.rb +85 -0
- data/lib/archimate/cli/lint.rb +16 -0
- data/lib/archimate/cli/mapper.rb +82 -0
- data/lib/archimate/cli/merge.rb +49 -0
- data/lib/archimate/cli/merger.rb +109 -0
- data/lib/archimate/cli/stats.rb +42 -0
- data/lib/archimate/cli/svger.rb +40 -0
- data/lib/archimate/color.rb +53 -0
- data/lib/archimate/config.rb +35 -0
- data/lib/archimate/data_model/any_attribute.rb +13 -0
- data/lib/archimate/data_model/any_element.rb +15 -0
- data/lib/archimate/data_model/archimate_node.rb +181 -0
- data/lib/archimate/data_model/bounds.rb +72 -0
- data/lib/archimate/data_model/color.rb +47 -0
- data/lib/archimate/data_model/concept.rb +14 -0
- data/lib/archimate/data_model/concern.rb +17 -0
- data/lib/archimate/data_model/connection.rb +107 -0
- data/lib/archimate/data_model/constants.rb +82 -0
- data/lib/archimate/data_model/container.rb +17 -0
- data/lib/archimate/data_model/diagram.rb +40 -0
- data/lib/archimate/data_model/diffable_array.rb +213 -0
- data/lib/archimate/data_model/diffable_primitive.rb +83 -0
- data/lib/archimate/data_model/documentation.rb +14 -0
- data/lib/archimate/data_model/element.rb +87 -0
- data/lib/archimate/data_model/font.rb +54 -0
- data/lib/archimate/data_model/label.rb +19 -0
- data/lib/archimate/data_model/lang_string.rb +41 -0
- data/lib/archimate/data_model/location.rb +31 -0
- data/lib/archimate/data_model/metadata.rb +14 -0
- data/lib/archimate/data_model/model.rb +217 -0
- data/lib/archimate/data_model/modeling_note.rb +13 -0
- data/lib/archimate/data_model/named_referenceable.rb +14 -0
- data/lib/archimate/data_model/organization.rb +40 -0
- data/lib/archimate/data_model/property.rb +25 -0
- data/lib/archimate/data_model/property_definition.rb +16 -0
- data/lib/archimate/data_model/referenceable.rb +29 -0
- data/lib/archimate/data_model/relationship.rb +85 -0
- data/lib/archimate/data_model/schema_info.rb +18 -0
- data/lib/archimate/data_model/style.rb +32 -0
- data/lib/archimate/data_model/view.rb +12 -0
- data/lib/archimate/data_model/view_concept.rb +18 -0
- data/lib/archimate/data_model/view_node.rb +114 -0
- data/lib/archimate/data_model/viewpoint.rb +87 -0
- data/lib/archimate/data_model.rb +54 -0
- data/lib/archimate/diff/archimate_array_reference.rb +113 -0
- data/lib/archimate/diff/archimate_identified_node_reference.rb +41 -0
- data/lib/archimate/diff/archimate_node_attribute_reference.rb +70 -0
- data/lib/archimate/diff/archimate_node_reference.rb +80 -0
- data/lib/archimate/diff/change.rb +49 -0
- data/lib/archimate/diff/conflict.rb +31 -0
- data/lib/archimate/diff/conflicts/base_conflict.rb +53 -0
- data/lib/archimate/diff/conflicts/deleted_items_child_updated_conflict.rb +30 -0
- data/lib/archimate/diff/conflicts/deleted_items_referenced_conflict.rb +63 -0
- data/lib/archimate/diff/conflicts/path_conflict.rb +51 -0
- data/lib/archimate/diff/conflicts.rb +89 -0
- data/lib/archimate/diff/delete.rb +41 -0
- data/lib/archimate/diff/difference.rb +113 -0
- data/lib/archimate/diff/insert.rb +43 -0
- data/lib/archimate/diff/merge.rb +70 -0
- data/lib/archimate/diff/move.rb +51 -0
- data/lib/archimate/diff.rb +17 -0
- data/lib/archimate/export/csv_export.rb +32 -0
- data/lib/archimate/export/cypher.rb +171 -0
- data/lib/archimate/export/graph_ml.rb +131 -0
- data/lib/archimate/export/n_quads.rb +142 -0
- data/lib/archimate/file_format.rb +30 -0
- data/lib/archimate/file_formats/archi_file_format.rb +151 -0
- data/lib/archimate/file_formats/archi_file_reader.rb +252 -0
- data/lib/archimate/file_formats/archi_file_writer.rb +230 -0
- data/lib/archimate/file_formats/archimate_v2.rb +461 -0
- data/lib/archimate/file_formats/model_exchange_file/xml_lang_string.rb +35 -0
- data/lib/archimate/file_formats/model_exchange_file/xml_metadata.rb +115 -0
- data/lib/archimate/file_formats/model_exchange_file/xml_property_definitions.rb +28 -0
- data/lib/archimate/file_formats/model_exchange_file/xml_property_defs.rb +27 -0
- data/lib/archimate/file_formats/model_exchange_file_reader.rb +237 -0
- data/lib/archimate/file_formats/model_exchange_file_reader_21.rb +73 -0
- data/lib/archimate/file_formats/model_exchange_file_reader_30.rb +134 -0
- data/lib/archimate/file_formats/model_exchange_file_writer.rb +157 -0
- data/lib/archimate/file_formats/model_exchange_file_writer_21.rb +143 -0
- data/lib/archimate/file_formats/model_exchange_file_writer_30.rb +153 -0
- data/lib/archimate/file_formats/writer.rb +56 -0
- data/lib/archimate/lint/duplicate_entities.rb +121 -0
- data/lib/archimate/lint/linter.rb +146 -0
- data/lib/archimate/logging.rb +55 -0
- data/lib/archimate/maybe_io.rb +34 -0
- data/lib/archimate/progress_indicator.rb +29 -0
- data/lib/archimate/svg/archimate.css +232 -0
- data/lib/archimate/svg/child.rb +29 -0
- data/lib/archimate/svg/connection.rb +184 -0
- data/lib/archimate/svg/css_style.rb +31 -0
- data/lib/archimate/svg/diagram.rb +87 -0
- data/lib/archimate/svg/entity/and_junction.rb +14 -0
- data/lib/archimate/svg/entity/application_collaboration.rb +14 -0
- data/lib/archimate/svg/entity/application_component.rb +41 -0
- data/lib/archimate/svg/entity/application_event.rb +13 -0
- data/lib/archimate/svg/entity/application_function.rb +13 -0
- data/lib/archimate/svg/entity/application_interaction.rb +13 -0
- data/lib/archimate/svg/entity/application_interface.rb +13 -0
- data/lib/archimate/svg/entity/application_process.rb +13 -0
- data/lib/archimate/svg/entity/application_service.rb +13 -0
- data/lib/archimate/svg/entity/artifact.rb +39 -0
- data/lib/archimate/svg/entity/assessment.rb +14 -0
- data/lib/archimate/svg/entity/base_entity.rb +128 -0
- data/lib/archimate/svg/entity/business_actor.rb +14 -0
- data/lib/archimate/svg/entity/business_collaboration.rb +14 -0
- data/lib/archimate/svg/entity/business_event.rb +10 -0
- data/lib/archimate/svg/entity/business_function.rb +13 -0
- data/lib/archimate/svg/entity/business_interaction.rb +13 -0
- data/lib/archimate/svg/entity/business_interface.rb +13 -0
- data/lib/archimate/svg/entity/business_object.rb +13 -0
- data/lib/archimate/svg/entity/business_process.rb +13 -0
- data/lib/archimate/svg/entity/business_role.rb +14 -0
- data/lib/archimate/svg/entity/business_service.rb +13 -0
- data/lib/archimate/svg/entity/capability.rb +14 -0
- data/lib/archimate/svg/entity/communication_network.rb +14 -0
- data/lib/archimate/svg/entity/communication_path.rb +14 -0
- data/lib/archimate/svg/entity/constraint.rb +15 -0
- data/lib/archimate/svg/entity/contract.rb +13 -0
- data/lib/archimate/svg/entity/course_of_action.rb +14 -0
- data/lib/archimate/svg/entity/data_entity.rb +29 -0
- data/lib/archimate/svg/entity/data_object.rb +13 -0
- data/lib/archimate/svg/entity/deliverable.rb +13 -0
- data/lib/archimate/svg/entity/device.rb +48 -0
- data/lib/archimate/svg/entity/diagram_model_reference.rb +22 -0
- data/lib/archimate/svg/entity/diagram_object.rb +30 -0
- data/lib/archimate/svg/entity/distribution_network.rb +14 -0
- data/lib/archimate/svg/entity/driver.rb +14 -0
- data/lib/archimate/svg/entity/equipment.rb +16 -0
- data/lib/archimate/svg/entity/event_entity.rb +36 -0
- data/lib/archimate/svg/entity/facility.rb +16 -0
- data/lib/archimate/svg/entity/function_entity.rb +14 -0
- data/lib/archimate/svg/entity/gap.rb +21 -0
- data/lib/archimate/svg/entity/goal.rb +14 -0
- data/lib/archimate/svg/entity/group.rb +24 -0
- data/lib/archimate/svg/entity/implementation_event.rb +13 -0
- data/lib/archimate/svg/entity/infrastructure_function.rb +13 -0
- data/lib/archimate/svg/entity/infrastructure_interface.rb +13 -0
- data/lib/archimate/svg/entity/infrastructure_service.rb +13 -0
- data/lib/archimate/svg/entity/interaction_entity.rb +14 -0
- data/lib/archimate/svg/entity/interface_entity.rb +35 -0
- data/lib/archimate/svg/entity/junction.rb +19 -0
- data/lib/archimate/svg/entity/location.rb +14 -0
- data/lib/archimate/svg/entity/material.rb +14 -0
- data/lib/archimate/svg/entity/meaning.rb +40 -0
- data/lib/archimate/svg/entity/motivation_entity.rb +40 -0
- data/lib/archimate/svg/entity/network.rb +14 -0
- data/lib/archimate/svg/entity/node.rb +33 -0
- data/lib/archimate/svg/entity/node_shape.rb +64 -0
- data/lib/archimate/svg/entity/note.rb +30 -0
- data/lib/archimate/svg/entity/or_junction.rb +14 -0
- data/lib/archimate/svg/entity/outcome.rb +15 -0
- data/lib/archimate/svg/entity/path.rb +14 -0
- data/lib/archimate/svg/entity/plateau.rb +15 -0
- data/lib/archimate/svg/entity/principle.rb +15 -0
- data/lib/archimate/svg/entity/process_entity.rb +63 -0
- data/lib/archimate/svg/entity/product.rb +20 -0
- data/lib/archimate/svg/entity/rect.rb +13 -0
- data/lib/archimate/svg/entity/rect_entity.rb +25 -0
- data/lib/archimate/svg/entity/representation.rb +31 -0
- data/lib/archimate/svg/entity/requirement.rb +15 -0
- data/lib/archimate/svg/entity/resource.rb +14 -0
- data/lib/archimate/svg/entity/rounded_rect_entity.rb +23 -0
- data/lib/archimate/svg/entity/service_entity.rb +51 -0
- data/lib/archimate/svg/entity/sketch_model_sticky.rb +14 -0
- data/lib/archimate/svg/entity/stakeholder.rb +15 -0
- data/lib/archimate/svg/entity/system_software.rb +14 -0
- data/lib/archimate/svg/entity/technology_collaboration.rb +14 -0
- data/lib/archimate/svg/entity/technology_event.rb +13 -0
- data/lib/archimate/svg/entity/technology_function.rb +13 -0
- data/lib/archimate/svg/entity/technology_interaction.rb +13 -0
- data/lib/archimate/svg/entity/technology_interface.rb +13 -0
- data/lib/archimate/svg/entity/technology_process.rb +13 -0
- data/lib/archimate/svg/entity/technology_service.rb +13 -0
- data/lib/archimate/svg/entity/value.rb +27 -0
- data/lib/archimate/svg/entity/work_package.rb +14 -0
- data/lib/archimate/svg/entity.rb +93 -0
- data/lib/archimate/svg/entity_factory.rb +17 -0
- data/lib/archimate/svg/extents.rb +27 -0
- data/lib/archimate/svg/point.rb +7 -0
- data/lib/archimate/svg/svg_template.rb +27 -0
- data/lib/archimate/svg/svg_template.svg.erb +169 -0
- data/lib/archimate/version.rb +4 -0
- data/lib/archimate.rb +114 -0
- 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
|