jekyll-archimate 0.2.2 → 0.2.3

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