kramdown-plantuml 1.1.1 → 1.1.5

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
  SHA256:
3
- metadata.gz: 488e793ad6c95b20619319fdec43866b7a96369321767eb415c441b2d8946c0c
4
- data.tar.gz: 2f9b7957b2b7d09ebe2758d1fc3f3600c7464f2ccee53d5ea11f04bf25b0df33
3
+ metadata.gz: 3968b5c182c334e7dbe020e7cd02309f6349fedeab4c6011b63178aa570b5c7f
4
+ data.tar.gz: 804e7e4c062c10f1f1c7578c338a8b19ec70a23031081a129f90dc44fa087926
5
5
  SHA512:
6
- metadata.gz: 4e4547033622f876ea2c63eb4af2f73433e57fa4cbe8bdd818bbcb5261f4bef829f50da55b9f5485023e5c040d7c260844f27fcf2a67f17858839aa4e9f0dab3
7
- data.tar.gz: cd38086b669bf27b64ccae97f45005a23a508fdfd94647e40c2f8e9960a89edc0abd2781c74fb42493c1b1df7bd5c0aaee6c19acd63cf20394d594c077cc072d
6
+ metadata.gz: ad3f371c0c4608c8d613ba9ec73414f6eea92c8eccf9a087fe7ccc487403b4742afc68c63574937aaf8ea30c9f90aaf56b7e5636764647b442137e7a683cbecd
7
+ data.tar.gz: c437216460a278794c2caeb20c151c5c2a98cce5d5a72e2a4d1b1fb077cc970ceb50fbb0fb3d832b92a7991d35b36776e019a295cbe9884ff928fee073dd9938
data/.github/mergify.yml CHANGED
@@ -17,6 +17,7 @@ pull_request_rules:
17
17
  - name: Thank contributor
18
18
  conditions:
19
19
  - merged
20
+ - -author~=^.*\[bot\]$
20
21
  actions:
21
22
  comment:
22
23
  message: "Thank you @{{author}} for your contribution!"
@@ -1,6 +1,73 @@
1
1
  #!/usr/bin/env bash
2
2
  set -o errexit # Abort if any command fails
3
3
 
