archimate 2.0.2 → 2.0.3

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 (108) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +1 -1
  3. data/Gemfile +1 -0
  4. data/README.md +10 -55
  5. data/Rakefile +19 -1
  6. data/archimate.gemspec +16 -14
  7. data/bin/archimate +12 -0
  8. data/lib/archimate/cli/archi.rb +38 -19
  9. data/lib/archimate/cli/cleanup.rb +41 -25
  10. data/lib/archimate/cli/duper.rb +0 -1
  11. data/lib/archimate/cli/mapper.rb +53 -40
  12. data/lib/archimate/cli/svger.rb +33 -4
  13. data/lib/archimate/core_refinements.rb +41 -0
  14. data/lib/archimate/data_model/bounds.rb +14 -4
  15. data/lib/archimate/data_model/comparison.rb +3 -27
  16. data/lib/archimate/data_model/connection.rb +26 -11
  17. data/lib/archimate/data_model/diagram.rb +17 -10
  18. data/lib/archimate/data_model/element.rb +21 -9
  19. data/lib/archimate/data_model/elements.rb +24 -18
  20. data/lib/archimate/data_model/lang_string.rb +34 -10
  21. data/lib/archimate/data_model/layer.rb +6 -0
  22. data/lib/archimate/data_model/location.rb +10 -13
  23. data/lib/archimate/data_model/model.rb +55 -43
  24. data/lib/archimate/data_model/organization.rb +21 -5
  25. data/lib/archimate/data_model/property.rb +0 -6
  26. data/lib/archimate/data_model/referenceable.rb +29 -3
  27. data/lib/archimate/data_model/referenceable_list.rb +34 -14
  28. data/lib/archimate/data_model/relationship.rb +29 -8
  29. data/lib/archimate/data_model/relationship_references.rb +115 -6
  30. data/lib/archimate/data_model/relationships.rb +15 -0
  31. data/lib/archimate/data_model/view_node.rb +20 -13
  32. data/lib/archimate/data_model/viewpoint.rb +13 -1
  33. data/lib/archimate/data_model/viewpoints.rb +416 -0
  34. data/lib/archimate/data_model.rb +7 -1
  35. data/lib/archimate/derived_relations.rb +2 -11
  36. data/lib/archimate/export/cypher.rb +6 -5
  37. data/lib/archimate/file_format.rb +1 -0
  38. data/lib/archimate/file_formats/archi_file_reader.rb +11 -1
  39. data/lib/archimate/file_formats/archi_file_writer.rb +15 -46
  40. data/lib/archimate/file_formats/archi_file_writer_4.rb +14 -0
  41. data/lib/archimate/file_formats/model_exchange_file_reader.rb +2 -1
  42. data/lib/archimate/file_formats/model_exchange_file_writer_21.rb +1 -0
  43. data/lib/archimate/file_formats/sax/archi/diagram.rb +53 -13
  44. data/lib/archimate/file_formats/sax/archi/location.rb +1 -3
  45. data/lib/archimate/file_formats/sax/model_exchange_file/diagram.rb +13 -2
  46. data/lib/archimate/file_formats/sax.rb +1 -0
  47. data/lib/archimate/file_formats/serializer/archi/archi_file_writer.rb +63 -0
  48. data/lib/archimate/file_formats/serializer/archi/archi_file_writer_3.rb +18 -0
  49. data/lib/archimate/file_formats/serializer/archi/archi_file_writer_4.rb +18 -0
  50. data/lib/archimate/file_formats/serializer/archi/bounds.rb +2 -2
  51. data/lib/archimate/file_formats/serializer/archi/connection.rb +24 -13
  52. data/lib/archimate/file_formats/serializer/archi/diagram.rb +3 -3
  53. data/lib/archimate/file_formats/serializer/archi/element.rb +2 -2
  54. data/lib/archimate/file_formats/serializer/archi/organization.rb +1 -1
  55. data/lib/archimate/file_formats/serializer/archi/property.rb +1 -1
  56. data/lib/archimate/file_formats/serializer/archi/relationship.rb +2 -2
  57. data/lib/archimate/file_formats/serializer/archi/view_node.rb +20 -22
  58. data/lib/archimate/file_formats/serializer/archi/viewpoint3.rb +43 -0
  59. data/lib/archimate/file_formats/serializer/archi/viewpoint4.rb +41 -0
  60. data/lib/archimate/file_formats/serializer/model_exchange_file/style.rb +3 -7
  61. data/lib/archimate/file_formats/serializer/model_exchange_file/v21/diagram.rb +3 -3
  62. data/lib/archimate/file_formats/serializer/model_exchange_file/v21/model.rb +9 -2
  63. data/lib/archimate/file_formats/serializer/model_exchange_file/v21/view_node.rb +1 -1
  64. data/lib/archimate/file_formats/serializer/model_exchange_file/v21/viewpoint.rb +23 -0
  65. data/lib/archimate/file_formats/serializer/model_exchange_file/v30/diagram.rb +3 -3
  66. data/lib/archimate/file_formats/serializer/model_exchange_file/v30/model.rb +5 -7
  67. data/lib/archimate/file_formats/serializer/model_exchange_file/v30/view_node.rb +3 -3
  68. data/lib/archimate/file_formats/serializer/writer.rb +0 -5
  69. data/lib/archimate/file_formats/serializer.rb +6 -2
  70. data/lib/archimate/lint/duplicate_entities.rb +5 -5
  71. data/lib/archimate/lint/linter.rb +4 -4
  72. data/lib/archimate/maybe_io.rb +3 -2
  73. data/lib/archimate/svg/archimate.css +19 -21
  74. data/lib/archimate/svg/connection.rb +1 -1
  75. data/lib/archimate/svg/diagram.rb +1 -6
  76. data/lib/archimate/svg/entity/application_component.rb +9 -3
  77. data/lib/archimate/svg/entity/constraint.rb +0 -1
  78. data/lib/archimate/svg/entity/contract.rb +9 -0
  79. data/lib/archimate/svg/entity/data_entity.rb +1 -1
  80. data/lib/archimate/svg/entity/device.rb +1 -1
  81. data/lib/archimate/svg/entity/event_entity.rb +24 -7
  82. data/lib/archimate/svg/entity/group.rb +23 -4
  83. data/lib/archimate/svg/entity/grouping.rb +37 -0
  84. data/lib/archimate/svg/entity/interface_entity.rb +1 -1
  85. data/lib/archimate/svg/entity/node.rb +1 -1
  86. data/lib/archimate/svg/entity/outcome.rb +0 -1
  87. data/lib/archimate/svg/entity/principle.rb +0 -1
  88. data/lib/archimate/svg/entity/process_entity.rb +1 -1
  89. data/lib/archimate/svg/entity/requirement.rb +0 -1
  90. data/lib/archimate/svg/entity/service_entity.rb +6 -13
  91. data/lib/archimate/svg/entity.rb +1 -0
  92. data/lib/archimate/svg/entity_factory.rb +9 -5
  93. data/lib/archimate/svg/path.rb +57 -46
  94. data/lib/archimate/svg/point.rb +4 -0
  95. data/lib/archimate/svg/segment.rb +30 -0
  96. data/lib/archimate/svg/svg_template.svg.erb +11 -3
  97. data/lib/archimate/svg/view_node.rb +22 -0
  98. data/lib/archimate/version.rb +1 -1
  99. data/lib/archimate.rb +3 -2
  100. metadata +54 -54
  101. data/exe/archidiff +0 -7
  102. data/exe/archidiff-summary +0 -7
  103. data/exe/archimerge +0 -7
  104. data/exe/fmtxml +0 -7
  105. data/lib/archimate/data_model/viewpoint_type.rb +0 -389
  106. data/lib/archimate/file_formats/serializer/archi/location.rb +0 -26
  107. data/lib/archimate/file_formats/serializer/archi/viewpoint_type.rb +0 -45
  108. data/lib/archimate/svg/child.rb +0 -29
