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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 4adaa55a050dfda4d29150a63c15860a21f50932
4
- data.tar.gz: 78c4c98ef53b79704018ef5b761377ac01e4497d
2
+ SHA256:
3
+ metadata.gz: 24983cbe0a25eb6b4fdd4592faf9d424f3f6dab8a0a28495cfa0c79f81bf6f18
4
+ data.tar.gz: 91796ec52b8680a1449c8a9e424894e786191a82b2e96679ca767dee69ffa04a
5
5
  SHA512:
6
- metadata.gz: ad77b807f9800be3bd0b4cde9a8dffa00161b94925aa2fdaf7519bfbcd80a71a63acc80ef294647b6d442cbb92b41113c50de10397bd7b9831a52acfb5d35a38
7
- data.tar.gz: ef2e775c6067f3a9b097c853083d687af9289ce28f68d7d36a166257e2572af06089bab38513f4c7661cb0d30810bba271f89a4be82b0724beeafa46e225b66a
6
+ metadata.gz: 6b3d2698127d1aad4f6f729ef1735322658670e8527ab0d7a746ca23c9c7d77669ccb9751d731a4efe431355bf376904237f3a85fd60edf260f252a411a1a6be
7
+ data.tar.gz: 530ce2ba4cb376a6120fcb2c80c7aab6f2eed57c02a6dff21825de3018079c3714e39fec69728bc6dd38bbc0e8433e87d9c9d9fd458c9a4914738af2ad1cb0c5
data/.travis.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.3.1
3
+ - 2.4.2
4
4
  before_install: gem install bundler -v 1.14.5
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  source 'https://rubygems.org'
3
4
 
4
5
  # Specify your gem's dependencies in archimate.gemspec
data/README.md CHANGED
@@ -26,62 +26,17 @@ Or install it yourself as:
26
26
 
27
27
  The example scripts are (some are planned):
28
28
 
29
- command | description
30
- ------------- | -----------
31
- `archimate help [COMMAND]` | Describe available commands or one specific command
29
+ command | description
30
+ ----------------------------- | -----------
31
+ `archimate clean ARCHIFILE` | Clean up unreferenced elements and relations
32
32
  `archimate convert ARCHIFILE` | Convert the incoming file to the desired type
33
- `archimate dedupe ARCHIFILE` | de-duplicate elements in Archi file
34
- `archimate map ARCHIFILE` | *EXPERIMENTAL:* Produce a map of diagram links to a diagram
35
- `archimate merge ARCHIFILE1 ARCHIFILE2` | *EXPERIMENTAL:*Merge two archimate files
36
- `archimate project ARCHIFILE PROJECTFILE` | *EXPERIMENTAL:*Synchronize an Archi file and an MSProject XML file
37
- `archimate svg ARCHIFILE` | Produce semantically meaningful SVG files from an Archi file
38
- `archimate lint ARCHIFILE` | Produce a report of warnings and issues in the Archi file
39
-
40
- ### archidiff & archimerge
41
-
42
- Archidiff is a set of tools to help with versioning an `.archimate` file from Archi in a version control system (like git). Eventually I want to provide diff and (3-way) merge tools that understand how *Archi* files are structured and avoid problems that happen when multiple people collaborate on a model.
43
-
44
- To enable using these from the command line git, add these lines to your `~/.gitconfig` replacing `{PATH_TO_ARCHIDIFF}` with the path to the archidiff binaries.
45
-
46
- ```
47
- [difftool "archidiff"]
48
- cmd = {PATH_TO_ARCHIDIFF}/archidiff $LOCAL $REMOTE
49
-
50
- [difftool "archidiff-summary"]
51
- cmd = {PATH_TO_ARCHIDIFF}/archidiff-summary $LOCAL $REMOTE
52
-
53
- [mergetool "archimerge"]
54
- cmd = {PATH_TO_ARCHIDIFF}/archimerge $PWD/$BASE $PWD/$REMOTE $PWD/$LOCAL $PWD/$MERGED
55
- trustExitCode = false
56
- ```
57
-
58
- Then to use the tool for diffing you can do this:
59
-
60
- ```sh
61
- git difftool --tool archidiff 833cbb7 HEAD -- path_to/my.archimate
62
- ```
63
-
64
- or to see a summary of what changed between versions:
65
-
66
- ```sh
67
- git difftool --tool archidiff-summary 833cbb7 HEAD -- path_to/my.archimate
68
- ```
69
-
70
- Finally, if you have a merge conflict, you can use archimerge to help make the merge sane:
71
-
72
- ```sh
73
- git mergetool --tool archimerge -- path_to/my.archimate
74
- ```
75
-
76
- ### fmtxml
77
-
78
- Can be used as a `textconv` filter in .gitconfig to pre-format files for better diff use (for visual scanning). You'd set this up in your `$HOME/.gitconfig` file like this.
79
-
80
- ```
81
- [diff "archimate"]
82
- textconv = fmtxml
83
- cachetextconv = true
84
- ```
33
+ `archimate dedupe ARCHIFILE` | de-duplicate elements in Archi file
34
+ `archimate dupes ARCHIFILE` | List (potential) duplicate elements in Archi file
35
+ `archimate help [COMMAND]` | Describe available commands or one specific command
36
+ `archimate lint ARCHIFILE` | Examine the ArchiMate file for potential problems
37
+ `archimate map ARCHIFILE` | Produce a map of diagram links to a diagram
38
+ `archimate stats ARCHIFILE` | Show some statistics about the model
39
+ `archimate svg -o OUTPUTDIR ARCHIFILE o, --output=OUTPUT` | Produce semantically meaningful SVG files from an Archi file
85
40
 
86
41
  ## Development
87
42
 
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
- # frozen_string_literal: true
1
+ # frozen_string_literal: false
2
+
2
3
  require "bundler/gem_tasks"
3
4
  require "rake/testtask"
4
5
  require "yard"
@@ -9,6 +10,23 @@ Rake::TestTask.new(:test) do |t|
9
10
  t.test_files = FileList['test/**/*_test.rb']
10
11
  end
11
12
 
13
+ namespace :test do
14
+ desc "Run only integration tests."
15
+ Rake::TestTask.new(:integration) do |t|
16
+ t.libs << "test"
17
+ t.libs << "lib"
18
+ t.test_files = FileList['test/integration/**/*_test.rb']
19
+ end
20
+
21
+ desc "Run tests and report slowest tests."
22
+ Rake::TestTask.new(:profile) do |t|
23
+ t.libs << "test"
24
+ t.libs << "lib"
25
+ t.options = "--profile"
26
+ t.test_files = FileList['test/**/*_test.rb']
27
+ end
28
+ end
29
+
12
30
  YARD::Rake::YardocTask.new do |t|
13
31
  t.options += ['--title', "YARD #{YARD::VERSION} Documentation"]
14
32
  end
data/archimate.gemspec CHANGED
@@ -21,32 +21,34 @@ Gem::Specification.new do |spec|
21
21
  spec.metadata["yard.run"] = "yri" # use "yard" to build full HTML docs.
22
22
  spec.required_ruby_version = '>= 2.3.0'
23
23
 
24
- spec.add_runtime_dependency "nokogiri", "~> 1.6"
25
- spec.add_runtime_dependency "thor", "~> 0.19"
26
24
  spec.add_runtime_dependency "highline", "~> 1.7"
27
- spec.add_runtime_dependency "ruby-progressbar", "~>1.8.1"
25
+ spec.add_runtime_dependency "nokogiri", "~> 1.6"
28
26
  spec.add_runtime_dependency "parallel", "~> 1.11"
29
27
  spec.add_runtime_dependency "ruby-enum", "~> 0.7.1"
28
+ spec.add_runtime_dependency "ruby-progressbar", "~>1.8.1"
29
+ spec.add_runtime_dependency "thor", "~> 0.19"
30
30
 
31
+ spec.add_development_dependency "awesome_print"
31
32
  spec.add_development_dependency "bundler"
32
- spec.add_development_dependency "rake"
33
+ spec.add_development_dependency "faker"
34
+ spec.add_development_dependency "kramdown"
33
35
  spec.add_development_dependency "minitest"
