archimate 2.0.2 → 2.0.3

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