@@ -22,6 +22,15 @@ module Archimate
22
22
  Archimate::DataModel::Bounds.new(x: 0, y: 0, width: 0, height: 0)
23
23
  end
24
24
 
25
+ def self.from_location(location)
26
+ DataModel::Bounds.new(
27
+ x: location.x,
28
+ y: location.y,
29
+ width: 0,
30
+ height: 0
31
+ )
32
+ end
33
+
25
34
  def initialize(x: nil, y: nil, width:, height:)
26
35
  raise "Width expected" unless width
27
36
  raise "Height expected" unless height
@@ -68,19 +77,19 @@ module Archimate
68
77
  )
69
78
  end
70
79
 
71
- def is_above?(other)
80
+ def above?(other)
72
81
  bottom < other.top
73
82
  end
74
83
 
75
- def is_below?(other)
84
+ def below?(other)
76
85
  top > other.bottom
77
86
  end
78
87
 
79
- def is_right_of?(other)
88
+ def right_of?(other)
80
89
  left > other.right
81
90
  end
82
91
 
83
- def is_left_of?(other)
92
+ def left_of?(other)
84
93
  right < other.left
85
94
  end
86
95
 
@@ -88,6 +97,7 @@ module Archimate
88
97
  Bounds.new(x: left + val, y: top + val, width: width - val * 2, height: height - val * 2)
89
98
  end
90
99
 
100
+ # Tests if this bounds is inside the argument Bounds
91
101
  def inside?(other)
92
102
  left > other.left &&
93
103
  right < other.right &&
@@ -78,18 +78,12 @@ module Archimate
78
78
  end
79
79
  end
80
80
 
81
- # @todo implement inspect as a more normal inspect with correct handling for
82
- # attr_info values for comparison_attr
83
81
  def inspect
84
82
  "#<#{self.class.to_s.split('::').last}\n " +
85
83
  self.class.attr_info
86
84
  .map { |sym, info| info.attr_inspect(self, sym) }
87
85
  .compact
88
- .join("\n ") + "\n >"
89
- end
90
-
91
- def brief_inspect
92
- "#<#{self.class.to_s.split('::').last}#{" id=#{id}" if respond_to?(:id)}#{" #{name.brief_inspect}" if respond_to?(:name) && name}>"
86
+ .join(" ") + ">"
93
87
  end
94
88
 
95
89
  def self.included(base)
@@ -103,29 +97,11 @@ module Archimate
103
97
  when :no_compare
104
98
  nil
105
99
  when nil
106
- "#{sym}: #{attr_value_inspect(obj.send(sym))}"
100
+ "#{sym}: #{obj.send(sym).inspect}"
107
101
  else
108
102
  val = obj.send(sym)
109
103
  cval = val&.send(comparison_attr)
110
- "#{sym}: #<#{val.class.to_s.split('::').last} #{comparison_attr}=#{attr_value_inspect(cval)}>"
111
- end
112
- end
113
-
114
- def attr_value_inspect(val)
115
- case val
116
- when Comparison
117
- val.brief_inspect
118
- when Array
119
- vals = val.first(3).map do |v|
120
- if v.is_a?(Comparison)
121
- v.brief_inspect
122
- else
123
- v.inspect
124
- end
125
- end
126
- "[#{vals.join(', ')}#{"...#{val.size}" if val.size > 3}]"
127
- else
128
- val.inspect
104
+ "#{sym}: #<#{val.class.to_s.split('::').last} #{comparison_attr}=#{cval.inspect}>"
129
105
  end
130
106
  end
131
107
  end
@@ -92,8 +92,20 @@ module Archimate
92
92
  ].compact.join(" ")
93
93
  end
94
94
 
95
- def referenced_identified_nodes
96
- [@source, @target, @relationship].compact
95
+ def start_location
96
+ source_attachment || source_bounds.center
97
+ end
98
+
99
+ def end_location
100
+ target_attachment || target_bounds.center
101
+ end
102
+
103
+ def source_bounds
104
+ source.absolute_position
105
+ end
106
+
107
+ def target_bounds
108
+ target.absolute_position
97
109
  end
98
110
 
99
111
  # This is used when rendering a connection to connection relationship.
@@ -101,17 +113,20 @@ module Archimate
101
113
  []
102
114
  end
103
115
 
104
- # TODO: Is this true for all or only Archi models?
105
116
  def absolute_position
106
- # offset = bounds || Archimate::DataModel::Bounds.zero
107
- offset = Archimate::DataModel::Bounds.zero
108
- el = parent.parent
109
- while el.respond_to?(:bounds) && el.bounds
110
- bounds = el.bounds
111
- offset = offset.with(x: (offset.x || 0) + (bounds.x || 0), y: (offset.y || 0) + (bounds.y || 0))
112
- el = el.parent.parent
117
+ pt = Svg::Path.new(self).midpoint
118
+ Bounds.new(x: pt.x, y: pt.y, width: 0, height: 0)
119
+ end
120
+
121
+ def replace_item_with(item, replacement)
122
+ super
123
+ item.remove_reference(self)
124
+ case item
125
+ when relationship
126
+ @relationship = replacement
127
+ else
128
+ raise "Trying to replace #{item} that I don't reference"
113
129
  end
114
- offset
115
130
  end
116
131
  end
117
132
  end
@@ -26,10 +26,6 @@ module Archimate
26
26
  # @!attribute [rw] properties
27
27
  # @return [Array<Property>]
28
28
  model_attr :properties, writable: true, default: []
29
- # @todo make this a ViewpointType is better but is Archimate specification version dependent
30
- # @!attribute [r] viewpoint_type
31
- # @return [String, NilClass]
32
- model_attr :viewpoint_type, default: nil
33
29
  # @!attribute [r] viewpoint
34
30
  # @return [Viewpoint, NilClass]
35
31
  model_attr :viewpoint, default: nil
@@ -73,14 +69,25 @@ module Archimate
73
69
  end
74
70
 
75
71
  def total_viewpoint?
76
- viewpoint_type.nil? || viewpoint_type.empty?
72
+ viewpoint.nil?
77
73
  end
78
74
 
79
- def referenced_identified_nodes
80
- (nodes.to_ary + connections)
81
- .map(&:referenced_identified_nodes)
82
- .flatten
83
- .uniq
75
+ def viewpoint_description
76
+ case viewpoint
77
+ when Symbol
78
+ viewpoint.to_s
79
+ when Viewpoint
80
+ viewpoint.name.to_s
81
+ else
82
+ case type
83
+ when "canvas:CanvasModel"
84
+ "Canvas"
85
+ when "archimate:SketchModel"
86
+ "Sketch"
87
+ else
88
+ "Total"
89
+ end
90
+ end
84
91
  end
85
92
  end
86
93
  end
@@ -22,10 +22,12 @@ module Archimate
22
22
  # @!attribute [rw] documentation
23
23
  # @return [PreservedLangString, NilClass]
24
24
  model_attr :documentation, writable: true, default: nil
25
- # # @return [Array<AnyElement>]
26
- # model_attr :other_elements
27
- # # @return [Array<AnyAttribute>]
28
- # model_attr :other_attributes
25
+ # @!attribute [r] other_elements
26
+ # @return [Array<AnyElement>]
27
+ model_attr :other_elements, default: []
28
+ # @!attribute [r] other_attributes
29
+ # @return [Array<AnyAttribute>]
30
+ model_attr :other_attributes, default: []
29
31
  # @!attribute [r] properties
