jekyll-archimate 0.2.2 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 28f21df623d8b7517b7cb96f6afa8d2447a70d20
4
- data.tar.gz: 723393f13b2337fbc0ff24e4e743fd132735c7b3
3
+ metadata.gz: 450c76bef35c5a96c2dd7b52a7e8598ef0b3910a
4
+ data.tar.gz: 9506cf3a7a0a23f70ac2f4083be19f9ae25023b2
5
5
  SHA512:
6
- metadata.gz: 3d4dfa4d1e4ab5ac8dad5bb6290369053af032950183cf53cbf5a1727180ebe413b1572777da64ac65b21f7d87afa8edaf9c09016ee9e0e36e1efc4f17f8d088
7
- data.tar.gz: 256ae8198b5cd36510c13351f3fb30be571f0cd06be3513e24f59f784dc388e5b809ca71ba382f5d0de11bab22c5ff2f2064d17bf7b4dd3276976a1a2cbc4639
6
+ metadata.gz: ad3da00341c4db0c5329e8e23a50cdf1eac79c9f0fcbf36db9f85a6cfd8acc4171716a98539c77f69183cc27053f2d28b2965a0ab3c43c0cd4674dc8bdc2bd21
7
+ data.tar.gz: 79ac5eae532d4709e7fe08de80acea38649c8f3c95ae8d5029a565648ef1b5a442681b888c91ae00069e15bed420d7258e0a29c5ded297e54b82a9730b691bc5
data/.gitignore CHANGED
@@ -8,3 +8,4 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  *.sublime*
11
+ docs/
@@ -1,8 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "jekyll"
4
+ require "archimate"
5
+
6
+ module Jekyll
7
+ module Archimate
8
+ # Removes all keys that have null or empty values
9
+ def self.hash_purge(hash)
10
+ hash.delete_if { |_, value| !value || (value.is_a?(String) && value.empty?) }
11
+ end
12
+ end
13
+ end
2
14
 
3
15
  require "jekyll/archimate/version"
4
16
  require "jekyll/archimate/archimate_cache"
5
17
  require "jekyll/archimate/archimate_diagram_tag"
6
18
  require "jekyll/archimate/catalog_tag"
7
19
  require "jekyll/archimate/application_interaction_matrix_tag"
8
- require "jekyll/archimate/archimate_hook"
20
+ require "jekyll/archimate/conditional_file"
21
+ require "jekyll/archimate/archimate_index_generator"
22
+ require "jekyll/archimate/archimate_svg_generator"
23
+ require "jekyll/archimate/entity_base"
24
+ require "jekyll/archimate/model_entity"
25
+ require "jekyll/archimate/element_entity"
26
+ require "jekyll/archimate/relationship_entity"
27
+ require "jekyll/archimate/diagram_entity"
28
+ require "jekyll/archimate/folder"
29
+ require "jekyll/archimate/unified_model"
30
+ require "jekyll/archimate/archimate_generator"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Jekyll
2
4
  module Archimate
3
5
  # Insert a diagram from the ArchiMate model.
@@ -5,12 +7,13 @@ module Jekyll
5
7
  # {% application_interaction_matrix plateau:"Today" | caption: "Today's Application Interaction" }
6
8
  #
7
9
  class ApplicationInteractionMatrixTag < Liquid::Tag
10
+ EMPTY_CELL = "<td></td>"
11
+
8
12
  attr_reader :context
9
13
  attr_reader :markup
10
14
  attr_reader :caption
11
15
  attr_reader :plateau
12
16
 
13
-
14
17
  def initialize(tag_name, markup, tokens)
15
18
  @markup = markup
16
19
  @context = nil
@@ -34,27 +37,26 @@ module Jekyll
34
37
  # target_selector: Element selector for target elements
35
38
  # relationship_selector
36
39
  def application_interaction
37
- model = site.data["archimate_model"]
40
+ model = ArchimateCache.instance.model
38
41
  dr_engine = ::Archimate::DerivedRelations.new(model)
39
42
 
40
- relationship_filter = lambda { |rel| rel.weight >= ::Archimate::DataModel::Serving::WEIGHT }
43
+ relationship_filter = ->(rel) { rel.weight >= ::Archimate::DataModel::Serving::WEIGHT }
41
44
 
42
45
  plateau_today = dr_engine.element_by_name(plateau)
43
46
  today_rels = model.relationships.select do |rel|
44
47
  rel.source.id == plateau_today.id &&
45
- %w{CompositionRelationship AggregationRelationship}.include?(rel.type) &&
48
+ %w[CompositionRelationship AggregationRelationship].include?(rel.type) &&
46
49
  rel.target.type == "ApplicationComponent"