34
- spec.add_development_dependency "minitest-matchers"
35
36
  spec.add_development_dependency "minitest-color"
37
+ spec.add_development_dependency "minitest-matchers"
36
38
  spec.add_development_dependency "minitest-profile"
37
39
  spec.add_development_dependency "pry"
38
- spec.add_development_dependency "pry-byebug"
40
+ spec.add_development_dependency "rake"
41
+ spec.add_development_dependency "rubocop"
42
+ spec.add_development_dependency "yard"
43
+
44
+ # TODO: These should be in an MRI block
39
45
  spec.add_development_dependency "guard"
40
- spec.add_development_dependency "guard-minitest"
41
46
  spec.add_development_dependency "guard-bundler"
47
+ spec.add_development_dependency "guard-ctags-bundler"
48
+ spec.add_development_dependency "guard-minitest"
49
+ spec.add_development_dependency "pry-byebug"
50
+ spec.add_development_dependency "rsense"
42
51
  spec.add_development_dependency "ruby-prof"
43
52
  spec.add_development_dependency "simplecov"
44
53
  spec.add_development_dependency "simplecov-json"
45
- spec.add_development_dependency "kramdown"
46
- spec.add_development_dependency "yard"
47
- spec.add_development_dependency "guard-ctags-bundler"
48
- spec.add_development_dependency "faker"
49
- spec.add_development_dependency "rsense"
50
- spec.add_development_dependency "awesome_print"
51
- spec.add_development_dependency "rubocop"
52
54
  end
data/bin/archimate CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
+
3
4
  #
4
5
  # This file was generated by Bundler.
5
6
  #
@@ -11,6 +12,17 @@ require "pathname"
11
12
  ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
13
  Pathname.new(__FILE__).realpath)
13
14
 
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
14
26
  require "rubygems"
15
27
  require "bundler/setup"
16
28
 
@@ -48,7 +48,7 @@ module Archimate
48
48
  ).map
49
49
  end
50
50
 
51
- desc "svg -o OUTPUTDIR ARCHIFILE", "IN DEVELOPMENT: Produce semantically meaningful SVG files from an Archi file"
51
+ desc "svg -o OUTPUTDIR ARCHIFILE", "Produce semantically meaningful SVG files from an Archi file"
52
52
  option :output,
53
53
  aliases: :o,
54
54
  required: true,
@@ -58,39 +58,53 @@ module Archimate
58
58
  type: :boolean,
59
59
  default: false,
60
60
  desc: "Don't provide interactive feedback"
61
+ option :name,
62
+ type: :boolean,
63
+ default: false,
64
+ desc: "Use diagram name for filename (default: diagram id)"
61
65
  def svg(archifile)
62
66
  Config.instance.interactive = !options.fetch("noninteractive", false)
63
67
  Archimate::Cli::Svger.export_svgs(
64
68
  archifile,
65
- options.fetch("output", Dir.pwd)
69
+ options.fetch("output", Dir.pwd),
70
+ options["name"] ? :name : :id
66
71
  )
67
72
  end
68
73
 
69
74
  desc "clean ARCHIFILE", "Clean up unreferenced elements and relations"
70
75
  option :output,
71
76
  aliases: :o,
72
- desc: "Write output to FILE instead of replacing ARCHIFILE"
77
+ desc: "Write output to OUTPUT instead of replacing ARCHIFILE"
73
78
  option :saveremoved,
74
79
  aliases: :r,
75
- desc: "Write removed elements into FILE"
80
+ desc: "Write removed elements into SAVEREMOVED"
76
81
  option :noninteractive,
77
82
  aliases: :n,
78
83
  type: :boolean,
79
84
  default: false,
80
85
  desc: "Don't provide interactive feedback"
86
+ option :force,
87
+ aliases: :f,
88
+ type: :boolean,
89
+ default: false,
90
+ desc: "Force overwriting of existing output file"
81
91
  def clean(archifile)
82
92
  Config.instance.interactive = !options.fetch("noninteractive", false)
83
- outfile = options.key?(:output) ? options[:output] : archifile
93
+ output_io = Cli.output_io(
94
+ options.fetch("output", archifile),
95
+ options.fetch("force", false)
96
+ )
84
97
  Archimate::MaybeIO.new(options.fetch(:saveremoved, nil)) do |removed_element_io|
85
- Archimate::Cli::Cleanup.new(Archimate.read(archifile), outfile, removed_element_io)
98
+ Archimate::Cli::Cleanup.new(Archimate.read(archifile), output_io, removed_element_io).clean
86
99
  end
100
+ output_io.close
87
101
  end
88
102
 
89
103
  desc "dupes ARCHIFILE", "List (potential) duplicate elements in Archi file"
90
104
  def dupes(archifile)
91
105
  Archimate::Cli::Duper.new(
92
- archimate.read(archifile),
93
- STDOUT
106
+ Archimate.read(archifile),
107
+ $stdout
94
108
  ).list
95
109
  end
96
110
 
@@ -115,27 +129,32 @@ module Archimate
115
129
  desc: "Don't provide interactive feedback"
116
130
  def dedupe(archifile)
117
131
  Config.instance.interactive = !options.fetch("noninteractive", false)
132
+ output_io = Cli.output_io(
133
+ options.fetch("output", archifile),
134
+ options[:force]
135
+ )
118
136
  Archimate::Cli::Duper.new(
119
137
  Archimate.read(archifile),
120
- Cli.output_io(
121
- options.fetch("output", archifile),
122
- options[:force]
123
- ),
138
+ output_io,
124
139
  options[:mergeall]
125
140
  ).merge
141
+ output_io.close
126
142
  end
127
143
 
128
144
  desc "convert ARCHIFILE", "Convert the incoming file to the desired type"