30
32
  # @return [Array<Property>]
31
33
  model_attr :properties, default: []
@@ -51,12 +53,22 @@ module Archimate
51
53
  # 2. Child `documentation` (and different `xml:lang` attribute values)
52
54
  # 3. Child `properties`
53
55
  # 4. Any other elements
54
- # @todo this implementation is broken - in_model no longer exists
55
56
  def merge(element)
56
- super
57
- element.diagrams.each { |diagram| diagram.replace(element, self) }
58
- element.relationships.each { |relationship| relationship.replace(element, self) }
59
- element.organization.remove(element.id)
57
+ if !documentation
58
+ self.documentation = element.documentation
59
+ elsif documentation != element.documentation
60
+ documentation.merge(element.documentation)
61
+ end
62
+ element.properties.each do |property|
63
+ unless properties.find { |my_prop| my_prop.property_definition.name == property.property_definition.name && my_prop.value == property.value}
64
+ properties << property
65
+ end
66
+ end
67
+ end
68
+
69
+ # Diagrams that this entity is referenced in.
70
+ def diagrams
71
+ references.select { |ref| ref.is_a?(Diagram) }
60
72
  end
61
73
  end
62
74
  end
@@ -115,15 +115,6 @@ module Archimate
115
115
  end
116
116
  end
117
117
 
118
- class Meaning < Element
119
- CLASSIFICATION = :passive_structure
120
- LAYER = Layers::Business
121
-
122
- def initialize(args)
123
- super
124
- end
125
- end
126
-
127
118
  class Product < Element
128
119
  CLASSIFICATION = :passive_structure
129
120
  LAYER = Layers::Business
@@ -142,15 +133,6 @@ module Archimate
142
133
  end
143
134
  end
144
135
 
145
- class Value < Element
146
- CLASSIFICATION = :passive_structure
147
- LAYER = Layers::Business
148
-
149
- def initialize(args)
150
- super
151
- end
152
- end
153
-
154
136
  #############################################################
155
137
  # Application Layer
156
138
  #############################################################
@@ -491,6 +473,15 @@ module Archimate
491
473
  end
492
474
  end
493
475
 
476
+ class Meaning < Element
477
+ CLASSIFICATION = :passive_structure
478
+ LAYER = Layers::Motivation
479
+
480
+ def initialize(args)
481
+ super
482
+ end
483
+ end
484
+
494
485
  class Outcome < Element
495
486
  CLASSIFICATION = :active_structure
496
487
  LAYER = Layers::Motivation
@@ -527,6 +518,15 @@ module Archimate
527
518
  end
528
519
  end
529
520
 
521
+ class Value < Element
522
+ CLASSIFICATION = :passive_structure
523
+ LAYER = Layers::Motivation
524
+
525
+ def initialize(args)
526
+ super
527
+ end
528
+ end
529
+
530
530
  #############################################################
531
531
  # Implementation and Migration Layer
532
532
  #############################################################
@@ -676,6 +676,12 @@ module Archimate
676
676
  def self.classes
677
677
  constants.map { |cls_name| const_get(cls_name) }
678
678
  end
679
+
680
+ def self.core_elements
681
+ classes.select do |el|
682
+ [Layers::Business, Layers::Application, Layers::Technology].include?(el::LAYER)
683
+ end
684
+ end
679
685
  end
680
686
  end
681
687
  end
@@ -6,6 +6,7 @@ module Archimate
6
6
  module DataModel
7
7
  # A base string type for multi-language strings.
8
8
  class LangString
9
+ include Comparable
9
10
  include Comparison
10
11
  extend Forwardable
11
12
 
@@ -23,16 +24,18 @@ module Archimate
23
24
 
24
25
  def self.string(str, lang = nil)
25
26
  return nil if !str || str.strip.empty?
26
- new(str, lang)
27
+ new(str, default_lang: lang)
27
28
  end
28
29
 
