kramdown-plantuml 1.0.4 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) 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 +135 -81
  15. data/.github/workflows/shell.yml +11 -0
  16. data/.gitignore +9 -0
  17. data/.rubocop.yml +18 -0
  18. data/Gemfile +4 -8
  19. data/GitVersion.yml +5 -0
  20. data/LICENSE +201 -21
  21. data/README.md +130 -42
  22. data/Rakefile +33 -3
  23. data/bin/{plantuml.1.2020.5.jar → net/sourceforge/plantuml/plantuml/1.2021.8/plantuml-1.2021.8.jar} +0 -0
  24. data/kramdown-plantuml.gemspec +32 -17
  25. data/lib/kramdown-plantuml.rb +4 -6
  26. data/lib/kramdown-plantuml/bool_env.rb +24 -0
  27. data/lib/kramdown-plantuml/console_logger.rb +56 -0
  28. data/lib/kramdown-plantuml/converter.rb +40 -33
  29. data/lib/kramdown-plantuml/executor.rb +48 -0
  30. data/lib/kramdown-plantuml/logger.rb +89 -0
  31. data/lib/kramdown-plantuml/plantuml_error.rb +33 -0
  32. data/lib/kramdown-plantuml/plantuml_result.rb +50 -0
  33. data/lib/kramdown-plantuml/themer.rb +76 -0
  34. data/lib/kramdown-plantuml/version.rb +3 -2
  35. data/lib/kramdown_html.rb +21 -9
  36. data/lib/which.rb +3 -0
  37. data/pom.xml +16 -0
  38. metadata +128 -10
  39. data/bin/console +0 -14
  40. 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", "~> 13.0"
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,45 +1,52 @@
1
- require 'which'
2
- require 'open3'
3
- require_relative 'version'
4
-
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"
1
+ # frozen_string_literal: true
12
2
 
13
- if not File.exists? @plant_uml_jar_file
14
- raise IOError.new("'#{@plant_uml_jar_file}' does not exist")
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
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
24
35
 
25
- stdout, stderr, _ = Open3.capture3(cmd, :stdin_data => content)
36
+ def wrap(svg)
37
+ theme_class = @themer.theme_name ? "theme-#{@themer.theme_name}" : ''
38
+ class_name = "plantuml #{theme_class}".strip
26
39
 
27
- unless stderr.empty?
28
- raise stderr
29
- end
30
-
31
- xml_prologue_start = "<?xml"
32
- xml_prologue_end = "?>"
40
+ wrapper_element_start = "<div class=\"#{class_name}\">"
41
+ wrapper_element_end = '</div>'
33
42
 
34
- start_index = stdout.index(xml_prologue_start)
35
- end_index = stdout.index(xml_prologue_end, xml_prologue_start.length) + xml_prologue_end.length
36
-
37
- stdout.slice! start_index, end_index
38
-
39
- wrapper_element_start = "<div class=\"plantuml\">"
40
- wrapper_element_end = "</div>"
43
+ "#{wrapper_element_start}#{svg}#{wrapper_element_end}"
44
+ end
41
45
 
42
- return "#{wrapper_element_start}#{stdout}#{wrapper_element_end}"
46
+ def log(plantuml)
47
+ @logger.debug ' kramdown-plantuml: PlantUML converting diagram:'
48
+ @logger.debug_with_prefix ' kramdown-plantuml: ', plantuml
49
+ end
43
50
  end
44
51
  end
45
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,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'console_logger'
4
+
5
+ module Kramdown
6
+ module PlantUml
7
+ # Logs stuff
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
+ def level
49
+ @level ||= level_from_logger || self.class.env
50
+ end
51
+
52
+ class << self
53
+ 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
64
+ end
65
+
66
+ def level
67
+ @level ||= level_from_env
68
+ end
69
+
70
+ private
71
+
72
+ def level_from_env
73
+ return :debug if BoolEnv.new('DEBUG').true?
74
+ return :debug if BoolEnv.new('VERBOSE').true?
75
+
76
+ :warn
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def level_from_logger
83
+ return @logger.level if @logger.respond_to? :level
84
+
85
+ nil
86
+ end
87
+ end
88
+ end
89
+ 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