archimate 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|