29
- # @param [Hash{Symbol => Object},LangString, String] attributes
30
+ # @param str [String, LangString] optional shortcut to set define this LangString
31
+ # @param lang_hash [Hash{Symbol => Object}] attributes
32
+ # @param default_lang [String] optional setting of the default language
30
33
  # @raise [Struct::Error] if the given attributes don't conform {#schema}
31
34
  # with given {# # constructor_type}
32
- def initialize(str = nil, lang = nil, lang_hash: {}, default_lang: nil, default_text: nil)
35
+ def initialize(str = nil, lang_hash: {}, default_lang: nil)
33
36
  @lang_hash = lang_hash
34
- @default_lang = default_lang || lang
35
- @default_text = default_text
37
+ @default_lang = default_lang || lang_hash.keys.first
38
+ @default_text = str || lang_hash.fetch(@default_lang, nil)
36
39
  case str
37
40
  when String
38
41
  @lang_hash[@default_lang] = @default_text = str.strip
@@ -41,7 +44,7 @@ module Archimate
41
44
  @default_lang = str.default_lang
42
45
  @default_text = str.default_text
43
46
  else
44
- @lang_hash[default_lang] = default_text if default_text
47
+ @lang_hash[default_lang] = @default_text if @default_text
45
48
  end
46
49
  end
47
50
 
@@ -59,10 +62,6 @@ module Archimate
59
62
  end
60
63
  end
61
64
 
62
- def brief_inspect
63
- "#{to_s.slice(0, 40).inspect}#{'...' if to_s.size > 40}"
64
- end
65
-
66
65
  def by_lang(lang)
67
66
  lang_hash.fetch(lang, nil)
68
67
  end
@@ -75,6 +74,14 @@ module Archimate
75
74
  default_lang
76
75
  end
77
76
 
77
+ def ==(other)
78
+ if other.is_a?(String)
79
+ to_s == other
80
+ else
81
+ super
82
+ end
83
+ end
84
+
78
85
  def =~(other)
79
86
  str = to_s
80
87
  if other.is_a?(Regexp)
@@ -83,6 +90,23 @@ module Archimate
83
90
  Regexp.new(Regexp.escape(str)) =~ other
84
91
  end
85
92
  end
93
+
94
+ def <=>(other)
95
+ to_s <=> other.to_s
96
+ end
97
+
98
+ def merge(other)
99
+ return unless other
100
+ other.lang_hash.each do |k, v|
101
+ if @lang_hash.include?(k)
102
+ @lang_hash[k] = [@lang_hash[k], v].join("\n") if @lang_hash[k] != other.lang_hash[k]
103
+ else
104
+ @lang_hash[k] = v
105
+ end
106
+ end
107
+ @default_lang = @default_lang || other.default_lang || @lang_hash.keys.first
108
+ @default_text = @lang_hash[@default_lang]
109
+ end
86
110
  end
87
111
  end
88
112
  end
@@ -5,6 +5,8 @@ require "ruby-enum"
5
5
  module Archimate
6
6
  module DataModel
7
7
  class Layer
8
+ include Comparable
9
+
8
10
  attr_reader :name
9
11
  attr_reader :background_class
10
12
 
@@ -37,6 +39,10 @@ module Archimate
37
39
  @symbol == other&.to_sym
38
40
  end
39
41
 
42
+ def <=>(other)
43
+ Layers.find_index(self) <=> Layers.find_index(other)
44
+ end
45
+
40
46
  def to_sym
41
47
  @symbol
42
48
  end
@@ -16,32 +16,29 @@ module Archimate
16
16
  # @note the XSD has this as a NonNegativeInteger
17
17
  # @!attribute [r] x
18
18
  # @return [Float]
19
- model_attr :x
19
+ model_attr :x, writable: true
20
20
  # The y (towards the bottom, associated with height) attribute from the Top,Left (i.e. 0,0)