47
50
  end
48
- today_apps = today_rels.map { |rel| rel.target }
49
- source_filter = lambda { |el| }
50
- target_filter = lambda { |el| today_apps.map(&:id).include?(el.id) }
51
- stop_filter = lambda { |el| el.type == "ApplicationComponent" }
51
+ today_apps = today_rels.map(&:target)
52
+ target_filter = ->(el) { today_apps.map(&:id).include?(el.id) }
53
+ stop_filter = ->(el) { el.type == "ApplicationComponent" }
52
54
 
53
- concrete_rels = model.relationships.select { |rel|
55
+ concrete_rels = model.relationships.select do |rel|
54
56
  rel.type == "ServingRelationship" &&
55
57
  today_apps.include?(rel.source.id) &&
56
58
  today_apps.include?(rel.target.id)
57
- }
59
+ end
58
60
 
59
61
  derived_rels = dr_engine.derived_relations(
60
62
  today_apps,
@@ -69,61 +71,52 @@ module Jekyll
69
71
  @callees = @all_rels.map(&:target).uniq.sort { |a, b| a.name.to_s <=> b.name.to_s }
70
72
  end
71
73
 
72
- def matrix_data
73
- model = site.data["archimate_model"]
74
- dr_engine = ::Archimate::DerivedRelations.new(model)
75
-
76
- end
77
-
78
74
  def render_table
79
- <<~END
80
- <table class="table table-condensed table-hover table-striped">
81
- <caption>#{caption}</caption>
82
- <thead>
83
- <tr>
84
- <th>&nbsp;</th>
85
- <th class="success" scope="col" colspan="#{@callers.size}">Callers</th>
86
- </tr>
87
- <tr>
88
- <th class="info" scope="col">Callees</th>
89
- #{@callers.map { |ac| "<th class=\"success\" scope=\"col\" style=\"text-transform: capitalize\">#{ac.name}</th>" }.join("\n")}
90
- </tr>
91
- </thead>
92
- <tbody>
93
- #{render_rows.strip}
94
- </tbody>
95
- </table>
96
- END
75
+ <<~TABLE
76
+ <table class="table table-condensed table-hover table-striped">
77
+ <caption>#{caption}</caption>
78
+ <thead>
79
+ <tr>
80
+ <th>&nbsp;</th>
81
+ <th class="success" scope="col" colspan="#{@callers.size}">Callers</th>
82
+ </tr>
83
+ <tr>
84
+ <th class="info" scope="col">Callees</th>
85
+ #{@callers.map { |ac| "<th class=\"success\" scope=\"col\" style=\"text-transform: capitalize\">#{ac.name}</th>" }.join("\n")}
86
+ </tr>
87
+ </thead>
88
+ <tbody>
89
+ #{render_rows.strip}
90
+ </tbody>
91
+ </table>
92
+ TABLE
97
93
  end
98
94
 
99
95
  def render_rows
100
96
  return "<tr><td>No Items</td></tr>" if @callees.empty?
101
97
  @callees.map do |callee|
102
- <<~END
103
- <tr>
104
- <th class="info" scope="row">#{callee.name}</th>
105
- #{@callers.map { |caller| cell_content(caller, callee) }.join("")}
106
- </tr>
107
- END
98
+ <<~TABLE_ROW
99
+ <tr>
100
+ <th class="info" scope="row">#{callee.name}</th>
101
+ #{@callers.map { |caller| cell_content(caller, callee) }.join('')}
102
+ </tr>
103
+ TABLE_ROW
108
104
  end.join("")
109
105
  end
110
106
 
111
107
  def cell_content(caller, callee)
112
108
  rels = @all_rels.select { |rel| rel.source == caller && rel.target == callee }
113
- if rels.empty?
114
- "<td></td>"
115
- else
116
- derived = rels.all? { |rel| rel.derived }
117
- span_class = derived ? "text-danger" : "text-primary"
118
- tooltip = "#{caller.name} &rarr; #{}#{callee.name} #{"(derived)" if derived}"
119
- cell = <<~END
109
+ return EMPTY_CELL if rels.empty?
110
+ derived = rels.all?(&:derived)
111
+ span_class = derived ? "text-danger" : "text-primary"
112
+ tooltip = "#{caller.name} &rarr; #{callee.name} #{'(derived)' if derived}"
113
+ <<~TABLE_CELL
120
114
  <td>
121
115
  <a href="#" data-toggle="tooltip" data-placement="top" title="#{tooltip}">
122
116
  <span class="#{span_class}">&crarr; calls</span>
123
117
  </a>
124
118
  </td>
125
- END
126
- end
119
+ TABLE_CELL
127
120
  end
128
121
 
129
122
  def scan_attributes(context)
@@ -147,4 +140,3 @@ module Jekyll
147
140
  end
148
141
 
149
142
  Liquid::Template.register_tag("application_interaction_matrix", Jekyll::Archimate::ApplicationInteractionMatrixTag)
150
-
@@ -1,75 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "archimate"
4
-
5
3
  module Jekyll
6
4
  module Archimate
7
- module ArchimateCache
8
- # load an archimate model from either...
9
- # 1. Memory
10
- # 2. Cached & Marshaled File
11
- # 3. Archimate File
12
- # Defaulting to the Archimate file if it is newer than either cached version
13
- def load_model(file_path)
14
- @@cache ||= {}
15
- file_path.sub!(%r{^/}, "")
16
- mod_time = File.mtime(file_path)
17
-
18
- if @@cache.key?(file_path)
19
- model_info = @@cache[file_path]
20
- return model_info[:model] if model_info[:cache_time] >= mod_time
21
- end
5
+ ArchimateFileCacheInfo = Struct.new(:archimate_file, :model)
22
6
 
23
- cache_file = marshal_cache_file(file_path)
24
- if File.exist?(cache_file) && File.mtime(cache_file) >= mod_time
25
- begin
26
- @@cache[file_path] = {
27
- cache_time: File.mtime(cache_file),
28
- model: File.open(cache_file, "rb") { |f| Marshal.load(f) }
29
- }
30
- return @@cache[file_path][:model]
31
- rescue ArgumentError
32
- puts "Cache file is invalid - removing cache #{cache_file} and loading #{file_path}"
33
- FileUtils.rm cache_file
34
- end
35
- end
7
+ class ArchimateCache
8
+ include Singleton
36
9
 
37
- model = ::Archimate.read(file_path)
38
- File.open(cache_file, "wb") { |f| Marshal.dump(model, f) }
39
- @@cache[file_path] = {
40
- cache_time: File.mtime(cache_file),
41
- model: model
42
- }
43
-
44
- model
10
+ def initialize
11
+ @cache = {}
12
+ @default_archimate_file = nil
45
13
  end
46
- module_function :load_model
47
14
 
48
- def rel_path(file_path)
49
- file_path.sub!(%r{^/}, "")
15
+ def cache_valid?(archimate_file)
16
+ path = archimate_file.path
17
+ @cache.key?(path) &&
18
+ @cache[path].archimate_file.modified_time.to_i == archimate_file.modified_time.to_i
50
19
  end
51
20
 
52
- module_function :rel_path
21
+ def model(archimate_file = nil)
22
+ archimate_file ||= default_archimate_file
23
+ raise "No ArchiMate file has been detected." unless archimate_file
24
+ update_cache(archimate_file) unless cache_valid?(archimate_file)
25
+ @cache[archimate_file.path].model
26
+ end
53
27
 
54
- def cache_stale?(file_path)
55
- @@cache ||= {}
56
- rel = rel_path(file_path)
57
- mod_time = File.mtime(rel)
58
- !@@cache.key?(rel) ||
59
- @@cache[rel][:cache_time] < mod_time
28
+ def default_archimate_file
29
+ @default_archimate_file || @cache.keys.first
60
30
  end
61
- module_function :cache_stale?
62
31
 
63
- def marshal_cache_file(path)
64
- file_path, file_name = File.split(path)
65
- cache_path = File.join("_cache", file_path).split("/").inject("") do |cpath, rel_dir|
66
- npath = cpath.empty? ? rel_dir : File.join(cpath, rel_dir)
67
- Dir.mkdir(File.absolute_path(npath)) unless Dir.exist?(File.absolute_path(npath))
68
- npath
32
+ def default_archimate_file=(archimate_file)
33
+ unless @cache.key?(archimate_file.path)
34
+ raise(
35
+ "Default ArchiMate file does not exist in cache: #{archimate_file.relative_path}"
36
+ )
69
37
  end
70
- File.join(cache_path, File.basename(file_name, ".archimate" + ".marshal"))
38
+ @default_archimate_file = archimate_file
39
+ end
40
+
41
+ def update_cache(archimate_file)
42
+ # update the cache
43
+ Jekyll.logger.info " loading ArchiMate #{archimate_file.relative_path}"
44
+ load_start_time = Time.new
45
+ model = ::Archimate.read(archimate_file.path)
46
+ load_finish_time = Time.new
47
+ Jekyll.logger.info format(" %.1f seconds", (load_finish_time - load_start_time))
48
+ @cache[archimate_file.path] = ArchimateFileCacheInfo.new(archimate_file, model)
71
49
  end
72
- module_function :marshal_cache_file
73
50
  end
74
51
  end
75
52
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Jekyll
2
4
  module Archimate
3
5
  # Insert a diagram from the ArchiMate model.
@@ -21,12 +23,12 @@ module Jekyll
21
23
  # TODO: make the archimate_dir configurable in _config.yml and as an
22
24
  # optional argument in the tag.
23
25
  archimate_dir = [baseurl, "archimate", "svg"].join("/")
24
- <<~END
26
+ <<~FIGURE
25
27
  <figure id="#{@diagram_id}">
26
- <a href="#{baseurl}/archimate/svg/#{@diagram_id}.svg" alt="View Full Screen">
28
+ <a href="#{archimate_dir}/#{@diagram_id}.svg" alt="View Full Screen">
27
29
  <span class="glyphicon glyphicon-fullscreen" style="float:right"></span>
28
30
  </a>
29
- <img src="#{baseurl}/archimate/svg/#{@diagram_id}.svg" class="img-responsive" alt="#{@caption}">
31
+ <img src="#{archimate_dir}/#{@diagram_id}.svg" class="img-responsive" alt="#{@caption}">
30
32
  <figcaption>
31
33
  #{@caption}
32
34
  <br/>
@@ -35,7 +37,7 @@ module Jekyll
35
37
  </a>
36
38
  </figcaption>
37
39
  </figure>
38
- END
40
+ FIGURE
39
41
  end
40
42
  end
41
43
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Archimate
5
+ class ArchimateGenerator < Jekyll::Generator
6
+ def generate(site)
7
+ Jekyll.logger.info "ArchimateGenerator.generate"
8
+ archimate_files = site.static_files.select { |static_file| static_file.extname =~ /\.(archimate|xml)$/ }
9
+ archimate_file = preload_cache(archimate_files)
10
+ return unless needs_generation?
11
+ ArchimateSvgGenerator.new(site, archimate_file).generate
12
+ ArchimateIndexGenerator.new(site, archimate_file).generate
13
+ end
14
+
15
+ private
16
+
17
+ def needs_generation?
18
+ cache.cache_valid?(cache.default_archimate_file)
19
+ end
20
+
21
+ def preload_cache(archimate_files)
22
+ archimate_files.each { |file| cache.model(file) }
23
+ cache.default_archimate_file = archimate_files.first # TODO: select file in the `archimate` directory
24
+ end
25
+
26
+ def cache
27
+ @cache ||= ArchimateCache.instance
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Archimate
5
+ class ArchimateIndexGenerator
6
+ attr_reader :site
7
+ attr_reader :archimate_file
8
+
9
+ def initialize(site, archimate_file)
10
+ @site = site
11
+ @archimate_file = archimate_file
12
+ @name = "index.json"
13
+ end
14
+
15
+ def generate
16
+ ConditionalFile.new(
17
+ site,
18
+ File.dirname(archimate_file.relative_path),
19
+ @name,
20
+ archimate_file
21
+ ).write(JSON.generate(UnifiedModel.new(model).to_h))
22
+ end
23
+
24
+ def model
25
+ @model ||= ArchimateCache.instance.model(archimate_file)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Archimate
5
+ class ArchimateSvgGenerator
6
+ attr_reader :site
7
+ attr_reader :archimate_file
8
+
9
+ def initialize(site, archimate_file)
10
+ @site = site
11
+ @archimate_file = archimate_file
12
+ end
13
+
14
+ def generate
15
+ create_svg_source_path_if_needed
16
+ model.diagrams.each do |diagram|
17
+ generate_diagram(diagram)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def model
24
+ @model ||= ArchimateCache.instance.model(archimate_file)
25
+ end
26
+
27
+ def svg_source_path
28
+ @svg_source_path ||= File.join(
29
+ site.source,
30
+ File.dirname(archimate_file.relative_path),
31
+ 'svg'
32
+ )
33
+ end
34
+
35
+ def svg_relative_path
36
+ @svg_relative_path ||= File.join(File.dirname(archimate_file.relative_path), "svg")
37
+ end
38
+
39
+ def create_svg_source_path_if_needed
40
+ Dir.mkdir(svg_source_path) unless Dir.exist?(svg_source_path)
41
+ end
42
+
43
+ def generate_diagram(diagram)
44
+ ConditionalFile.new(
45
+ site,
46
+ svg_relative_path,
47
+ "#{diagram.id}.svg",
48
+ archimate_file
49
+ ).write(::Archimate::Svg::Diagram.new(diagram).to_svg)
50
+ end
51
+ end
52
+ end
53
+ end