145
+ long_desc <<~CONVERT_DESC
146
+ Converts the input ArchiMate file into another format. Options are:
147
+ \x5 • 'archi' for Archi file format (http://archimatetool.com),
148
+ \x5 • 'meff2.1' for Open Group Model Exchange File Format for ArchiMate 2.1,
149
+ \x5 • 'nquads' for RDF 1.1 N-Quads format https://www.w3.org/TR/n-quads/,
150
+ \x5 • 'graphml' for GraphML,
151
+ \x5 • 'csv' for CSV files (one file per element/relationship type)
152
+ CONVERT_DESC
129
153
  option :to,
130
154
  aliases: :t,
131
155
  default: Archimate::Cli::Convert::SUPPORTED_FORMATS.first,
132
- desc: "File type to convert to. Options are: " \
133
- "'meff2.1' for Open Group Model Exchange File Format for ArchiMate 2.1 " \
134
- "'archi' for Archi http://archimatetool.com/ file format " \
135
- "'nquads' for RDF 1.1 N-Quads format https://www.w3.org/TR/n-quads/" \
136
- "'graphml' for GraphML" \
137
- "'csv' for CSV files (one file per element/relationship type",
138
- enum: Archimate::Cli::Convert::SUPPORTED_FORMATS
156
+ enum: Archimate::Cli::Convert::SUPPORTED_FORMATS,
157
+ desc: "Output format to convert to"
139
158
  option :output,
140
159
  aliases: :o,
141
160
  desc: "Write output to FILE instead of stdout."
@@ -1,52 +1,68 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "nokogiri"
3
4
 
4
5
  #
5
6
  # The point of this script is to identify elements that aren't a part of any
6
7
  # relationship and not referenced on any diagrams.
7
-
8
8
  module Archimate
9
9
  module Cli
10
10
  class Cleanup
11
11
  attr_reader :model
12
12
 
13
- def self.cleanup(input, output, options)
14
- cleaner = new(Archimate.read(input), output, options)
15
- cleaner.clean
16
- end
17
-
18
- def initialize(model, output, options)
13
+ def initialize(model, output, removed_items_io)
19
14
  @model = model
20
15
  @output = output
21
- @options = options
22
- @trash = Nokogiri::XML::Document.new("<deleted></deleted>")
16
+ @removed_items_io = removed_items_io
23
17
  @model_set = nil
24
- @progressbar = ProgressIndicator.new(total: model.unreferenced_nodes.size, title: "Elements")
18
+ @progressbar = ProgressIndicator.new(total: unreferenced_nodes.size, title: "Unreferenced Elements and Relationships")
19
+ @unref_set = []
25
20
  end
26
21
 
27
- def remove_unreferenced_nodes
28
- model.unreferenced_nodes.each do |unreferenced_node|
29
- raise "This functionality is not implemeted yet"
22
+ # detects elements not referenced by a relationship or a diagram
23
+ def unreferenced_elements
24
+ model
25
+ .elements
26
+ .select { |el| el.references.none?(&ref_is_relationship_or_diagram) }
27
+ end
30
28
 
31
- # TODO: this needs to be the XML serialization of the node
32
- trash.root.add_child unreferenced_node.dup
33
- # TODO: implement this
34
- # model.delete(unreferenced_node)
35
- @progressbar.increment
36
- end
29
+ def unreferenced_relationships
30
+ model
31
+ .relationships
32
+ .select { |rel| rel.references.none?(&ref_is_relationship_or_diagram) }
37
33
  end
38
34
 
39
- def write_trash
40
- options[:saveremoved].write(trash)
35
+ def unreferenced_nodes
36
+ unreferenced_relationships + unreferenced_elements
37
+ end
38
+
39
+ def destroy_nodes(nodes)
40
+ nodes.each do |unreferenced_node|
41
+ unreferenced_node.destroy
42
+ @removed_items_io.write(Color.uncolor(unreferenced_node.to_s) + "\n")
43
+ @unref_set << unreferenced_node
44
+ @progressbar.increment
45
+ end
41
46
  end
42
47
 
43
48
  def clean
44
49
  return unless model
45
50
 
46
- remove_unreferenced_nodes
47
- puts "Found #{unref_set.size} model items unreferenced by diagram or relationships"
48
- Archimate::ArchiFileWriter.write(model, output)
49
- write_trash
51
+ rels = unreferenced_relationships
52
+ $stdout.puts "Unreferenced Relationships: #{rels.size}"
53
+ destroy_nodes rels
54
+ els = unreferenced_elements
55
+ $stdout.puts "Unreferenced Elements: #{els.size}"
56
+ destroy_nodes els
57
+
58
+ $stdout.puts "Found #{@unref_set.size} model items unreferenced by diagram or relationships"
59
+ Archimate::FileFormats::ArchiFileWriter.write(model, @output)
60
+ end
61
+
62
+ private
63
+
64
+ def ref_is_relationship_or_diagram
65
+ ->(ref) { ref.is_a?(DataModel::Relationship) || ref.is_a?(DataModel::Diagram) }
50
66
  end
51
67
  end
52
68
  end
@@ -33,7 +33,6 @@ module Archimate
33
33
  handle_duplicate(element_type, name, entities)
34
34
  end
35
35
 
36
- # TODO: make the output writer be an argument
37
36
  Archimate::FileFormats::ArchiFileWriter.new(@model).write(@output)
38
37
  end
39
38
 
@@ -13,34 +13,57 @@ module Archimate
13
13
  @output = output_io
14
14
  end
15
15
 
16
+ def map
17
+ widths = compute_column_widths(process_diagrams(model.diagrams), HEADERS)
18
+ adjusted_widths = widths.inject(COL_DIVIDER.size * (HEADERS.size - 1), &:+)
19
+ header_row(widths, HEADERS)
20
+ organization_paths = build_organization_hash(model.organizations)
21
+ organization_paths.keys.sort.each do |organization_name|
22
+ diagrams = organization_paths[organization_name].select { |i| i.is_a?(DataModel::Diagram) }
23
+ next if diagrams.empty?
24
+ @output.puts(Color.color(format("%-#{adjusted_widths}s", organization_name), %i[bold green on_light_black]))
25
+ output_diagrams(process_diagrams(diagrams), widths)
26
+ end
27
+
28
+ @output.puts "\n#{model.diagrams.size} Diagrams"
29
+ end
30
+
31
+ private
32
+
16
33
  def header_row(widths, headers)
17
- titles = []
18
- widths.each_with_index { |w, i| titles << format("%-#{w}s", headers[i]) }
19
- @output.puts titles.map { |t| Color.color(t.capitalize, %i[bold blue]) }.join(Color.color(COL_DIVIDER, :light_black))
20
- @output.puts Color.color(widths.map { |w| "-" * w }.join("-+-"), :light_black)
34
+ titles = widths
35
+ .zip(headers)
36
+ .map(&header_cell_format)
37
+ .join(col_divider)
38
+ @output.puts titles
39
+ @output.puts header_border(widths)
40
+ end
41
+
42
+ def col_divider
43
+ Color.color(COL_DIVIDER, :light_black)
44
+ end
45
+
46
+ def header_border(widths)
47
+ Color.color(widths.map { |w| "-" * w }.join("-+-"), :light_black)
48
+ end
49
+
50
+ def header_cell_format
51
+ ->((width, header)) { Color.color(format("%-#{width}s", header).capitalize, %i[bold blue]) }
21
52
  end
22
53
 
23
54
  def process_diagrams(diagrams)
24
- diagrams.map { |e| [e.id, e.name, e.viewpoint, e.type] }.map do |row|
25
- row[2] = case row[3]
26
- when "canvas:CanvasModel"
27
- ["Canvas", row[4]].compact.join(": ")
28
- when "archimate:SketchModel"
29
- "Sketch"
30
- when "archimate:ArchimateDiagramModel"
31
- DataModel::ViewpointType.values[(row[2] || 0).to_i].to_s
32
- else
33
- row[3]
34
- end
35
- row[0] = Color.color("#{row[0]}.png", :underline)
36
- row
55
+ diagrams.map do |diagram|
56
+ [
57
+ "#{diagram.id}.png",
58
+ diagram.name.to_s,
59
+ diagram.viewpoint_description
60
+ ]
37
61
  end
38
62
  end
39
63
 
40
64
  def compute_column_widths(diagrams, headers)
41
- initial_widths = headers.map(&:size)
42
- diagrams.each_with_object(initial_widths) do |diagram, memo|
43
- diagram.slice(0, headers.size).each_with_index do |o, i|
65
+ diagrams.each_with_object(headers.map(&:size)) do |diagram, memo|
66
+ diagram.each_with_index do |o, i|
44
67
  memo[i] = !o.nil? && Color.uncolor(o).size > memo[i] ? Color.uncolor(o).size : memo[i]
45
68
  end
46
69
  memo
@@ -49,33 +72,23 @@ module Archimate
49
72
 
50
73
  def output_diagrams(diagrams, widths)
51
74
  diagrams.sort_by { |a| a[1] }.each do |m|
52
- row = []
53
- m.slice(0, widths.size).each_with_index { |c, i| row << format("%-#{widths[i]}s", c) }
54
- @output.puts row.join(Color.color(COL_DIVIDER, :light_black))
75
+ @output.puts(
76
+ widths
77
+ .zip(m)
78
+ .map { |(width, col)| format("%-#{width}s", col) }
79
+ .join(Color.color(COL_DIVIDER, :light_black))
80
+ )
55
81
  end
56
82
  end
57
83
 
58
84
  def build_organization_hash(organizations, parent = "", hash = {})
59
- organizations.each_with_object(hash) do |i, a|
85
+ organization_paths = organizations.each_with_object(hash) do |i, a|
60
86
  organization_path = [parent, i.name].join("/")
61
- a[organization_path] = i
87
+ a[organization_path] = i.items
62
88
  build_organization_hash(i.organizations, organization_path, a)
63
89
  end
64
- end
65
-
66
- def map
67
- widths = compute_column_widths(process_diagrams(model.diagrams), HEADERS)
68
- adjusted_widths = widths.inject(COL_DIVIDER.size * (HEADERS.size - 1), &:+)
69
- header_row(widths, HEADERS)
70
- organization_paths = build_organization_hash(model.organizations)
71
- organization_paths.keys.sort.each do |organization_name|
72
- diagrams = organization_paths[organization_name].items.map { |i| model.lookup(i) }.select { |i| i.is_a?(DataModel::Diagram) }
73
- next if diagrams.empty?
74
- @output.puts(Color.color(format("%-#{adjusted_widths}s", organization_name), %i[bold green on_light_black]))
75
- output_diagrams(process_diagrams(diagrams), widths)
76
- end
77
-
78
- @output.puts "\n#{model.diagrams.size} Diagrams"
90
+ organization_paths = { "/" => model.diagrams } if organization_paths.empty?
91
+ organization_paths
79
92
  end
80
93
  end
81
94
  end
@@ -2,21 +2,26 @@
2
2
 
3
3
  module Archimate
4
4
  module Cli
5
+ SVG_NAME_OPTION = [:id, :name] # TODO: add a way to export in folders with :folder_name]
6
+
5
7
  # This class is used to export SVG diagrams as defined in the given model
6
8
  class Svger
7
- def self.export_svgs(archi_file, output_dir)
8
- new(Archimate.read(archi_file).diagrams, output_dir).export_svgs
9
+ def self.export_svgs(archi_file, output_dir, name_option = :id)
10
+ new(Archimate.read(archi_file).diagrams, output_dir, name_option).export_svgs
9
11
  end
10
12
 
11
- def initialize(diagrams, output_dir)
13
+ def initialize(diagrams, output_dir, name_option = :id)
12
14
  @diagrams = diagrams
13
15
  @output_dir = output_dir
16
+ @name_option = name_option
17
+ @diagram_base_names = {}
14
18
  end
15
19
 
16
20
  def export_svgs
21
+ @diagram_base_names = {}
17
22
  progress = ProgressIndicator.new(total: @diagrams.size, title: "Writing SVGs")
18
23
  @diagrams.each do |diagram|
19
- export(diagram)
24
+ export(diagram, filename_for(diagram, @name_option))
20
25
  progress.increment
21
26
  end
22
27
  ensure
@@ -29,6 +34,30 @@ module Archimate
29
34
  svg_file.write(Svg::Diagram.new(diagram).to_svg)
30
35
  end
31
36
  end
37
+
38
+ # TODO: in case of duplicate name in a model, we need to extend the filename with a numeric or something.
39
+ def filename_for(diagram, name_option)
40
+ base_name = diagram_base_name(diagram, name_option)
41
+ if @diagram_base_names.include?(base_name)
42
+ idx = 2
43
+ while @diagram_base_names.include?(base_name + " - #{idx}") do
44
+ idx += 1
45
+ end
46
+ base_name = base_name + " - #{idx}"
47
+ end
48
+ base_name
49
+ end
50
+
51
+ def diagram_base_name(diagram, name_option)
52
+ case name_option
53
+ when :name
54
+ diagram.name
55
+ # when :folder_name
56
+ # diagram.name # TODO: add the path
57
+ else
58
+ diagram.id
59
+ end
60
+ end
32
61
  end
33
62
 
34
63
  def self.process_svg_filename(name)
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Archimate
4
+ module CoreRefinements
5
+ refine String do
6
+ # Converts a mixed case string to a snake case string
7
+ # @return String
8
+ def snake_case
9
+ gsub(/([A-Z])/, '_\1').downcase.sub(/^_/, "")
10
+ end
11
+
12
+ def to_method_name(suffix = nil)
13
+ [tr(' ', '_'), suffix]
14
+ .compact
15
+ .join("_")
16
+ .to_sym
17
+ end
18
+
19
+ # A helper that makes it easier to compare a string to a DataModel::LangString
20
+ # @param [String, DataModel::LangString] other string to compare
21
+ # @returns [Boolean]
22
+ def ==(other)
23
+ super(other.to_s)
24
+ end
25
+ end
26
+
27
+ refine Range do
28
+ # Returns midpoint of overlap between numeric ranges or nil if there
29
+ # is no overlap
30
+ # @param r [Range] a numeric range
31
+ # @return [Numeric] if the ranges overlap, the midpoint of the overlap
32
+ # @return [Nil] if the ranges do not overlap
33
+ def overlap_midpoint(r)
34
+ begin_max = [self, r].map(&:begin).max
35
+ end_min = [self, r].map(&:end).min
36
+ return nil if begin_max > end_min
37
+ (begin_max + end_min) / 2.0
38
+ end
39
+ end
40
+ end
41
+ end