21
21
  # corner of the diagram to the Top, Left corner of the bounding box of the concept.
22
22
  # @note the XSD has this as a NonNegativeInteger
23
23
  # @!attribute [r] y
24
24
  # @return [Float]
25
- model_attr :y
25
+ model_attr :y, writable: true
26
26
 
27
- # These are holdovers from the archi file format and are only maintained for compatability
28
- # @!attribute [r] end_x
29
- # @return [Int, NilClass]
30
- model_attr :end_x, default: nil
31
- # @!attribute [r] end_y
32
- # @return [Int, NilClass]
33
- model_attr :end_y, default: nil
34
-
35
- def initialize(x:, y:, end_x: nil, end_y: nil)
27
+ def initialize(x:, y:)
36
28
  @x = x.to_i
37
29
  @y = y.to_i
38
- @end_x = end_x.nil? ? nil : end_x.to_i
39
- @end_y = end_y.nil? ? nil : end_y.to_i
40
30
  end
41
31
 
42
32
  def to_s
43
33
  "Location(x: #{x}, y: #{y})"
44
34
  end
35
+
36
+ # Returns true if this location is inside the bounds argument
37
+ # @param bounds [Bounds]
38
+ def inside?(bounds)
39
+ bounds.x_range.cover?(x) &&
40
+ bounds.y_range.cover?(y)
41
+ end
45
42
  end
46
43
  end
47
44
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using Archimate::CoreRefinements
4
+
3
5
  module Archimate
4
6
  module DataModel
5
7
  # This is the root model type.
@@ -116,45 +118,65 @@ module Archimate
116
118
  self
117
119
  end
118
120
 
119
- # Only used by [Diff::DeletedItemsReferencedConflict]
120
- def referenced_identified_nodes
121
- classes = [Diagram, ViewNode, Connection, Organization, Relationship].freeze
122
- @index_hash
123
- .values
124
- .select { |entity| classes.include?(entity.class) }
125
- .map(&:referenced_identified_nodes)
126
- .flatten
127
- .uniq
121
+ # @todo this should move into either Comparison or a Mergeable class
122
+ # Steps to merge
123
+ # merge attributes of each copy into master_entity
124
+ # update references of each copy to reference master_entity instead (where it makes sense)
125
+ # remove reference of each copy from its references
126
+ def merge_entities(master_entity, copies)
127
+ copies.delete(master_entity)
128
+ copies.each do |copy|
129
+ master_entity.merge(copy)
130
+ copy.replace_with(master_entity)
131
+ # if !copy.references.empty?
132
+ # puts "#{copy.class} still referenced by #{copy.references.map { |ref| ref.class.name }.join(", ")}"
133
+ # end
134
+ deregister(copy)
135
+ end
128
136
  end
129
137
 
130
- # This is used only by the model [Cli::Cleanup] class.
131
- def unreferenced_nodes
132
- identified_nodes - referenced_identified_nodes
138
+ def replace_item_with(item, replacement)
139
+ case item
140
+ when Organization
141
+ organizations.delete(item)
142
+ organizations << replacement
143
+ when Element
144
+ elements.delete(item)
145
+ elements << replacement
146
+ when Relationship
147
+ relationships.delete(item)
148
+ relationships << replacement
149
+ end
133
150
  end
134
151
 
135
- # def merge_entities(master_entity, copies)
136
- # copies.delete(master_entity)
137
- # copies.each do |copy|
138
- # entities.each do |entity|
139
- # case entity
140
- # when entity == master_entity
141
- # master_entity.merge(copy)
142
- # when Organization
143
- # entity.remove(copy.id)
144
- # when ViewNode, Relationship, Connection
145
- # entity.replace(copy, master_entity)
146
- # end
147
- # end
148
- # deregister(copy)
149
- # end
150
- # end
151
-
152
152
  def make_unique_id