4
- gem_build_name=$(gem build kramdown-plantuml.gemspec | awk '/File/ {print $2}')
5
- echo "Gem filename: '${gem_build_name}'"
6
- echo "::set-output name=name::${gem_build_name}"
4
+ me=$(basename "$0")
5
+ help_message="\
6
+ Usage:
7
+ ${me} [--ref <ref>] [--verbose]
8
+ ${me} --help
9
+ Arguments:
10
+ -r, --ref The Git reference that is being built.
11
+ -h, --help Displays this help screen.
12
+ -v, --verbose Increase verbosity. Useful for debugging."
13
+
14
+ parse_args() {
15
+ while : ; do
16
+ if [[ $1 = "-h" || $1 = "--help" ]]; then
17
+ echo "${help_message}"
18
+ return 0
19
+ elif [[ $1 = "-v" || $1 = "--verbose" ]]; then
20
+ verbose=true
21
+ shift
22
+ elif [[ $1 = "-r" || $1 = "--ref" ]]; then
23
+ ref=${2// }
24
+
25
+ if [[ "${ref}" = "--"* ]]; then
26
+ ref=""
27
+ shift 1
28
+ else
29
+ shift 2
30
+ fi
31
+ else
32
+ break
33
+ fi
34
+ done
35
+ }
36
+
37
+ # Echo expanded commands as they are executed (for debugging)
38
+ enable_expanded_output() {
39
+ if [ "${verbose}" = true ]; then
40
+ set -o xtrace
41
+ set +o verbose
42
+ export VERBOSE=true
43
+ fi
44
+ }
45
+
46
+ add_coverage() {
47
+ # If we're not building a tag, bootstrap code coverage.
48
+ if [[ "${ref}" != "refs/tags/"* ]]; then
49
+ [[ "${verbose}" = true ]] && echo "Bootstrapping code coverage."
50
+ export COVER=true
51
+ export COVERAGE=true
52
+ printf "require 'simplecov'\nSimpleCov.start\n" >> lib/kramdown-plantuml.rb
53
+ elif [[ "${verbose}" = true ]]; then
54
+ echo "Skipping coverage report since a tag ref was pushed."
55
+ fi
56
+ }
57
+
58
+ build_gem() {
59
+ gem_build_name=$(gem build kramdown-plantuml.gemspec | awk '/File/ {print $2}')
60
+
61
+ [[ "${verbose}" = true ]] && echo "Gem filename: '${gem_build_name}'"
62
+
63
+ echo "::set-output name=name::${gem_build_name}"
64
+ }
65
+
66
+ main() {
67
+ parse_args "$@"
68
+ enable_expanded_output
69
+ add_coverage
70
+ build_gem
71
+ }
72
+
73
+ main "$@"
@@ -6,7 +6,7 @@ me=$(basename "$0")
6
6
 
7
7
  help_message="\
8
8
  Usage:
9
- ${me} --workdir <workdir> [--gemdir <gemdir> | --version <version> --token <token>] [--verbose]
9
+ ${me} --workdir <workdir> [--gemdir <gemdir> | --version <version> --token <token>] [--verbose] [--theme <name> [--theme-directory <path>]]
10
10
  ${me} --help
11
11
  Arguments:
12
12
  -w, --workdir <workdir> The path to the working directory.
@@ -14,6 +14,8 @@ Arguments:
14
14
  -v, --version <version> The version of the Gem to test.
15
15
  -t, --token <token> The GitHub token to use for retrieving the gem
16
16
  from the GitHub Package Registry.
17
+ -T, --theme-name <name> The theme name to use for the test.
18
+ -p, --theme-directory <path> The directory in which the [--theme-name] is placed.
17
19
  -h, --help Displays this help screen.
18
20
  -v, --verbose Increase verbosity. Useful for debugging."
19
21
 
@@ -52,6 +54,24 @@ parse_args() {
52
54
  else
53
55
  shift 2
54
56
  fi
57
+ elif [[ $1 = "-T" || $1 = "--theme-name" ]]; then
58
+ theme_name=${2// }
59
+
60
+ if [[ "${theme_name}" = "--"* ]]; then
61
+ theme_name=""
62
+ shift 1
63
+ else
64
+ shift 2
65
+ fi
66
+ elif [[ $1 = "-p" || $1 = "--theme-directory" ]]; then
67
+ theme_directory=${2// }
68
+
69
+ if [[ "${theme_directory}" = "--"* ]]; then
70
+ theme_directory=""
71
+ shift 1
72
+ else
73
+ shift 2
74
+ fi
55
75
  elif [[ $1 = "-w" || $1 = "--workdir" ]]; then
56
76
  workdir=${2// }
57
77
 
@@ -67,22 +87,29 @@ parse_args() {
67
87
  done
68
88
 
69
89
  if [[ -z "${workdir}" ]]; then
70
- echo "Missing required argument: --workdir <workdir>."
71
- echo "${help_message}"
90
+ echo "Missing required argument: --workdir <workdir>." >&2
91
+ echo "${help_message}" >&2
72
92
  return 1
73
93
  fi
74
94
 
75
95
  if [[ (-z "${gemdir}" && -z "${token}") || (-n "${gemdir}" && -n "${token}") ]]; then
76
- echo "Missing or invalid required arguments: --gemdir <gem-path> or --token <token>."
77
- echo "Either [--gemdir] or [--token] needs to be provided, but not both."
96
+ echo "Missing or invalid required arguments: --gemdir <gem-path> or --token <token>." >&2
97
+ echo "Either [--gemdir] or [--token] needs to be provided, but not both." >&2
78
98
  echo "${help_message}"
79
99
  return 1
80
100
  fi
81
101
 
82
102
  if [[ (-n "${version}" && -z "${token}") || (-z "${version}" && -n "${token}") ]]; then
83
- echo "Missing or invalid required arguments: --version <gem-path> and --token <token>."
84
- echo "When either argument is present, both must be."
85
- echo "${help_message}"
103
+ echo "Missing or invalid required arguments: --version <gem-path> and --token <token>." >&2
104
+ echo "When either argument is present, both must be." >&2
105
+ echo "${help_message}" >&2
106
+ return 1
107
+ fi
108
+
109
+ if [[ -z "${theme_name}" && -n "${theme_directory}" ]]; then
110
+ echo "Missing or invalid required arguments: --theme-name <name>." >&2
111
+ echo "[--theme-name] is required when [--theme-directory] is provided." >&2
112
+ echo "${help_message}" >&2
86
113
  return 1
87
114
  fi
88
115
  }
@@ -101,28 +128,47 @@ test_gem() {
101
128
 
102
129
  cd "${workdir}"
103
130
 
104
- gem install bundler
131
+ # Recreate Gemfile
132
+ printf "# frozen_string_literal: true\nsource 'https://rubygems.org'\ngem 'jekyll'\ngem 'simplecov'\n" > Gemfile
105
133
 
106
134
  if [[ -n "${token}" ]]; then
107
135
  # A non-empty $token means we should install the Gem from GPR
108
136
  repository="https://rubygems.pkg.github.com/swedbankpay"
109
137
  bundle config "${repository}" "SwedbankPay:${token}"
110
- printf "source '%s' do\n\tgem 'kramdown-plantuml', '%s'\nend" "${repository}" "${version}" >> Gemfile
138
+ printf "source '%s' do\n\tgem 'kramdown-plantuml', '%s'\nend\n" "${repository}" "${version}" >> Gemfile
111
139
  else
112
- echo "gem 'kramdown-plantuml', path: '${gemdir}'" >> Gemfile
140
+ printf "gem 'kramdown-plantuml', path: '%s'\n" "${gemdir}" >> Gemfile
141
+ fi
142
+
143
+ # Recreate _config.yml
144
+ printf "plugins:\n- kramdown-plantuml\n" > _config.yml
145
+
146
+ if [[ -n "${theme_name}" ]]; then
147
+ printf "kramdown:\n plantuml:\n theme:\n name: %s\n" "${theme_name}" >> _config.yml
148
+ class="plantuml theme-${theme_name}"
149
+ else
150
+ class='plantuml'
151
+ fi
152
+
153
+ if [[ -n "${theme_directory}" ]]; then
154
+ printf " directory: %s\n" "${theme_directory}" >> _config.yml
113
155
  fi
114
156
 
115
157
  if [[ "${verbose}" = true ]]; then
158
+ printf "\nGemfile:\n"
116
159
  cat Gemfile
160
+ printf "\n_config.yml\n"
161
+ cat _config.yml
117
162
  jekyll_build_args+=(--verbose)
118
163
  fi
119
164
 
120
165
  bundle install
121
166
  bundle exec jekyll build "${jekyll_build_args[@]}"
122
167
 
168
+
123
169
  file="${workdir}/_site/index.html"
124
170
 
125
- file_contains "${file}" "class=\"plantuml theme-spacelab\""
171
+ file_contains "${file}" "class=\"${class}\""
126
172
  file_contains "${file}" "<svg"
127
173
  file_contains "${file}" "<ellipse"
128
174
  file_contains "${file}" "<polygon"
@@ -91,20 +91,35 @@ jobs:
91
91
  - name: rubocop
92
92
  run: bundle exec rubocop --fail-level warning --display-only-fail-level-offenses
93
93
 
94
- - name: Test with Rake
95
- run: bundle exec rake
96
-
97
94
  - name: RSPec (debug)
98
95
  env:
99
96
  DEBUG: 1
100
97
  run: bundle exec rspec --tag debug
101
98
 
99
+ - name: Upload code coverage (debug)
100
+ uses: actions/upload-artifact@v2
101
+ with:
102
+ name: rspec-debug-coverage
103
+ path: ./coverage
104
+
105
+ - name: Codecov upload (debug)
106
+ run: bundle exec rake codecov:upload || echo 'Codecov upload failed'
107
+
108
+ - name: Test with Rake
109
+ run: bundle exec rake
110
+
111
+ - name: Upload code coverage
112
+ uses: actions/upload-artifact@v2
113
+ with:
114
+ name: rspec-coverage
115
+ path: ./coverage
116
+
102
117
  - name: Codecov upload
103
118
  run: bundle exec rake codecov:upload || echo 'Codecov upload failed'
104
119
 
105
120
  - name: Build gem
106
121
  id: gem
107
- run: .github/scripts/build-gem.sh
122
+ run: .github/scripts/build-gem.sh --ref ${{ github.ref }} --verbose
108
123
 
109
124
  - name: Upload gem
110
125
  uses: actions/upload-artifact@v2-preview
@@ -116,7 +131,16 @@ jobs:
116
131
  run: .github/scripts/inspect-gem.sh --gem "${{ github.workspace }}/${{ steps.gem.outputs.name }}" --verbose
117
132
 
118
133
  - name: Test gem
119
- run: .github/scripts/test-gem.sh --workdir "${{ github.workspace }}/spec/fixture" --gemdir "${{ github.workspace }}" --verbose
134
+ run: .github/scripts/test-gem.sh --workdir "${{ github.workspace }}/spec/examples" --gemdir "${{ github.workspace }}" --verbose
135
+
136
+ - name: Upload code coverage (gem)
137
+ uses: actions/upload-artifact@v2
138
+ with:
139
+ name: gem-coverage
140
+ path: ./coverage
141
+
142
+ - name: Codecov upload (gem)
143
+ run: bundle exec rake codecov:upload || echo 'Codecov upload failed'
120
144
 
121
145
  publish-dev:
122
146
  needs: [version, gem]
@@ -150,15 +174,21 @@ jobs:
150
174
  - name: Publish to GPR
151
175
  run: .github/scripts/publish-gem.sh --gem ${{ needs.gem.outputs.name }} --token "${{ secrets.GPR_TOKEN }}" --owner SwedbankPay --verbose
152
176
 
153
- - name: Test gem
154
- run: .github/scripts/test-gem.sh --workdir "${{ github.workspace }}/spec/fixture" --version ${{ needs.version.outputs.version }} --token "${{ secrets.GPR_TOKEN }}" --verbose
177
+ - name: Test gem (no theme)
178
+ run: .github/scripts/test-gem.sh --workdir "${{ github.workspace }}/spec/examples" --version ${{ needs.version.outputs.version }} --token "${{ secrets.GPR_TOKEN }}" --verbose
179
+
180
+ - name: Test gem (built-in theme)
181
+ run: .github/scripts/test-gem.sh --workdir "${{ github.workspace }}/spec/examples" --version ${{ needs.version.outputs.version }} --token "${{ secrets.GPR_TOKEN }}" --verbose --theme-name spacelab
182
+
183
+ - name: Test gem (custom theme)
184
+ run: .github/scripts/test-gem.sh --workdir "${{ github.workspace }}/spec/examples" --version ${{ needs.version.outputs.version }} --token "${{ secrets.GPR_TOKEN }}" --verbose --theme-name c2a3b0 --theme-directory "${{ github.workspace }}/spec/examples"
155
185
 
156
186
  - name: Upload Jekyll site
157
187
  uses: actions/upload-artifact@v2-preview
158
188
  if: always()
159
189
  with:
160
190
  name: site
161
- path: ${{ github.workspace }}/spec/fixture/_site
191
+ path: ${{ github.workspace }}/spec/examples/_site
162
192
 
163
193
  publish-prod:
164
194
  needs: [version, gem]
data/README.md CHANGED
@@ -6,6 +6,7 @@
6
6
  [![No PlantUML][no-plantuml-badge]][no-plantuml-workflow]
7
7
  [![Shell][shell-badge]][shell-workflow]
8
8
  [![Codecov][codecov-badge]][codecov]
9
+ [![Codacy Badge][codacy-badge]][codacy]
9
10
  [![License][license-badge]][license]
10
11
  [![CLA assistant][cla-badge]][cla]
11
12
  [![Contributor Covenant][coc-badge]][coc]
@@ -180,6 +181,8 @@ agreement][cla].
180
181
  [clone]: https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/cloning-a-repository
181
182
  [coc-badge]: https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg
182
183
  [coc]: ./CODE_OF_CONDUCT.md
184
+ [codacy-badge]: https://app.codacy.com/project/badge/Grade/de72385f4ca444c18819a3ce8a506638
185
+ [codacy]: https://www.codacy.com/gh/SwedbankPay/kramdown-plantuml/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=SwedbankPay/kramdown-plantuml&amp;utm_campaign=Badge_Grade
183
186
  [codecov-badge]: https://codecov.io/gh/SwedbankPay/kramdown-plantuml/branch/main/graph/badge.svg?token=U3QJLVG3HY
184
187
  [codecov]: https://codecov.io/gh/SwedbankPay/kramdown-plantuml/
185
188
  [diagram-svg]: ./spec/examples/diagram.svg
@@ -9,8 +9,8 @@ module Kramdown
9
9
 
10
10
  def initialize(name)
11
11
  @name = name
12
- @value = ENV.key?(name) ? ENV[name] : nil
13
- @value = @value.to_s.downcase unless @value.nil?
12
+ value = ENV.fetch(name, nil)
13
+ @value = value.to_s.downcase unless value.nil?
14
14
  end
15
15
 
16
16
  def true?
@@ -34,7 +34,7 @@ module Kramdown
34
34
  return false unless write_message?(level)
35
35
 
36
36
  pipe = pipe_for(level)
37
- pipe.write("\n#{message}")
37
+ pipe.write("#{message}\n")
38
38
  end
39
39
 
40
40
  def write_message?(level_of_message)
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'version'
4
+ require_relative 'theme'
5
+ require_relative 'plantuml_error'
6
+ require_relative 'logger'
7
+ require_relative 'executor'
8
+
9
+ module Kramdown
10
+ module PlantUml
11
+ # Represents a PlantUML diagram that can be converted to SVG.
12
+ class Diagram
13
+ attr_reader :theme, :plantuml, :result
14
+
15
+ def initialize(plantuml, options = {})
16
+ @plantuml = plantuml
17
+ @theme = Theme.new(options || {})
18
+ @logger = Logger.init
19
+ @executor = Executor.new
20
+ @logger.warn 'PlantUML diagram is empty' if @plantuml.nil? || @plantuml.empty?
21
+ end
22
+
23
+ def convert_to_svg
24
+ return @svg unless @svg.nil?
25
+ return @plantuml if @plantuml.nil? || @plantuml.empty?
26
+
27
+ @plantuml = @theme.apply(@plantuml)
28
+ @plantuml = plantuml.strip
29
+ log(plantuml)
30
+ @result = @executor.execute(self)
31
+ @result.validate
32
+ @svg = wrap(@result.without_xml_prologue)
33
+ @svg
34
+ end
35
+
36
+ private
37
+
38
+ def wrap(svg)
39
+ theme_class = @theme.name ? "theme-#{@theme.name}" : ''
40
+ class_name = "plantuml #{theme_class}".strip
41
+
42
+ wrapper_element_start = "<div class=\"#{class_name}\">"
43
+ wrapper_element_end = '</div>'
44
+
45
+ "#{wrapper_element_start}#{svg}#{wrapper_element_end}"
46
+ end
47
+
48
+ def log(plantuml)
49
+ @logger.debug 'PlantUML converting diagram:'
50
+ @logger.debug_multiline plantuml
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'open3'
4
4
  require_relative '../which'
@@ -18,21 +18,19 @@ module Kramdown
18
18
  raise IOError, "'#{@plantuml_jar_file}' does not exist" unless File.exist? @plantuml_jar_file
19
19
  end
20
20
 
21
- def execute(stdin)
22
- cmd = "java -Djava.awt.headless=true -jar #{@plantuml_jar_file} -tsvg -failfast -pipe"
23
- cmd << if @logger.debug?
24
- ' -verbose'
25
- else
26
- ' -nometadata'
27
- end
21
+ def execute(diagram)
22
+ raise ArgumentError, 'diagram cannot be nil' if diagram.nil?
23
+ raise ArgumentError, "diagram must be a #{Diagram}" unless diagram.is_a?(Diagram)
28
24
 
29
- @logger.debug "PlantUML executing: #{cmd}"
25
+ cmd = "java -Djava.awt.headless=true -jar #{@plantuml_jar_file} -tsvg -failfast -pipe #{debug_args}"
30
26
 
31
- stdout, stderr, status = Open3.capture3 cmd, stdin_data: stdin
27
+ @logger.debug "Executing '#{cmd}'."
32
28
 
33
- @logger.debug "PlantUML exit code: #{status.exitstatus}"
29
+ stdout, stderr, status = Open3.capture3 cmd, stdin_data: diagram.plantuml
34
30
 
35
- PlantUmlResult.new(stdout, stderr, status)
31
+ @logger.debug "PlantUML exit code '#{status.exitstatus}'."
32
+
33
+ PlantUmlResult.new(diagram, stdout, stderr, status.exitstatus)
36
34
  end
37
35
 
38
36
  private
@@ -43,6 +41,12 @@ module Kramdown
43
41
  first_jar = Dir[jar_glob].first
44
42
  File.expand_path first_jar unless first_jar.nil?
45
43
  end
44
+
45
+ def debug_args
46
+ return ' -verbose' if @logger.debug?
47
+
48
+ ' -nometadata'
49
+ end
46
50
  end
47
51
  end
48
52
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'English'
5
+ require_relative 'logger'
6
+ require_relative 'diagram'
7
+
8
+ module Kramdown
9
+ module PlantUml
10
+ # Provides an instance of Jekyll if available.
11
+ module JekyllProvider
12
+ class << self
13
+ def jekyll
14
+ return @jekyll if defined? @jekyll
15
+
16
+ @jekyll = load_jekyll
17
+ end
18
+
19
+ def install
20
+ return @installed = false if jekyll.nil?
21
+
22
+ logger.debug 'Jekyll detected, hooking into :site:post_render'
23
+
24
+ Jekyll::Hooks.register :site, :post_render do |site|
25
+ logger.debug ':site:post_render triggered.'
26
+
27
+ site.pages.each do |page|
28
+ page.output = replace_needles(page.output)
29
+ end
30
+ end
31
+
32
+ @installed = true
33
+ end
34
+
35
+ def installed?
36
+ @installed
37
+ end
38
+
39
+ def needle(plantuml, options)
40
+ plantuml_options = !options.nil? && options.key?(:plantuml) ? options[:plantuml] : nil
41
+ hash = { 'plantuml' => plantuml, 'options' => plantuml_options }
42
+
43
+ <<~NEEDLE
44
+ <!--#kramdown-plantuml.start#-->
45
+ #{hash.to_json}
46
+ <!--#kramdown-plantuml.end#-->
47
+ NEEDLE
48
+ end
49
+
50
+ private
51
+
52
+ def replace_needles(html)
53
+ html.gsub(/<!--#kramdown-plantuml\.start#-->(?<json>.*?)<!--#kramdown-plantuml\.end#-->/m) do
54
+ json = $LAST_MATCH_INFO[:json]
55
+ hash = JSON.parse(json)
56
+ plantuml = hash['plantuml']
57
+ options = hash['options']
58
+ diagram = ::Kramdown::PlantUml::Diagram.new(plantuml, options)
59
+ return diagram.convert_to_svg
60
+ end
61
+ end
62
+
63
+ def load_jekyll
64
+ require 'jekyll'
65
+ ::Jekyll
66
+ rescue LoadError
67
+ nil
68
+ end
69
+
70
+ def logger
71
+ @logger ||= ::Kramdown::PlantUml::Logger.init
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'console_logger'
4
+ require_relative 'jekyll_provider'
4
5
 
5
6
  module Kramdown
6
7
  module PlantUml
7
- # Provides theming support for PlantUML
8
+ # Logs stuff
8
9
  class Logger
9
10
  def initialize(logger)
10
11
  raise ArgumentError, 'logger cannot be nil' if logger.nil?
@@ -17,37 +18,42 @@ module Kramdown
17
18
  end
18
19
 
19
20
  def debug(message)
20
- @logger.debug(message)
21
+ write :debug, message
22
+ end
23
+
24
+ def debug_multiline(multiline_string)
25
+ return if multiline_string.nil? || multiline_string.empty?
26
+
27
+ lines = multiline_string.lines
28
+ lines.each do |line|
29
+ write :debug, line.rstrip
30
+ end
21
31
  end
22
32
 
23
33
  def info(message)
24
- @logger.info(message)
34
+ write :info, message
25
35
  end
26
36
 
27
37
  def warn(message)
28
- @logger.warn(message)
38
+ write :warn, message
29
39
  end
30
40
 
31
41
  def error(message)
32
- @logger.error(message)
42
+ write :error, message
33
43
  end
34
44
 
35
45
  def debug?
36
46
  self.class.level == :debug
37
47
  end
38
48
 
49
+ def level
50
+ @level ||= level_from_logger || self.class.env
51
+ end
52
+
39
53
  class << self
40
54
  def init
41
- inner = nil
42
-
43
- begin
44
- require 'jekyll'
45
- inner = Jekyll.logger
46
- rescue LoadError
47
- inner = ConsoleLogger.new(level)
48
- end
49
-
50
- Logger.new(inner)
55
+ inner = JekyllProvider.jekyll ? JekyllProvider.jekyll.logger : nil || ConsoleLogger.new(level)
56
+ Logger.new inner
51
57
  end
52
58
 
53
59
  def level
@@ -63,6 +69,18 @@ module Kramdown
63
69
  :warn
64
70
  end
65
71
  end
72
+
73
+ private
74
+
75
+ def write(level, message)
76
+ @logger.public_send(level, " kramdown-plantuml: #{message}")
77
+ end
78
+
79
+ def level_from_logger
80
+ return @logger.level if @logger.respond_to? :level
81
+
82
+ nil
83
+ end
66
84
  end
67
85
  end
68
86
  end
@@ -1,32 +1,69 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'plantuml_result'
4
+
3
5
  module Kramdown
4
6
  module PlantUml
5
7
  # PlantUML Error
6
8
  class PlantUmlError < StandardError
7
- def initialize(plantuml, stderr, exitcode)
9
+ def initialize(result)
10
+ raise ArgumentError, 'result cannot be nil' if result.nil?
11
+ raise ArgumentError, "result must be a #{PlantUmlResult}" unless result.is_a?(PlantUmlResult)
12
+
13
+ super create_message(result)
14
+ end
15
+
16
+ private
17
+
18
+ def create_message(result)
19
+ header = header(result).gsub("\n", ' ').strip
20
+ plantuml = plantuml(result)
21
+ result = result(result)
8
22
  message = <<~MESSAGE
9
- Conversion of the following PlantUML diagram failed:
23
+ #{header}
10
24
 
11
25
  #{plantuml}
12
26
 
13
- The error received from PlantUML was:
14
-
15
- Exit code: #{exitcode}
16
- #{stderr}
27
+ #{result}
17
28
  MESSAGE
18
29
 
19
- super message
30
+ message.strip
20
31
  end
21
32
 
22
- def self.should_raise?(exitcode, stderr)
23
- return false if exitcode.zero?
33
+ def header(result)
34
+ if theme_not_found?(result) && !result.diagram.nil? && !result.diagram.theme.nil?
35
+ return <<~HEADER
36
+ Conversion of the following PlantUML result failed because the
37
+ theme '#{result.diagram.theme.name}' can't be found in the directory
38
+ '#{result.diagram.theme.directory}':
39
+ HEADER
40
+ end
41
+
42
+ 'Conversion of the following PlantUML result failed:'
43
+ end
44
+
45
+ def theme_not_found?(result)
46
+ !result.nil? \
47
+ && !result.stderr.nil? \
48
+ && result.stderr.include?('NullPointerException') \
49
+ && result.stderr.include?('getTheme')
50
+ end
51
+
52
+ def plantuml(result)
53
+ return nil if result.nil? || result.diagram.nil?
54
+
55
+ result.diagram.plantuml
56
+ end
57
+
58
+ def result(result)
59
+ return nil if result.nil?
60
+
61
+ <<~RESULT
62
+ The error received from PlantUML was:
24
63
 
25
- !stderr.nil? && !stderr.empty? && \
26
- # If stderr is not empty, but contains the string 'CoreText note:',
27
- # the error is caused by a bug in Java, and should be ignored.
28
- # Circumvents https://bugs.openjdk.java.net/browse/JDK-8244621
29
- !stderr.include?('CoreText note:')
64
+ Exit code: #{result.exitcode}
65
+ #{result.stderr}
66
+ RESULT
30
67
  end
31
68
  end
32
69
  end
@@ -2,17 +2,24 @@
2
2
 
3
3
  require_relative 'logger'
4
4
  require_relative 'plantuml_error'
5
+ require_relative 'diagram'
5
6
 
6
7
  module Kramdown
7
8
  module PlantUml
8
9
  # Executes the PlantUML Java application.
9
10
  class PlantUmlResult
10
- attr_reader :stdout, :stderr, :exitcode
11
+ attr_reader :diagram, :stdout, :stderr, :exitcode
11
12
 
12
- def initialize(stdout, stderr, status)
13
+ def initialize(diagram, stdout, stderr, exitcode)
14
+ raise ArgumentError, 'diagram cannot be nil' if diagram.nil?
15
+ raise ArgumentError, "diagram must be a #{Diagram}" unless diagram.is_a?(Diagram)
16
+ raise ArgumentError, 'exitcode cannot be nil' if exitcode.nil?
17
+ raise ArgumentError, "exitcode must be a #{Integer}" unless exitcode.is_a?(Integer)
18
+
19
+ @diagram = diagram
13
20
  @stdout = stdout
14
21
  @stderr = stderr
15
- @exitcode = status.exitstatus
22
+ @exitcode = exitcode
16
23
  @logger = Logger.init
17
24
  end
18
25
 
@@ -37,12 +44,22 @@ module Kramdown
37
44
  @stdout
38
45
  end
39
46
 
40
- def validate(plantuml)
41
- raise PlantUmlError.new(plantuml, @stderr, @exitcode) if PlantUmlError.should_raise?(@exitcode, @stderr)
47
+ def valid?
48
+ return true if @exitcode.zero? || @stderr.nil? || @stderr.empty?
49
+
50
+ # If stderr is not empty, but contains the string 'CoreText note:',
51
+ # the error is caused by a bug in Java, and should be ignored.
52
+ # Circumvents https://bugs.openjdk.java.net/browse/JDK-8244621
53
+ @stderr.include?('CoreText note:')
54
+ end
55
+
56
+ def validate
57
+ raise PlantUmlError, self unless valid?
42
58
 
43
59
  return if @stderr.nil? || @stderr.empty?
44
60
 
45
- @logger.debug("PlantUML log:\n#{@stderr}")
61
+ @logger.debug 'PlantUML log:'
62
+ @logger.debug_multiline @stderr
46
63
  end
47
64
  end
48
65
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: false
2
+
3
+ require_relative 'logger'
4
+
5
+ module Kramdown
6
+ module PlantUml
7
+ # Provides theming support for PlantUML
8
+ class Theme
9
+ attr_reader :name, :directory
10
+
11
+ def initialize(options = {})
12
+ @logger = Logger.init
13
+ @name, @directory = theme_options(options)
14
+ end
15
+
16
+ def apply(plantuml)
17
+ if plantuml.nil? || !plantuml.is_a?(String) || plantuml.empty?
18
+ @logger.debug 'Empty diagram or not a String.'
19
+ return plantuml
20
+ end
21
+
22
+ if @name.nil? || @name.empty?
23
+ @logger.debug 'No theme to apply.'
24
+ return plantuml
25
+ end
26
+
27
+ theme(plantuml)
28
+ end
29
+
30
+ private
31
+
32
+ def theme_options(options)
33
+ options = symbolize_keys(options)
34
+
35
+ @logger.debug "Options: #{options}"
36
+
37
+ return nil if options.nil? || !options.key?(:theme)
38
+
39
+ theme = options[:theme] || {}
40
+ name = theme.key?(:name) ? theme[:name] : nil
41
+ directory = theme.key?(:directory) ? theme[:directory] : nil
42
+
43
+ [name, directory]
44
+ end
45
+
46
+ def symbolize_keys(options)
47
+ return options if options.nil?
48
+
49
+ array = options.map do |key, value|
50
+ value = value.is_a?(Hash) ? symbolize_keys(value) : value
51
+ [key.to_sym, value]
52
+ end
53
+
54
+ array.to_h
55
+ end
56
+
57
+ def theme(plantuml)
58
+ startuml = '@startuml'
59
+ startuml_index = plantuml.index(startuml) + startuml.length
60
+
61
+ return plantuml if startuml_index.nil?
62
+
63
+ theme_string = "\n!theme #{@name}"
64
+ theme_string << " from #{@directory}" unless @directory.nil?
65
+
66
+ @logger.debug "Applying #{theme_string.strip}"
67
+
68
+ /@startuml.*/.match(plantuml) do |match|
69
+ return plantuml.insert match.end(0), theme_string
70
+ end
71
+
72
+ plantuml
73
+ end
74
+ end
75
+ end
76
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Kramdown
4
4
  module PlantUml
5
- VERSION = '1.1.1'
5
+ VERSION = '1.1.5'
6
6
  end
7
7
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'kramdown-plantuml/version'
4
- require_relative 'kramdown-plantuml/converter'
5
3
  require_relative 'kramdown_html'
4
+ require_relative 'kramdown-plantuml/jekyll_provider'
5
+
6
+ ::Kramdown::PlantUml::JekyllProvider.install
data/lib/kramdown_html.rb CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  require 'kramdown'
4
4
  require 'kramdown-parser-gfm'
5
- require_relative 'kramdown-plantuml/converter'
6
5
  require_relative 'kramdown-plantuml/logger'
7
6
  require_relative 'kramdown-plantuml/plantuml_error'
7
+ require_relative 'kramdown-plantuml/diagram'
8
+ require_relative 'kramdown-plantuml/jekyll_provider'
8
9
 
9
10
  module Kramdown
10
11
  module Converter
@@ -14,13 +15,31 @@ module Kramdown
14
15
  alias super_convert_codeblock convert_codeblock
15
16
 
16
17
  def convert_codeblock(element, indent)
17
- return super_convert_codeblock(element, indent) if element.attr['class'] != 'language-plantuml'
18
+ return super_convert_codeblock(element, indent) unless plantuml?(element)
18
19
 
19
- plantuml = element.value
20
+ jekyll = ::Kramdown::PlantUml::JekyllProvider
21
+
22
+ # If Jekyll is successfully loaded, we'll wait with converting the
23
+ # PlantUML diagram to SVG since a theme may be configured that needs to
24
+ # be copied to the assets directory before the PlantUML conversion can
25
+ # be performed. We therefore place a needle in the haystack that we will
26
+ # convert in the :site:pre_render hook.
27
+ return jekyll.needle(element.value, @options) if jekyll.installed?
28
+
29
+ convert_plantuml(element.value)
30
+ end
31
+
32
+ private
33
+
34
+ def plantuml?(element)
35
+ element.attr['class'] == 'language-plantuml'
36
+ end
37
+
38
+ def convert_plantuml(plantuml)
20
39
  plantuml_options = @options.key?(:plantuml) ? @options[:plantuml] : {}
21
- converter = ::Kramdown::PlantUml::Converter.new(plantuml_options || {})
22
40
 
23
- converter.convert_plantuml_to_svg(plantuml)
41
+ diagram = ::Kramdown::PlantUml::Diagram.new(plantuml, plantuml_options)
42
+ diagram.convert_to_svg
24
43
  end
25
44
  end
26
45
  end
data/pom.xml CHANGED
@@ -10,7 +10,7 @@
10
10
  <dependency>
11
11
  <groupId>net.sourceforge.plantuml</groupId>
12
12
  <artifactId>plantuml</artifactId>
13
- <version>1.2021.9</version>
13
+ <version>1.2021.10</version>
14
14
  </dependency>
15
15
  </dependencies>
16
16
  </project>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kramdown-plantuml
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Swedbank Pay
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-31 00:00:00.000000000 Z
11
+ date: 2021-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kramdown
@@ -166,18 +166,18 @@ files:
166
166
  - LICENSE
167
167
  - README.md
168
168
  - Rakefile
169
- - bin/net/sourceforge/plantuml/plantuml/1.2021.8/plantuml-1.2021.8.jar
169
+ - bin/net/sourceforge/plantuml/plantuml/1.2021.10/plantuml-1.2021.10.jar
170
170
  - kramdown-plantuml.gemspec
171
171
  - lib/kramdown-plantuml.rb
172
172
  - lib/kramdown-plantuml/bool_env.rb
173
173
  - lib/kramdown-plantuml/console_logger.rb
174
- - lib/kramdown-plantuml/converter.rb
174
+ - lib/kramdown-plantuml/diagram.rb
175
175
  - lib/kramdown-plantuml/executor.rb
176
- - lib/kramdown-plantuml/hash.rb
176
+ - lib/kramdown-plantuml/jekyll_provider.rb
177
177
  - lib/kramdown-plantuml/logger.rb
178
178
  - lib/kramdown-plantuml/plantuml_error.rb
179
179
  - lib/kramdown-plantuml/plantuml_result.rb
180
- - lib/kramdown-plantuml/themer.rb
180
+ - lib/kramdown-plantuml/theme.rb
181
181
  - lib/kramdown-plantuml/version.rb
182
182
  - lib/kramdown_html.rb
183
183
  - lib/which.rb
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'version'
4
- require_relative 'themer'
5
- require_relative 'plantuml_error'
6
- require_relative 'logger'
7
- require_relative 'executor'
8
-
9
- module Kramdown
10
- module PlantUml
11
- # Converts PlantUML markup to SVG
12
- class Converter
13
- def initialize(options = {})
14
- @themer = Themer.new(options)
15
- @logger = Logger.init
16
- @executor = Executor.new
17
- end
18
-
19
- def convert_plantuml_to_svg(plantuml)
20
- plantuml = @themer.apply_theme(plantuml)
21
- plantuml = plantuml.strip
22
- @logger.debug "PlantUML converting diagram:\n#{plantuml}"
23
- result = @executor.execute(plantuml)
24
- result.validate(plantuml)
25
- svg = result.without_xml_prologue
26
- wrap(svg)
27
- end
28
-
29
- private
30
-
31
- def wrap(svg)
32
- theme_class = @themer.theme_name ? "theme-#{@themer.theme_name}" : ''
33
- class_name = "plantuml #{theme_class}".strip
34
-
35
- wrapper_element_start = "<div class=\"#{class_name}\">"
36
- wrapper_element_end = '</div>'
37
-
38
- "#{wrapper_element_start}#{svg}#{wrapper_element_end}"
39
- end
40
- end
41
- end
42
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Ruby's Hash class.
4
- class ::Hash
5
- # Via https://stackoverflow.com/a/25835016/2257038
6
- def symbolize_keys
7
- h = map do |k, v|
8
- v_sym = v.instance_of?(Hash) ? v.symbolize_keys : v
9
- [k.to_sym, v_sym]
10
- end
11
-
12
- h.to_h
13
- end
14
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: false
2
-
3
- require_relative 'hash'
4
- require_relative 'logger'
5
-
6
- module Kramdown
7
- module PlantUml
8
- # Provides theming support for PlantUML
9
- class Themer
10
- attr_reader :theme_name, :theme_directory
11
-
12
- def initialize(options = {})
13
- options = options.symbolize_keys
14
- @logger = Logger.init
15
- @logger.debug(options)
16
- @theme_name, @theme_directory = theme_options(options)
17
- end
18
-
19
- def apply_theme(plantuml)
20
- return plantuml if plantuml.nil? || plantuml.empty? || @theme_name.nil? || @theme_name.empty?
21
-
22
- startuml = '@startuml'
23
- startuml_index = plantuml.index(startuml) + startuml.length
24
-
25
- return plantuml if startuml_index.nil?
26
-
27
- /@startuml.*/.match(plantuml) do |match|
28
- theme_string = "\n!theme #{@theme_name}"
29
- theme_string << " from #{@theme_directory}" unless @theme_directory.nil?
30
-
31
- return plantuml.insert match.end(0), theme_string
32
- end
33
-
34
- plantuml
35
- end
36
-
37
- private
38
-
39
- def theme_options(options)
40
- return nil unless options.key?(:theme)
41
-
42
- theme = options[:theme] || {}
43
- theme_name = theme.key?(:name) ? theme[:name] : nil
44
- theme_directory = theme.key?(:directory) ? theme[:directory] : nil
45
-
46
- [theme_name, theme_directory]
47
- end
48
- end
49
- end
50
- end