kramdown-plantuml 1.0.1 → 1.1.2

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/codecov.yml +2 -0
  3. data/.github/dependabot.yml +23 -0
  4. data/.github/mergify.yml +22 -0
  5. data/.github/scripts/amend.sh +176 -0
  6. data/.github/scripts/build-gem.sh +6 -0
  7. data/.github/scripts/inspect-gem.sh +72 -0
  8. data/.github/scripts/publish-gem.sh +121 -0
  9. data/.github/scripts/test-gem.sh +170 -0
  10. data/.github/scripts/variables.sh +72 -0
  11. data/.github/workflows/amend.yml +19 -0
  12. data/.github/workflows/no-java.yml +3 -6
  13. data/.github/workflows/no-plantuml.yml +6 -9
  14. data/.github/workflows/ruby.yml +139 -86
  15. data/.github/workflows/shell.yml +11 -0
  16. data/.gitignore +9 -0
  17. data/.rubocop.yml +18 -0
  18. data/CODE_OF_CONDUCT.md +11 -9
  19. data/Gemfile +4 -8
  20. data/GitVersion.yml +5 -0
  21. data/LICENSE +201 -21
  22. data/README.md +143 -35
  23. data/Rakefile +33 -3
  24. data/bin/{plantuml.1.2020.5.jar → net/sourceforge/plantuml/plantuml/1.2021.8/plantuml-1.2021.8.jar} +0 -0
  25. data/kramdown-plantuml.gemspec +32 -17
  26. data/lib/kramdown-plantuml.rb +4 -6
  27. data/lib/kramdown-plantuml/bool_env.rb +24 -0
  28. data/lib/kramdown-plantuml/console_logger.rb +56 -0
  29. data/lib/kramdown-plantuml/converter.rb +39 -21
  30. data/lib/kramdown-plantuml/executor.rb +48 -0
  31. data/lib/kramdown-plantuml/hash.rb +14 -0
  32. data/lib/kramdown-plantuml/logger.rb +77 -0
  33. data/lib/kramdown-plantuml/plantuml_error.rb +33 -0
  34. data/lib/kramdown-plantuml/plantuml_result.rb +50 -0
  35. data/lib/kramdown-plantuml/themer.rb +64 -0
  36. data/lib/kramdown-plantuml/version.rb +3 -2
  37. data/lib/kramdown_html.rb +21 -9
  38. data/lib/which.rb +3 -0
  39. data/pom.xml +16 -0
  40. metadata +131 -12
  41. data/bin/console +0 -14
  42. data/bin/setup +0 -8
data/Rakefile CHANGED
@@ -1,10 +1,40 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rake'
2
4
  require 'bundler/gem_tasks'
3
5
  require 'rspec/core/rake_task'
4
6
 
5
7
  RSpec::Core::RakeTask.new(:spec) do |t|
6
- t.pattern = Dir.glob('spec/**/*_spec.rb')
7
- t.rspec_opts = '--format documentation --tag ~no_plantuml --tag ~no_java'
8
+ t.pattern = Dir.glob('spec/**/*_spec.rb')
9
+ t.rspec_opts = '--format documentation --tag ~no_plantuml --tag ~no_java --tag ~debug'
10
+ end
11
+
12
+ namespace :maven do
13
+ task :install do
14
+ require 'fileutils'
15
+
16
+ system 'mvn install'
17
+ bin_dir = File.join __dir__, 'bin'
18
+ target_file = File.join bin_dir, 'plantuml.jar'
19
+ repo_dir = File.expand_path '~/.m2/repository'
20
+ jar_glob = File.join repo_dir, '/**/plantuml*.jar'
21
+ first_jar = Dir[jar_glob].first
22
+ jar_file = File.expand_path first_jar
23
+
24
+ FileUtils.mkdir_p bin_dir
25
+ FileUtils.move(jar_file, target_file)
26
+ end
27
+ end
28
+
29
+ namespace :codecov do
30
+ desc 'Uploads the latest SimpleCov result set to codecov.io'
31
+ task :upload do
32
+ require 'simplecov'
33
+ require 'codecov'
34
+
35
+ formatter = SimpleCov::Formatter::Codecov.new
36
+ formatter.format(SimpleCov::ResultMerger.merged_result)
37
+ end
8
38
  end
9
39
 
10
- task :default => :spec
40
+ task default: :spec
@@ -1,33 +1,48 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'lib/which'
2
4
  require_relative 'lib/kramdown-plantuml/version'
3
5
 
4
6
  Gem::Specification.new do |spec|
5
- spec.name = "kramdown-plantuml"
7
+ spec.name = 'kramdown-plantuml'
6
8
  spec.version = Kramdown::PlantUml::VERSION
7
- spec.authors = ["Asbjørn Ulsberg"]
8
- spec.email = ["asbjorn@ulsberg.no"]
9
+ spec.authors = ['Swedbank Pay']
10
+ spec.email = ['opensource@swedbankpay.com']
9
11
 
10
- spec.summary = "Short summary"
11
- spec.homepage = "https://github.com/SwedbankPay/kramdown-plantuml"
12
- spec.license = "MIT"
13
- spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
12
+ spec.summary = <<~SUMMARY
13
+ kramdown-plantuml allows you to use PlantUML syntax within fenced code
14
+ blocks with Kramdown (Jekyll's default Markdown parser).
15
+ SUMMARY
16
+ spec.homepage = 'https://github.com/SwedbankPay/kramdown-plantuml'
17
+ spec.license = 'MIT'
18
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
14
19
 
15
- spec.metadata["homepage_uri"] = spec.homepage
16
- spec.metadata["source_code_uri"] = "https://github.com/SwedbankPay/kramdown-plantuml"
20
+ spec.metadata['homepage_uri'] = spec.homepage
21
+ spec.metadata['source_code_uri'] = 'https://github.com/SwedbankPay/kramdown-plantuml'
17
22
 
18
23
  # Specify which files should be added to the gem when it is released.
19
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
21
- if Which::which('git')
22
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into Git.
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ if Which.which('git')
27
+ files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
28
+ # Explicitly add plantuml.jar to the list of files as it is not committed to Git.
29
+ files.append(Dir['bin/**/plantuml*.jar'].first)
23
30
  else
24
31
  puts "Git not found, no files added to #{spec.name}."
25
32
  end
26
33
  end
27
- spec.bindir = "exe"
34
+ spec.bindir = 'exe'
28
35
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
- spec.require_paths = ["lib"]
36
+ spec.require_paths = ['lib']
37
+
38
+ spec.add_dependency 'kramdown', '~> 2.3'
39
+ spec.add_dependency 'kramdown-parser-gfm', '~> 1.1'
40
+ spec.add_dependency 'open3', '~> 0.1'
30
41
 
31
- spec.add_development_dependency "rake", "~> 12.3"
32
- spec.add_development_dependency "rspec", "~> 3.2"
42
+ spec.add_development_dependency 'rake', '~> 13.0'
43
+ spec.add_development_dependency 'rspec', '~> 3.2'
44
+ spec.add_development_dependency 'rspec-its', '~> 1.3'
45
+ spec.add_development_dependency 'rubocop', '~> 1.12'
46
+ spec.add_development_dependency 'rubocop-rake', '~> 0.6'
47
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.4'
33
48
  end
@@ -1,7 +1,5 @@
1
- require 'kramdown-plantuml/version'
2
- require 'kramdown-plantuml/converter'
3
- require 'kramdown_html'
1
+ # frozen_string_literal: true
4
2
 
5
- module Kramdown::PlantUml
6
- class Error < StandardError; end
7
- end
3
+ require_relative 'kramdown-plantuml/version'
4
+ require_relative 'kramdown-plantuml/converter'
5
+ require_relative 'kramdown_html'
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kramdown
4
+ module PlantUml
5
+ # Converts envrionment variables to boolean values
6
+ class BoolEnv
7
+ TRUTHY_VALUES = %w[t true yes y 1].freeze
8
+ FALSEY_VALUES = %w[f false n no 0].freeze
9
+
10
+ def initialize(name)
11
+ @name = name
12
+ value = ENV.fetch(name, nil)
13
+ @value = value.to_s.downcase unless value.nil?
14
+ end
15
+
16
+ def true?
17
+ return true if TRUTHY_VALUES.include?(@value)
18
+ return false if FALSEY_VALUES.include?(@value) || @value.nil? || value.empty?
19
+
20
+ raise "The value '#{@value}' of '#{@name}' can't be converted to a boolean"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'bool_env'
4
+
5
+ module Kramdown
6
+ module PlantUml
7
+ # Logs to $stdout and $stderr
8
+ class ConsoleLogger
9
+ LOG_LEVELS = %i[debug info warn error].freeze
10
+
11
+ def initialize(level)
12
+ @configured_log_level = level
13
+ end
14
+
15
+ def debug(message)
16
+ write(:debug, message)
17
+ end
18
+
19
+ def info(message)
20
+ write(:info, message)
21
+ end
22
+
23
+ def warn(message)
24
+ write(:warn, message)
25
+ end
26
+
27
+ def error(message)
28
+ write(:error, message)
29
+ end
30
+
31
+ private
32
+
33
+ def write(level, message)
34
+ return false unless write_message?(level)
35
+
36
+ pipe = pipe_for(level)
37
+ pipe.write("#{message}\n")
38
+ end
39
+
40
+ def write_message?(level_of_message)
41
+ LOG_LEVELS.index(@configured_log_level) <= LOG_LEVELS.index(level_of_message)
42
+ end
43
+
44
+ def pipe_for(level)
45
+ case level
46
+ when :debug, :info
47
+ $stdout
48
+ when :warn, :error
49
+ $stderr
50
+ else
51
+ raise ArgumentError, "Unknown log level '#{level}'."
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,34 +1,52 @@
1
- require 'which'
2
- require 'open3'
3
- require_relative 'version'
1
+ # frozen_string_literal: true
4
2
 
5
- module Kramdown::PlantUml
6
- class Converter
7
- def initialize
8
- dir = File.dirname __dir__
9
- bin = File.join dir, "../bin"
10
- bin = File.expand_path bin
11
- @plant_uml_jar_file = File.join bin, "plantuml.1.2020.5.jar"
3
+ require_relative 'version'
4
+ require_relative 'themer'
5
+ require_relative 'plantuml_error'
6
+ require_relative 'logger'
7
+ require_relative 'executor'
12
8
 
13
- if not File.exists? @plant_uml_jar_file
14
- raise IOError.new("'#{@plant_uml_jar_file}' does not exist")
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
15
17
  end
16
18
 
17
- unless Which::which("java")
18
- raise IOError.new("Java can not be found")
19
+ def convert_plantuml_to_svg(plantuml)
20
+ if plantuml.nil? || plantuml.empty?
21
+ @logger.warn 'kramdown-plantuml: PlantUML diagram is empty'
22
+ return plantuml
23
+ end
24
+
25
+ plantuml = @themer.apply_theme(plantuml)
26
+ plantuml = plantuml.strip
27
+ log(plantuml)
28
+ result = @executor.execute(plantuml)
29
+ result.validate(plantuml)
30
+ svg = result.without_xml_prologue
31
+ wrap(svg)
19
32
  end
20
- end
21
33
 
22
- def convert_plantuml_to_svg(content)
23
- cmd = "java -jar #{@plant_uml_jar_file} -tsvg -pipe"
34
+ private
35
+
36
+ def wrap(svg)
37
+ theme_class = @themer.theme_name ? "theme-#{@themer.theme_name}" : ''
38
+ class_name = "plantuml #{theme_class}".strip
24
39
 
25
- stdout, stderr, status = Open3.capture3(cmd, :stdin_data => content)
40
+ wrapper_element_start = "<div class=\"#{class_name}\">"
41
+ wrapper_element_end = '</div>'
26
42
 
27
- unless stderr.empty?
28
- raise stderr
43
+ "#{wrapper_element_start}#{svg}#{wrapper_element_end}"
29
44
  end
30
45
 
31
- return stdout
46
+ def log(plantuml)
47
+ @logger.debug 'kramdown-plantuml: PlantUML converting diagram:'
48
+ @logger.debug_with_prefix 'kramdown-plantuml: ', plantuml
49
+ end
32
50
  end
33
51
  end
34
52
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'open3'
4
+ require_relative '../which'
5
+ require_relative 'logger'
6
+ require_relative 'plantuml_result'
7
+
8
+ module Kramdown
9
+ module PlantUml
10
+ # Executes the PlantUML Java application.
11
+ class Executor
12
+ def initialize
13
+ @logger = Logger.init
14
+ @plantuml_jar_file = find_plantuml_jar_file
15
+
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
+ raise IOError, "'#{@plantuml_jar_file}' does not exist" unless File.exist? @plantuml_jar_file
19
+ end
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
28
+
29
+ @logger.debug "kramdown-plantuml: Executing '#{cmd}'."
30
+
31
+ stdout, stderr, status = Open3.capture3 cmd, stdin_data: stdin
32
+
33
+ @logger.debug "kramdown-plantuml: PlantUML exit code '#{status.exitstatus}'."
34
+
35
+ PlantUmlResult.new(stdout, stderr, status)
36
+ end
37
+
38
+ private
39
+
40
+ def find_plantuml_jar_file
41
+ dir = File.dirname __dir__
42
+ jar_glob = File.join dir, '../bin/**/plantuml*.jar'
43
+ first_jar = Dir[jar_glob].first
44
+ File.expand_path first_jar unless first_jar.nil?
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,14 @@
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
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'console_logger'
4
+
5
+ module Kramdown
6
+ module PlantUml
7
+ # Provides theming support for PlantUML
8
+ class Logger
9
+ def initialize(logger)
10
+ raise ArgumentError, 'logger cannot be nil' if logger.nil?
11
+ raise ArgumentError, 'logger must respond to #debug' unless logger.respond_to? :debug
12
+ raise ArgumentError, 'logger must respond to #info' unless logger.respond_to? :info
13
+ raise ArgumentError, 'logger must respond to #warn' unless logger.respond_to? :warn
14
+ raise ArgumentError, 'logger must respond to #error' unless logger.respond_to? :error
15
+
16
+ @logger = logger
17
+ end
18
+
19
+ def debug(message)
20
+ @logger.debug message
21
+ end
22
+
23
+ def debug_with_prefix(prefix, string)
24
+ return if string.nil? || string.empty?
25
+
26
+ lines = string.lines
27
+ lines.each do |line|
28
+ @logger.debug "#{prefix}#{line.rstrip}"
29
+ end
30
+ end
31
+
32
+ def info(message)
33
+ @logger.info message
34
+ end
35
+
36
+ def warn(message)
37
+ @logger.warn message
38
+ end
39
+
40
+ def error(message)
41
+ @logger.error message
42
+ end
43
+
44
+ def debug?
45
+ self.class.level == :debug
46
+ end
47
+
48
+ class << self
49
+ def init
50
+ inner = nil
51
+
52
+ begin
53
+ require 'jekyll'
54
+ inner = Jekyll.logger
55
+ rescue LoadError
56
+ inner = ConsoleLogger.new level
57
+ end
58
+
59
+ Logger.new inner
60
+ end
61
+
62
+ def level
63
+ @level ||= level_from_env
64
+ end
65
+
66
+ private
67
+
68
+ def level_from_env
69
+ return :debug if BoolEnv.new('DEBUG').true?
70
+ return :debug if BoolEnv.new('VERBOSE').true?
71
+
72
+ :warn
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kramdown
4
+ module PlantUml
5
+ # PlantUML Error
6
+ class PlantUmlError < StandardError
7
+ def initialize(plantuml, stderr, exitcode)
8
+ message = <<~MESSAGE
9
+ Conversion of the following PlantUML diagram failed:
10
+
11
+ #{plantuml}
12
+
13
+ The error received from PlantUML was:
14
+
15
+ Exit code: #{exitcode}
16
+ #{stderr}
17
+ MESSAGE
18
+
19
+ super message
20
+ end
21
+
22
+ def self.should_raise?(exitcode, stderr)
23
+ return false if exitcode.zero?
24
+
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:')
30
+ end
31
+ end
32
+ end
33
+ end