153
- unique_id = random_id
154
- unique_id = random_id while @index_hash.key?(unique_id)
153
+ @random ||= Random.new
154
+ begin
155
+ unique_id = format("%08x", @random.rand(0xffffffff))
156
+ end until !@index_hash.key?(unique_id)
155
157
  unique_id
156
158
  end
157
159
 
160
+ def remove_reference(item)
161
+ case item
162
+ when Element
163
+ elements.delete(item)
164
+ when Relationship
165
+ relationships.delete(item)
166
+ when Diagram
167
+ diagrams.delete(item)
168
+ else
169
+ raise "Unhandled remove reference for type #{item.class}"
170
+ end
171
+ organizations.each { |org| org.remove_reference(item) }
172
+ end
173
+
174
+ Elements.classes.each do |el_cls|
175
+ define_method(el_cls.name.split("::").last.snake_case + "s") do
176
+ elements.select { |el| el.is_a?(el_cls) }
177
+ end
178
+ end
179
+
158
180
  private
159
181
 
160
182
  # Only used by [#find_default_organization]
@@ -233,12 +255,7 @@ module Archimate
233
255
  find_by_class(DataModel::Organization).select { |f| f.items.include?(item) }.first
234
256
  end
235
257
 
236
- # Only used by [#unreferenced_nodes]
237
- def identified_nodes
238
- @index_hash.values.select { |node| node.respond_to? :id }
239
- end
240
-
241
- # @todo make this private - maybe move to [Organization]
258
+ # @todo maybe move to [Organization]
242
259
  def index_organizations(ref)
243
260
  ref.organizations.each do |org|
244
261
  @index_hash[org.id] = index_organizations(org)
@@ -246,7 +263,7 @@ module Archimate
246
263
  ref
247
264
  end
248
265
 
249
- # @todo make this private - maybe move to [ViewNode]
266
+ # @todo maybe move to [ViewNode]
250
267
  def index_view_nodes(ref)
251
268
  ref.nodes.each do |node|
252
269
  @index_hash[node.id] = index_view_nodes(node)
@@ -255,11 +272,6 @@ module Archimate
255
272
  ref
256
273
  end
257
274
 
258
- def random_id
259
- @random ||= Random.new
260
- format("%08x", @random.rand(0xffffffff))
261
- end
262
-
263
275
  def register(node, _parent)
264
276
  @index_hash[node.id] = node
265
277
  end
@@ -48,14 +48,30 @@ module Archimate
48
48
  "#{Archimate::Color.data_model('Organization')}<#{id}>[#{Archimate::Color.color(name, %i[white underline])}]"
49
49
  end
50
50
 
51
- def referenced_identified_nodes
52
- organizations.reduce(items) do |a, e|
53
- a.to_ary + e.referenced_identified_nodes
51
+ def remove(id)
52
+ items.delete_if { |item| item.id == id }
53
+ end
54
+
55
+ def remove_reference(item)
56
+ super
57
+ case item
58
+ when Organization
59
+ organizations.delete(item)
60
+ else
61
+ items.delete(item)
62
+ organizations.each { |org| org.remove_reference(item) }
54
63
  end
55
64
  end
56
65
 
57
- def remove(id)
58
- items.delete_if { |item| item.id == id }
66
+ def replace_item_with(item, replacement)
67
+ case item
68
+ when Organization
69
+ organizations.delete(item)
70
+ organizations << replacement
71
+ else
72
+ items.delete(item)
73
+ items << replacement
74
+ end
59
75
  end
60
76
  end
61
77
  end
@@ -21,12 +21,6 @@ module Archimate
21
21
  def key
22
22
  property_definition.name
23
23
  end
24
-
25
- def brief_inspect
26
- cls_name = self.class.name.split('::').last
27
- val = "#{value.to_s.slice(0, 40).inspect}#{'...' if value.to_s.size > 40}"
28
- "#<#{cls_name} #{key} = #{val}>"
29
- end
30
24
  end
31
25
  end
32
26
  end