kramdown-plantuml 1.1.4 → 1.1.8

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: beba25f229338deced9900f7a611c9040846e562c966de164e88dbb040acadde
4
- data.tar.gz: 8c2aa7aa04a1a6e59a98032e1cc325544a2be7c3bc3cd407cbb3af09babe390c
3
+ metadata.gz: f873d08b8ab86ebe4ea964c30ad59a43a75c28d042d6627af1694d1ff262cbfd
4
+ data.tar.gz: cca59110bbdac25b41dc4c7ef41f9df0f84b17aa4a6da662feab19a73ca61724
5
5
  SHA512:
6
- metadata.gz: fcdfa9c61a9b2c751aeae743edfb58d81c3602e1eb62f6b8993fd11b2372dd6ee271f77737f655967a093c03a90d0ebf3b4865c703b7a5ed2d7b806c5a4d19d5
7
- data.tar.gz: d35a3daea2ab515d0fd788d8b2fb0575c4993e4f25ba3c7ebda02631b8ae8d397dc88917b3809cf732e9b9a5d16bbbfa52cdfa2095a682c68de8d6ed8fba3377
6
+ metadata.gz: bbe10bc2e73c25b0a2bd09651e9bd73b2aeb612fa26f0470df0e9f0477c8f8f69f3c69d428b136826264e975068dc073724fcf4ab5e10b93e59585001983ada8
7
+ data.tar.gz: b74471596aadcf0bde3bf7878864918fb5bd6297172f69b5f9d30c214f84c1f67dfe2c11b7b6e1dc78bfb85c817c0a2c4a26e5bb9c420c519d490b5567b29fe8
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,17 +128,37 @@ test_gem() {
101
128
 
102
129
  cd "${workdir}"
103
130
 
131
+ # Recreate Gemfile
132
+ printf "# frozen_string_literal: true\nsource 'https://rubygems.org'\ngem 'jekyll'\ngem 'simplecov'\n" > Gemfile
133
+
104
134
  if [[ -n "${token}" ]]; then
105
135
  # A non-empty $token means we should install the Gem from GPR
106
136
  repository="https://rubygems.pkg.github.com/swedbankpay"
107
137
  bundle config "${repository}" "SwedbankPay:${token}"
108
- 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
109
139
  else
110
- 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
111
155
  fi
112
156
 
113
157
  if [[ "${verbose}" = true ]]; then
158
+ printf "\nGemfile:\n"
114
159
  cat Gemfile
160
+ printf "\n_config.yml\n"
161
+ cat _config.yml
115
162
  jekyll_build_args+=(--verbose)
116
163
  fi
117
164
 
@@ -120,7 +167,7 @@ test_gem() {
120
167
 
121
168
  file="${workdir}/_site/index.html"
122
169
 
123
- file_contains "${file}" "class=\"plantuml theme-spacelab\""
170
+ file_contains "${file}" "class=\"${class}\""
124
171
  file_contains "${file}" "<svg"
125
172
  file_contains "${file}" "<ellipse"
126
173
  file_contains "${file}" "<polygon"
@@ -94,7 +94,16 @@ jobs:
94
94
  - name: RSPec (debug)
95
95
  env:
96
96
  DEBUG: 1
97
- run: bundle exec rspec --tag debug
97
+ run: bundle exec rspec --format documentation --tag debug
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'
98
107
 
99
108
  - name: Test with Rake
100
109
  run: bundle exec rake
@@ -102,15 +111,31 @@ jobs:
102
111
  - name: Upload code coverage
103
112
  uses: actions/upload-artifact@v2
104
113
  with:
105
- name: coverage
114
+ name: rspec-coverage
106
115
  path: ./coverage
107
116
 
108
117
  - name: Codecov upload
109
118
  run: bundle exec rake codecov:upload || echo 'Codecov upload failed'
110
119
 
120
+ - name: RSPec (Jekyll)
121
+ run: |
122
+ echo "gem 'jekyll', require: false, group: :test" >> Gemfile
123
+ bundle install
124
+ bundle exec rspec --format documentation --tag jekyll
125
+ git checkout HEAD -- Gemfile
126
+
127
+ - name: Upload code coverage (Jekyll)
128
+ uses: actions/upload-artifact@v2
129
+ with:
130
+ name: rspec-jekyll-coverage
131
+ path: ./coverage
132
+
133
+ - name: Codecov upload (Jekyll)
134
+ run: bundle exec rake codecov:upload || echo 'Codecov upload failed'
135
+
111
136
  - name: Build gem
112
137
  id: gem
113
- run: .github/scripts/build-gem.sh
138
+ run: .github/scripts/build-gem.sh --ref ${{ github.ref }} --verbose
114
139
 
115
140
  - name: Upload gem
116
141
  uses: actions/upload-artifact@v2-preview
@@ -122,7 +147,16 @@ jobs:
122
147
  run: .github/scripts/inspect-gem.sh --gem "${{ github.workspace }}/${{ steps.gem.outputs.name }}" --verbose
123
148
 
124
149
  - name: Test gem
125
- run: .github/scripts/test-gem.sh --workdir "${{ github.workspace }}/spec/fixture" --gemdir "${{ github.workspace }}" --verbose
150
+ run: .github/scripts/test-gem.sh --workdir "${{ github.workspace }}/spec/examples" --gemdir "${{ github.workspace }}" --verbose
151
+
152
+ - name: Upload code coverage (gem)
153
+ uses: actions/upload-artifact@v2
154
+ with:
155
+ name: gem-coverage
156
+ path: ./coverage
157
+
158
+ - name: Codecov upload (gem)
159
+ run: bundle exec rake codecov:upload || echo 'Codecov upload failed'
126
160
 
127
161
  publish-dev:
128
162
  needs: [version, gem]
@@ -156,15 +190,21 @@ jobs:
156
190
  - name: Publish to GPR
157
191
  run: .github/scripts/publish-gem.sh --gem ${{ needs.gem.outputs.name }} --token "${{ secrets.GPR_TOKEN }}" --owner SwedbankPay --verbose
158
192
 
159
- - name: Test gem
160
- run: .github/scripts/test-gem.sh --workdir "${{ github.workspace }}/spec/fixture" --version ${{ needs.version.outputs.version }} --token "${{ secrets.GPR_TOKEN }}" --verbose
193
+ - name: Test gem (no theme)
194
+ run: .github/scripts/test-gem.sh --workdir "${{ github.workspace }}/spec/examples" --version ${{ needs.version.outputs.version }} --token "${{ secrets.GPR_TOKEN }}" --verbose
195
+
196
+ - name: Test gem (built-in theme)
197
+ run: .github/scripts/test-gem.sh --workdir "${{ github.workspace }}/spec/examples" --version ${{ needs.version.outputs.version }} --token "${{ secrets.GPR_TOKEN }}" --verbose --theme-name spacelab
198
+
199
+ - name: Test gem (custom theme)
200
+ 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"
161
201
 
162
202
  - name: Upload Jekyll site
163
203
  uses: actions/upload-artifact@v2-preview
164
204
  if: always()
165
205
  with:
166
206
  name: site
167
- path: ${{ github.workspace }}/spec/fixture/_site
207
+ path: ${{ github.workspace }}/spec/examples/_site
168
208
 
169
209
  publish-prod:
170
210
  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]
@@ -129,6 +130,20 @@ kramdown:
129
130
  directory: path/to/themes
130
131
  ```
131
132
 
133
+ ### Errors
134
+
135
+ By default, `kramdown-plantuml` will raise an error and crash if something goes
136
+ wrong during processing. This can be circumvented by setting the `raise_errors`
137
+ configuration key to `false`:
138
+
139
+ ```yaml
140
+ kramdown:
141
+ plantuml:
142
+ raise_errors: false
143
+ ```
144
+
145
+ The default value of `raise_errors` is `true`.
146
+
132
147
  ## Contributing
133
148
 
134
149
  Bug reports and pull requests are welcome on [GitHub][github]. This project is
@@ -180,6 +195,8 @@ agreement][cla].
180
195
  [clone]: https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/cloning-a-repository
181
196
  [coc-badge]: https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg
182
197
  [coc]: ./CODE_OF_CONDUCT.md
198
+ [codacy-badge]: https://app.codacy.com/project/badge/Grade/de72385f4ca444c18819a3ce8a506638
199
+ [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
200
  [codecov-badge]: https://codecov.io/gh/SwedbankPay/kramdown-plantuml/branch/main/graph/badge.svg?token=U3QJLVG3HY
184
201
  [codecov]: https://codecov.io/gh/SwedbankPay/kramdown-plantuml/
185
202
  [diagram-svg]: ./spec/examples/diagram.svg
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'rspec/core/rake_task'
6
6
 
7
7
  RSpec::Core::RakeTask.new(:spec) do |t|
8
8
  t.pattern = Dir.glob('spec/**/*_spec.rb')
9
- t.rspec_opts = '--format documentation --tag ~no_plantuml --tag ~no_java --tag ~debug'
9
+ t.rspec_opts = '--format documentation --tag ~no_plantuml --tag ~no_java --tag ~debug --tag ~jekyll'
10
10
  end
11
11
 
12
12
  namespace :maven do
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'lib/which'
3
+ require_relative 'lib/kramdown-plantuml/which'
4
4
  require_relative 'lib/kramdown-plantuml/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
@@ -35,6 +35,7 @@ Gem::Specification.new do |spec|
35
35
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
36
36
  spec.require_paths = ['lib']
37
37
 
38
+ spec.add_dependency 'htmlentities', '~> 4'
38
39
  spec.add_dependency 'kramdown', '~> 2.3'
39
40
  spec.add_dependency 'kramdown-parser-gfm', '~> 1.1'
40
41
  spec.add_dependency 'open3', '~> 0.1'
@@ -2,8 +2,9 @@
2
2
 
3
3
  require_relative 'version'
4
4
  require_relative 'theme'
5
+ require_relative 'options'
5
6
  require_relative 'plantuml_error'
6
- require_relative 'logger'
7
+ require_relative 'log_wrapper'
7
8
  require_relative 'executor'
8
9
 
9
10
  module Kramdown
@@ -12,28 +13,31 @@ module Kramdown
12
13
  class Diagram
13
14
  attr_reader :theme, :plantuml, :result
14
15
 
15
- def initialize(plantuml, options = {})
16
+ def initialize(plantuml, options)
17
+ raise ArgumentError, 'options cannot be nil' if options.nil?
18
+ raise ArgumentError, "options must be a '#{Options}'." unless options.is_a?(Options)
19
+
16
20
  @plantuml = plantuml
17
- @theme = Theme.new(options || {})
18
- @logger = Logger.init
21
+ @options = options
22
+ @theme = Theme.new(options)
23
+ @logger = LogWrapper.init
19
24
  @executor = Executor.new
25
+ @logger.warn 'PlantUML diagram is empty' if @plantuml.nil? || @plantuml.empty?
20
26
  end
21
27
 
22
28
  def convert_to_svg
23
29
  return @svg unless @svg.nil?
24
-
25
- if @plantuml.nil? || @plantuml.empty?
26
- @logger.warn ' kramdown-plantuml: PlantUML diagram is empty'
27
- return @plantuml
28
- end
30
+ return @plantuml if @plantuml.nil? || @plantuml.empty?
29
31
 
30
32
  @plantuml = @theme.apply(@plantuml)
31
- @plantuml = plantuml.strip
32
33
  log(plantuml)
33
34
  @result = @executor.execute(self)
34
35
  @result.validate
35
36
  @svg = wrap(@result.without_xml_prologue)
36
- @svg
37
+ rescue StandardError => e
38
+ raise e if @options.raise_errors?
39
+
40
+ @logger.error e.to_s
37
41
  end
38
42
 
39
43
  private
@@ -49,8 +53,8 @@ module Kramdown
49
53
  end
50
54
 
51
55
  def log(plantuml)
52
- @logger.debug ' kramdown-plantuml: PlantUML converting diagram:'
53
- @logger.debug_with_prefix ' kramdown-plantuml: ', plantuml
56
+ @logger.debug 'PlantUML converting diagram:'
57
+ @logger.debug_multiline plantuml
54
58
  end
55
59
  end
56
60
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'open3'
4
- require_relative '../which'
5
- require_relative 'logger'
4
+ require_relative 'which'
5
+ require_relative 'log_wrapper'
6
6
  require_relative 'plantuml_result'
7
7
 
8
8
  module Kramdown
@@ -10,12 +10,18 @@ module Kramdown
10
10
  # Executes the PlantUML Java application.
11
11
  class Executor
12
12
  def initialize
13
- @logger = Logger.init
13
+ @logger = LogWrapper.init
14
+
15
+ java_location = Which.which('java')
16
+
17
+ raise IOError, 'Java can not be found' if java_location.nil?
18
+
19
+ @logger.debug "Java found: #{java_location}"
14
20
  @plantuml_jar_file = find_plantuml_jar_file
15
21
 
16
- raise IOError, 'Java can not be found' unless Which.which('java')
17
- raise IOError, "No 'plantuml.jar' file could be found" if @plantuml_jar_file.nil?
18
22
  raise IOError, "'#{@plantuml_jar_file}' does not exist" unless File.exist? @plantuml_jar_file
23
+
24
+ @logger.debug "plantuml.jar found: #{@plantuml_jar_file}"
19
25
  end
20
26
 
21
27
  def execute(diagram)
@@ -24,11 +30,11 @@ module Kramdown
24
30
 
25
31
  cmd = "java -Djava.awt.headless=true -jar #{@plantuml_jar_file} -tsvg -failfast -pipe #{debug_args}"
26
32
 
27
- @logger.debug " kramdown-plantuml: Executing '#{cmd}'."
33
+ @logger.debug "Executing '#{cmd}'."
28
34
 
29
35
  stdout, stderr, status = Open3.capture3 cmd, stdin_data: diagram.plantuml
30
36
 
31
- @logger.debug " kramdown-plantuml: PlantUML exit code '#{status.exitstatus}'."
37
+ @logger.debug "PlantUML exit code '#{status.exitstatus}'."
32
38
 
33
39
  PlantUmlResult.new(diagram, stdout, stderr, status.exitstatus)
34
40
  end
@@ -37,9 +43,13 @@ module Kramdown
37
43
 
38
44
  def find_plantuml_jar_file
39
45
  dir = File.dirname __dir__
40
- jar_glob = File.join dir, '../bin/**/plantuml*.jar'
46
+ bin_dir = File.expand_path File.join dir, '../bin'
47
+ jar_glob = File.join bin_dir, '/**/plantuml*.jar'
41
48
  first_jar = Dir[jar_glob].first
42
- File.expand_path first_jar unless first_jar.nil?
49
+
50
+ raise IOError, "No 'plantuml.jar' file could be found within the '#{bin_dir}' directory." if first_jar.nil?
51
+
52
+ File.expand_path first_jar
43
53
  end
44
54
 
45
55
  def debug_args
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'English'
5
+ require 'htmlentities'
6
+ require_relative 'log_wrapper'
7
+ require_relative 'diagram'
8
+
9
+ module Kramdown
10
+ module PlantUml
11
+ # Provides an instance of Jekyll if available.
12
+ module JekyllProvider
13
+ class << self
14
+ def jekyll
15
+ return @jekyll if defined? @jekyll
16
+
17
+ @jekyll = load_jekyll
18
+ end
19
+
20
+ def install
21
+ return @installed = false if jekyll.nil?
22
+
23
+ logger.debug 'Jekyll detected, hooking into :site:post_render'
24
+
25
+ Jekyll::Hooks.register :site, :post_render do |site|
26
+ logger.debug ':site:post_render triggered.'
27
+
28
+ site.pages.each do |page|
29
+ page.output = replace_needles(page.output)
30
+ end
31
+ end
32
+
33
+ @installed = true
34
+ end
35
+
36
+ def installed?
37
+ @installed
38
+ end
39
+
40
+ def needle(plantuml, options)
41
+ hash = { 'plantuml' => plantuml, 'options' => options.to_h }
42
+
43
+ <<~NEEDLE
44
+ <!--#kramdown-plantuml.start#-->
45
+ #{hash.to_json}
46
+ <!--#kramdown-plantuml.end#-->
47
+ NEEDLE
48
+ rescue StandardError => e
49
+ raise e if options.raise_errors?
50
+
51
+ puts e
52
+ logger.error 'Error while placing needle.'
53
+ logger.error e.to_s
54
+ logger.debug_multiline plantuml
55
+ end
56
+
57
+ private
58
+
59
+ def replace_needles(html)
60
+ return html if html.nil? || html.empty? || !html.is_a?(String)
61
+
62
+ html.gsub(/<!--#kramdown-plantuml\.start#-->(?<json>.*?)<!--#kramdown-plantuml\.end#-->/m) do
63
+ json = $LAST_MATCH_INFO[:json]
64
+ return replace_needle(json)
65
+ end
66
+ end
67
+
68
+ def replace_needle(json)
69
+ hash = JSON.parse(json)
70
+ options_hash = hash['options']
71
+ options = ::Kramdown::PlantUml::Options.new({ plantuml: options_hash })
72
+
73
+ begin
74
+ decode_and_convert(hash, options)
75
+ rescue StandardError => e
76
+ raise e if options.raise_errors?
77
+
78
+ logger.error 'Error while replacing needle.'
79
+ logger.error e.to_s
80
+ logger.debug_multiline json
81
+ end
82
+ end
83
+
84
+ def decode_and_convert(hash, options)
85
+ encoded_plantuml = hash['plantuml']
86
+ plantuml = HTMLEntities.new.decode encoded_plantuml
87
+ diagram = ::Kramdown::PlantUml::Diagram.new(plantuml, options)
88
+ diagram.convert_to_svg
89
+ end
90
+
91
+ def load_jekyll
92
+ require 'jekyll'
93
+ ::Jekyll
94
+ rescue LoadError
95
+ nil
96
+ end
97
+
98
+ def logger
99
+ @logger ||= ::Kramdown::PlantUml::LogWrapper.init
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -1,11 +1,12 @@
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
8
  # Logs stuff
8
- class Logger
9
+ class LogWrapper
9
10
  def initialize(logger)
10
11
  raise ArgumentError, 'logger cannot be nil' if logger.nil?
11
12
  raise ArgumentError, 'logger must respond to #debug' unless logger.respond_to? :debug
@@ -17,28 +18,28 @@ module Kramdown
17
18
  end
18
19
 
19
20
  def debug(message)
20
- @logger.debug message
21
+ write :debug, message
21
22
  end
22
23
 
23
- def debug_with_prefix(prefix, multiline_string)
24
+ def debug_multiline(multiline_string)
24
25
  return if multiline_string.nil? || multiline_string.empty?
25
26
 
26
27
  lines = multiline_string.lines
27
28
  lines.each do |line|
28
- @logger.debug "#{prefix}#{line.rstrip}"
29
+ write :debug, line.rstrip
29
30
  end
30
31
  end
31
32
 
32
33
  def info(message)
33
- @logger.info message
34
+ write :info, message
34
35
  end
35
36
 
36
37
  def warn(message)
37
- @logger.warn message
38
+ write :warn, message
38
39
  end
39
40
 
40
41
  def error(message)
41
- @logger.error message
42
+ write :error, message
42
43
  end
43
44
 
44
45
  def debug?
@@ -51,16 +52,9 @@ module Kramdown
51
52
 
52
53
  class << self
53
54
  def init
54
- inner = nil
55
-
56
- begin
57
- require 'jekyll'
58
- inner = Jekyll.logger
59
- rescue LoadError
60
- inner = ConsoleLogger.new level
61
- end
62
-
63
- Logger.new inner
55
+ inner = JekyllProvider.jekyll ? JekyllProvider.jekyll.logger : nil
56
+ inner ||= ConsoleLogger.new(level)
57
+ new inner
64
58
  end
65
59
 
66
60
  def level
@@ -79,6 +73,10 @@ module Kramdown
79
73
 
80
74
  private
81
75
 
76
+ def write(level, message)
77
+ @logger.public_send(level, " kramdown-plantuml: #{message}")
78
+ end
79
+
82
80
  def level_from_logger
83
81
  return @logger.level if @logger.respond_to? :level
84
82
 
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'log_wrapper'
4
+
5
+ module Kramdown
6
+ module PlantUml
7
+ # Options for PlantUML processing
8
+ class Options
9
+ attr_reader :theme_name, :theme_directory
10
+
11
+ def initialize(options_hash = {})
12
+ @logger = LogWrapper.init
13
+ @options = massage(options_hash)
14
+ @raise_errors = extract_raise_errors(@options)
15
+ extract_theme_options(@options)
16
+ end
17
+
18
+ def raise_errors?
19
+ @raise_errors
20
+ end
21
+
22
+ def to_h
23
+ @options
24
+ end
25
+
26
+ private
27
+
28
+ def boolean(value, default_value)
29
+ return value if [true, false].include? value
30
+ return default_value if value.nil?
31
+
32
+ s = value.to_s.strip
33
+ return true if %w[true yes 1].select { |v| v.casecmp(s).zero? }.any?
34
+ return false if %w[false no 0].select { |v| v.casecmp(s).zero? }.any?
35
+
36
+ default_value
37
+ end
38
+
39
+ def extract_plantuml_options(options_hash)
40
+ return options_hash[:plantuml] if options_hash.key?(:plantuml)
41
+ return options_hash['plantuml'] if options_hash.key?('plantuml')
42
+
43
+ {}
44
+ end
45
+
46
+ def extract_theme_options(options)
47
+ return if options.empty? || !options.key?(:theme)
48
+
49
+ theme = options[:theme] || {}
50
+
51
+ unless theme.is_a?(Hash)
52
+ @logger.warn ":theme is not a Hash: #{theme}"
53
+ return
54
+ end
55
+
56
+ @theme_name = theme.key?(:name) ? theme[:name] : nil
57
+ @theme_directory = theme.key?(:directory) ? theme[:directory] : nil
58
+ end
59
+
60
+ def extract_raise_errors(options)
61
+ if options.key?(:raise_errors)
62
+ raise_errors = options[:raise_errors]
63
+ return boolean(raise_errors, true)
64
+ end
65
+
66
+ true
67
+ end
68
+
69
+ def massage(options_hash)
70
+ if options_hash.nil? || !options_hash.is_a?(Hash) || options_hash.empty?
71
+ @logger.debug 'No options provided'
72
+ return {}
73
+ end
74
+
75
+ plantuml_options = extract_plantuml_options(options_hash)
76
+ symbolize_keys(plantuml_options)
77
+ end
78
+
79
+ def symbolize_keys(options)
80
+ return options if options.nil? || options.empty?
81
+
82
+ array = options.map do |key, value|
83
+ value = value.is_a?(Hash) ? symbolize_keys(value) : value
84
+ [key.to_sym, value]
85
+ end
86
+
87
+ array.to_h
88
+ end
89
+ end
90
+ end
91
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'logger'
3
+ require_relative 'log_wrapper'
4
4
  require_relative 'plantuml_error'
5
5
  require_relative 'diagram'
6
6
 
@@ -20,7 +20,7 @@ module Kramdown
20
20
  @stdout = stdout
21
21
  @stderr = stderr
22
22
  @exitcode = exitcode
23
- @logger = Logger.init
23
+ @logger = LogWrapper.init
24
24
  end
25
25
 
26
26
  def without_xml_prologue
@@ -58,8 +58,8 @@ module Kramdown
58
58
 
59
59
  return if @stderr.nil? || @stderr.empty?
60
60
 
61
- @logger.debug ' kramdown-plantuml: PlantUML log:'
62
- @logger.debug_with_prefix ' kramdown-plantuml: ', @stderr
61
+ @logger.debug 'PlantUML log:'
62
+ @logger.debug_multiline @stderr
63
63
  end
64
64
  end
65
65
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: false
2
2
 
3
- require_relative 'logger'
3
+ require_relative 'options'
4
+ require_relative 'log_wrapper'
4
5
 
5
6
  module Kramdown
6
7
  module PlantUml
@@ -8,20 +9,24 @@ module Kramdown
8
9
  class Theme
9
10
  attr_reader :name, :directory
10
11
 
11
- def initialize(options = {})
12
- @logger = Logger.init
13
- @name, @directory = theme_options(options)
12
+ def initialize(options)
13
+ raise ArgumentError, 'options cannot be nil' if options.nil?
14
+ raise ArgumentError, "options must be a '#{Options}'." unless options.is_a?(Options)
15
+
16
+ @logger = LogWrapper.init
17
+ @name = options.theme_name
18
+ @directory = options.theme_directory
14
19
  end
15
20
 
16
21
  def apply(plantuml)
17
22
  if plantuml.nil? || !plantuml.is_a?(String) || plantuml.empty?
18
- @logger.debug ' kramdown-plantuml: Empty diagram or not a String.'
23
+ @logger.debug 'Empty diagram or not a String.'
19
24
  return plantuml
20
25
  end
21
26
 
22
27
  if @name.nil? || @name.empty?
23
- @logger.debug ' kramdown-plantuml: No theme to apply.'
24
- return plantuml
28
+ @logger.debug 'No theme to apply.'
29
+ return plantuml.strip
25
30
  end
26
31
 
27
32
  theme(plantuml)
@@ -29,31 +34,6 @@ module Kramdown
29
34
 
30
35
  private
31
36
 
32
- def theme_options(options)
33
- options = symbolize_keys(options)
34
-
35
- @logger.debug " kramdown-plantuml: 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
37
  def theme(plantuml)
58
38
  startuml = '@startuml'
59
39
  startuml_index = plantuml.index(startuml) + startuml.length
@@ -63,13 +43,13 @@ module Kramdown
63
43
  theme_string = "\n!theme #{@name}"
64
44
  theme_string << " from #{@directory}" unless @directory.nil?
65
45
 
66
- @logger.debug " kramdown-plantuml: Applying #{theme_string.strip}"
46
+ @logger.debug "Applying #{theme_string.strip}"
67
47
 
68
48
  /@startuml.*/.match(plantuml) do |match|
69
49
  return plantuml.insert match.end(0), theme_string
70
50
  end
71
51
 
72
- plantuml
52
+ plantuml.strip
73
53
  end
74
54
  end
75
55
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Kramdown
4
4
  module PlantUml
5
- VERSION = '1.1.4'
5
+ VERSION = '1.1.8'
6
6
  end
7
7
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Wraps the `which` Unix utility
3
+ # Mimics the `which` Unix utility
4
4
  class Which
5
5
  def self.which(cmd)
6
6
  exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
@@ -10,6 +10,7 @@ class Which
10
10
  return exe if File.executable?(exe) && !File.directory?(exe)
11
11
  end
12
12
  end
13
+
13
14
  nil
14
15
  end
15
16
  end
@@ -1,3 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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,11 @@
2
2
 
3
3
  require 'kramdown'
4
4
  require 'kramdown-parser-gfm'
5
- require_relative 'kramdown-plantuml/logger'
5
+ require_relative 'kramdown-plantuml/log_wrapper'
6
6
  require_relative 'kramdown-plantuml/plantuml_error'
7
+ require_relative 'kramdown-plantuml/options'
7
8
  require_relative 'kramdown-plantuml/diagram'
9
+ require_relative 'kramdown-plantuml/jekyll_provider'
8
10
 
9
11
  module Kramdown
10
12
  module Converter
@@ -14,12 +16,34 @@ module Kramdown
14
16
  alias super_convert_codeblock convert_codeblock
15
17
 
16
18
  def convert_codeblock(element, indent)
17
- return super_convert_codeblock(element, indent) if element.attr['class'] != 'language-plantuml'
19
+ return super_convert_codeblock(element, indent) unless plantuml?(element)
18
20
 
19
- plantuml = element.value
20
- plantuml_options = @options.key?(:plantuml) ? @options[:plantuml] : {}
21
- diagram = ::Kramdown::PlantUml::Diagram.new(plantuml, plantuml_options)
21
+ jekyll = ::Kramdown::PlantUml::JekyllProvider
22
+
23
+ # If Jekyll is successfully loaded, we'll wait with converting the
24
+ # PlantUML diagram to SVG since a theme may be configured that needs to
25
+ # be copied to the assets directory before the PlantUML conversion can
26
+ # be performed. We therefore place a needle in the haystack that we will
27
+ # convert in the :site:pre_render hook.
28
+ options = ::Kramdown::PlantUml::Options.new(@options)
29
+ return jekyll.needle(element.value, options) if jekyll.installed?
30
+
31
+ convert_plantuml(element.value, options)
32
+ end
33
+
34
+ private
35
+
36
+ def plantuml?(element)
37
+ element.attr['class'] == 'language-plantuml'
38
+ end
39
+
40
+ def convert_plantuml(plantuml, options)
41
+ diagram = ::Kramdown::PlantUml::Diagram.new(plantuml, options)
22
42
  diagram.convert_to_svg
43
+ rescue StandardError => e
44
+ raise e if options.raise_errors?
45
+
46
+ logger.error "Error while replacing needle: #{e.inspect}"
23
47
  end
24
48
  end
25
49
  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.12</version>
14
14
  </dependency>
15
15
  </dependencies>
16
16
  </project>
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kramdown-plantuml
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 1.1.8
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-08-30 00:00:00.000000000 Z
11
+ date: 2021-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: htmlentities
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: kramdown
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -166,20 +180,22 @@ files:
166
180
  - LICENSE
167
181
  - README.md
168
182
  - Rakefile
169
- - bin/net/sourceforge/plantuml/plantuml/1.2021.9/plantuml-1.2021.9.jar
183
+ - bin/net/sourceforge/plantuml/plantuml/1.2021.12/plantuml-1.2021.12.jar
170
184
  - kramdown-plantuml.gemspec
171
185
  - lib/kramdown-plantuml.rb
172
186
  - lib/kramdown-plantuml/bool_env.rb
173
187
  - lib/kramdown-plantuml/console_logger.rb
174
188
  - lib/kramdown-plantuml/diagram.rb
175
189
  - lib/kramdown-plantuml/executor.rb
176
- - lib/kramdown-plantuml/logger.rb
190
+ - lib/kramdown-plantuml/jekyll_provider.rb
191
+ - lib/kramdown-plantuml/log_wrapper.rb
192
+ - lib/kramdown-plantuml/options.rb
177
193
  - lib/kramdown-plantuml/plantuml_error.rb
178
194
  - lib/kramdown-plantuml/plantuml_result.rb
179
195
  - lib/kramdown-plantuml/theme.rb
180
196
  - lib/kramdown-plantuml/version.rb
197
+ - lib/kramdown-plantuml/which.rb
181
198
  - lib/kramdown_html.rb
182
- - lib/which.rb
183
199
  - pom.xml
184
200
  homepage: https://github.com/SwedbankPay/kramdown-plantuml
185
201
  licenses: