sevgi-showcase 0.94.0 → 0.98.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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +275 -2
  3. data/LICENSE +672 -3
  4. data/README.md +9 -17
  5. data/lib/sevgi/showcase/dark.rb +4 -0
  6. data/lib/sevgi/showcase/minitest/script.rb +59 -57
  7. data/lib/sevgi/showcase/minitest/shell.rb +136 -113
  8. data/lib/sevgi/showcase/minitest/suite.rb +48 -46
  9. data/lib/sevgi/showcase/minitest.rb +6 -0
  10. data/lib/sevgi/showcase/version.rb +1 -1
  11. data/lib/sevgi/showcase.rb +1 -2
  12. data/srv/checkers.sevgi +79 -0
  13. data/srv/checkers.svg +117 -0
  14. data/srv/clover.sevgi +19 -0
  15. data/srv/clover.svg +11 -0
  16. data/srv/clover.yml +3 -0
  17. data/srv/copperplate.sevgi +50 -0
  18. data/srv/copperplate.svg +99 -0
  19. data/srv/copperplate.yml +5 -0
  20. data/srv/{gear-wheel.sevgi → gear.sevgi} +7 -9
  21. data/srv/gear.svg +11 -0
  22. data/srv/gear.yml +2 -0
  23. data/srv/{grid-cells.sevgi → grid.sevgi} +9 -9
  24. data/srv/{grid-cells.svg → grid.svg} +13 -1
  25. data/srv/heart.sevgi +17 -0
  26. data/srv/{heart-mask.svg → heart.svg} +3 -3
  27. data/srv/heart.yml +3 -0
  28. data/srv/logo.sevgi +39 -0
  29. data/srv/logo.svg +29 -0
  30. data/srv/logo.yml +4 -0
  31. data/srv/logos.sevgi +34 -0
  32. data/srv/logos.svg +38 -0
  33. data/srv/logos.yml +4 -0
  34. data/srv/{meter-face.sevgi → meter.sevgi} +6 -6
  35. data/srv/meter.svg +18 -0
  36. data/srv/pacman.sevgi +18 -0
  37. data/srv/pacman.svg +15 -0
  38. data/srv/pokey.sevgi +14 -0
  39. data/srv/pokey.svg +6 -0
  40. data/srv/{ruler-line.sevgi → ruler-.sevgi} +7 -7
  41. data/srv/ruler.sevgi +61 -0
  42. data/srv/ruler.svg +445 -0
  43. data/srv/{snow-flake.sevgi → snowflake.sevgi} +2 -2
  44. data/srv/{snow-flake.svg → snowflake.svg} +1 -1
  45. data/srv/snowflake.yml +2 -0
  46. data/srv/squared.sevgi +42 -0
  47. data/srv/squared.svg +71 -0
  48. data/srv/squared.yml +4 -0
  49. data/srv/stars.sevgi +19 -0
  50. data/srv/stars.svg +30 -0
  51. data/srv/stars.yml +3 -0
  52. data/srv/tulips.sevgi +27 -0
  53. data/srv/tulips.svg +19 -0
  54. data/srv/tulips.yml +3 -0
  55. metadata +57 -36
  56. data/srv/checker-board.sevgi +0 -96
  57. data/srv/checker-board.svg +0 -117
  58. data/srv/gear-wheel.svg +0 -12
  59. data/srv/gear-wheel.yml +0 -3
  60. data/srv/heart-mask.sevgi +0 -17
  61. data/srv/heart-mask.yml +0 -3
  62. data/srv/meter-face.svg +0 -13
  63. data/srv/pacman-pokey.sevgi +0 -17
  64. data/srv/pacman-pokey.svg +0 -9
  65. data/srv/pacman-single.sevgi +0 -17
  66. data/srv/pacman-single.svg +0 -14
  67. data/srv/ruler-hline.sevgi +0 -55
  68. data/srv/ruler-hline.svg +0 -443
  69. data/srv/snow-flake.yml +0 -1
  70. /data/srv/{checker-board.yml → checkers.yml} +0 -0
  71. /data/srv/{grid-cells.yml → grid.yml} +0 -0
  72. /data/srv/{meter-face.yml → meter.yml} +0 -0
  73. /data/srv/{pacman-pokey.yml → pacman.yml} +0 -0
  74. /data/srv/{pacman-single.yml → pokey.yml} +0 -0
  75. /data/srv/{ruler-line.svg → ruler-.svg} +0 -0
  76. /data/srv/{ruler-hline.yml → ruler-.yml} +0 -0
  77. /data/srv/{ruler-line.yml → ruler.yml} +0 -0
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Sevgi Showcase
2
2
 
3
- Executable examples, rendered outputs, and documentation-site support.
3
+ Sevgi Showcase packages the executable examples, their SVG output, and support code for the documentation site.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,22 +8,14 @@ Executable examples, rendered outputs, and documentation-site support.
8
8
  gem install sevgi-showcase
9
9
  ```
10
10
 
11
- ## Require
11
+ ## Contents
12
12
 
13
- ```ruby
14
- require "sevgi/showcase"
15
- ```
16
-
17
- ## Example
18
-
19
- ```ruby
20
- suite = Sevgi::Test::Suite.new("srv")
21
- suite.valids.map(&:name)
22
- ```
13
+ The gem is an asset package rather than a runtime API. It contains the executable `.sevgi` examples, their expected
14
+ SVG output, and private build support used by the documentation site.
23
15
 
24
16
  ## Ruby compatibility
25
17
 
26
- Requires Ruby 3.4.0 or newer. CI verifies Ruby 3.4 and the current development Ruby from `.ruby-version`.
18
+ Requires Ruby 3.4.0 or newer. CI verifies the current Ruby 3.4 release and the development Ruby from `.ruby-version`.
27
19
 
28
20
  ## Native prerequisites
29
21
 
@@ -47,7 +39,7 @@ gem install cairo rsvg2 hexapdf
47
39
 
48
40
  ## Links
49
41
 
50
- - Documentation: https://sevgi.roktas.dev
51
- - API documentation: https://www.rubydoc.info/gems/sevgi-showcase
52
- - Source: https://github.com/roktas/sevgi/tree/main/showcase
53
- - Changelog: https://github.com/roktas/sevgi/blob/main/CHANGELOG.md
42
+ - Documentation: <https://sevgi.roktas.dev>
43
+ - API documentation: <https://www.rubydoc.info/gems/sevgi-showcase>
44
+ - Source: <https://github.com/roktas/sevgi/tree/main/showcase>
45
+ - Changelog: <https://github.com/roktas/sevgi/blob/main/CHANGELOG.md>
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../showcase"
4
+
3
5
  module Sevgi
4
6
  module Showcase
5
7
  # Applies dark-theme color mappings to showcase source files.
@@ -60,5 +62,7 @@ module Sevgi
60
62
  end
61
63
  end
62
64
  end
65
+
66
+ private_constant :Dark
63
67
  end
64
68
  end
@@ -1,78 +1,80 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sevgi
4
- # Minitest helpers used by the showcase component.
5
- # @api private
6
- module Test
7
- # Executable showcase script descriptor.
4
+ module Showcase
5
+ # Minitest helpers used by the showcase component.
8
6
  # @api private
9
- class Script
10
- # @return [String] absolute script path
11
- attr_reader :file
7
+ module Test
8
+ # Executable showcase script descriptor.
9
+ # @api private
10
+ class Script
11
+ # @return [String] absolute script path
12
+ attr_reader :file
12
13
 
13
- # Creates a script descriptor.
14
- # @param file [String] executable .sevgi file
15
- # @return [void]
16
- # @raise [Sevgi::ArgumentError] when the file is missing or not executable
17
- def initialize(file) = @file = ::File.expand_path(sanitize(file))
14
+ # Creates a script descriptor.
15
+ # @param file [String] executable .sevgi file
16
+ # @return [void]
17
+ # @raise [Sevgi::ArgumentError] when the file is missing or not executable
18
+ def initialize(file) = @file = ::File.expand_path(sanitize(file))
18
19
 
19
- # Returns the script directory.
20
- # @return [String]
21
- def dir = @dir ||= ::File.dirname(file)
20
+ # Returns the script directory.
21
+ # @return [String]
22
+ def dir = @dir ||= ::File.dirname(file)
22
23
 
23
- # Returns the script basename.
24
- # @return [String]
25
- def file! = ::File.basename(file)
24
+ # Returns the script basename.
25
+ # @return [String]
26
+ def file! = ::File.basename(file)
26
27
 
27
- # Returns the script name without extension.
28
- # @return [String]
29
- def name = @name ||= ::File.basename(file, ".*")
28
+ # Returns the script name without extension.
29
+ # @return [String]
30
+ def name = @name ||= ::File.basename(file, ".*")
30
31
 
31
- # @overload run(*args)
32
- # Runs the script directly.
33
- # @param args [Array<String>] extra command arguments
34
- # @return [Sevgi::Test::Shell::Result]
35
- def run(*) = Shell.run(file, *)
32
+ # @overload run(*args)
33
+ # Runs the script directly.
34
+ # @param args [Array<String>] extra command arguments
35
+ # @return [Sevgi::Showcase::Test::Shell::Result]
36
+ def run(*) = Shell.run(file, *)
36
37
 
37
- # Returns the script suite name.
38
- # @return [String]
39
- def suite = @suite ||= ::File.basename(dir)
38
+ # Returns the script suite name.
39
+ # @return [String]
40
+ def suite = @suite ||= ::File.basename(dir)
40
41
 
41
- # Reports whether the expected SVG output exists.
42
- # @return [Boolean]
43
- def svg? = ::File.exist?(svg)
42
+ # Reports whether the expected SVG output exists.
43
+ # @return [Boolean]
44
+ def svg? = ::File.exist?(svg)
44
45
 
45
- # Returns the expected SVG output path.
46
- # @return [String]
47
- def svg = @svg ||= ::File.expand_path("#{dir}/#{name}.svg")
46
+ # Returns the expected SVG output path.
47
+ # @return [String]
48
+ def svg = @svg ||= ::File.expand_path("#{dir}/#{name}.svg")
48
49
 
49
- # Returns the expected SVG output basename.
50
- # @return [String]
51
- def svg! = ::File.basename(svg)
50
+ # Returns the expected SVG output basename.
51
+ # @return [String]
52
+ def svg! = ::File.basename(svg)
52
53
 
53
- # Returns the optional YAML metadata path.
54
- # @return [String]
55
- def yml = @yml ||= ::File.expand_path("#{dir}/#{name}.yml")
54
+ # Returns the optional YAML metadata path.
55
+ # @return [String]
56
+ def yml = @yml ||= ::File.expand_path("#{dir}/#{name}.yml")
56
57
 
57
- # Returns the optional YAML metadata basename.
58
- # @return [String]
59
- def yml! = ::File.basename(yml)
58
+ # Returns the optional YAML metadata basename.
59
+ # @return [String]
60
+ def yml! = ::File.basename(yml)
60
61
 
61
- # @overload run_passive(*args)
62
- # Runs the script without writing Save output to files.
63
- # @param args [Array<String>] extra command arguments
64
- # @return [Sevgi::Test::Shell::Result]
65
- def run_passive(*)
66
- warn("...#{name}")
67
- Shell.run("sevgi", "-r", "sevgi/showcase/passive", file, *)
68
- end
62
+ # @overload run_passive(*args)
63
+ # Runs the script without writing Save output to files.
64
+ # @param args [Array<String>] extra command arguments
65
+ # @return [Sevgi::Showcase::Test::Shell::Result]
66
+ def run_passive(*)
67
+ warn("...#{name}")
68
+ Shell.run("sevgi", "-r", "sevgi/showcase/passive", file, *)
69
+ end
69
70
 
70
- private
71
+ private
71
72
 
72
- def sanitize(file)
73
- file.tap do
74
- ArgumentError.("No such file: #{file}") unless ::File.exist?(file)
75
- ArgumentError.("Not an executable: #{file}") unless ::File.executable?(file)
73
+ def sanitize(file)
74
+ file.tap do
75
+ ArgumentError.("No such file: #{file}") unless ::File.exist?(file)
76
+ ArgumentError.("Not an executable: #{file}") unless ::File.executable?(file)
77
+ end
76
78
  end
77
79
  end
78
80
  end
@@ -1,147 +1,170 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "open3"
4
+ require "sevgi/function"
4
5
 
5
6
  module Sevgi
6
- # Minitest helpers used by the showcase component.
7
- # @api private
8
- module Test
9
- # Shell runner helpers for showcase tests.
7
+ module Showcase
8
+ # Minitest helpers used by the showcase component.
10
9
  # @api private
11
- module Shell
12
- # Shell command result.
10
+ module Test
11
+ # Shell runner helpers for showcase tests.
13
12
  # @api private
14
- Result = Data.define(:args, :out, :err, :exit_code) do
15
- # Returns the command string.
16
- # @return [String]
17
- def cmd = args.join(" ")
18
-
19
- # Reports whether the command failed.
20
- # @return [Boolean]
21
- def notok? = !ok?
22
-
23
- # Reports whether the command exited successfully.
24
- # @return [Boolean]
25
- def ok? = exit_code&.zero?
26
-
27
- # Returns the first stdout line.
28
- # @return [String, nil]
29
- def outline = out.first
30
-
31
- # Returns stdout as a newline-joined string.
32
- # @return [String]
33
- def to_s = out.join("\n")
34
- end
13
+ module Shell
14
+ # Shared process-global SIGINT state.
15
+ # @api private
16
+ Signals = ::Sevgi::Function::Shell.const_get(:Signals, false)
35
17
 
36
- # Adapted to popen3 from github.com/mina-deploy/mina
37
- # Shell command runner.
38
- # @api private
39
- class Runner
40
- # Creates a runner.
41
- # @return [void]
42
- def initialize
43
- @coathooks = 0
18
+ private_constant :Signals
19
+
20
+ # Shell command result.
21
+ # @api private
22
+ Result = Data.define(:args, :out, :err, :exit_code) do
23
+ private_class_method :[]
24
+
25
+ # Returns the command string.
26
+ # @return [String]
27
+ def cmd = args.join(" ")
28
+
29
+ # Reports whether the command failed.
30
+ # @return [Boolean]
31
+ def notok? = !ok?
32
+
33
+ # Reports whether the command exited successfully.
34
+ # @return [Boolean]
35
+ def ok? = exit_code&.zero?
36
+
37
+ # Returns the first stdout line.
38
+ # @return [String, nil]
39
+ def outline = out.first
40
+
41
+ # Returns stdout as a newline-joined string.
42
+ # @return [String]
43
+ def to_s = out.join("\n")
44
44
  end
45
45
 
46
- # Runs a command and captures stdout, stderr, and exit status.
47
- # @param args [Array<String>] command and arguments
48
- # @yield optional stdin writer evaluated with stdin as receiver
49
- # @yieldreturn [Object]
50
- # @return [Sevgi::Test::Shell::Result]
51
- # @raise [StandardError] when the input block raises; the child is terminated and reaped before propagation
52
- def run(*args, &block)
53
- out, err, status = Open3.popen3(*args) do |stdin, stdout, stderr, thread|
54
- capture(stdin, stdout, stderr, thread, &block)
46
+ # Adapted to popen3 from github.com/mina-deploy/mina
47
+ # Shell command runner.
48
+ # @api private
49
+ class Runner
50
+ # Creates a runner.
51
+ # @return [void]
52
+ def initialize
53
+ @coathooks = 0
55
54
  end
56
55
 
57
- Result[args, out, err, status.exitstatus]
58
- end
56
+ # Runs a command and captures stdout, stderr, and exit status.
57
+ # @param args [Array<String>] command and arguments
58
+ # @yield optional stdin writer evaluated with stdin as receiver
59
+ # @yieldreturn [Object]
60
+ # @return [Sevgi::Showcase::Test::Shell::Result]
61
+ # @raise [StandardError] when the input block raises; the child is terminated and reaped before propagation
62
+ # @note The first SIGINT sends TERM and the second sends KILL outside trap context. The handler is restored after
63
+ # the last active run.
64
+ def run(*args, &block)
65
+ @coathooks = 0
66
+ out, err, status = Open3.popen3(*args) do |stdin, stdout, stderr, thread|
67
+ capture(stdin, stdout, stderr, thread, &block)
68
+ end
69
+
70
+ Result.new(args:, out:, err:, exit_code: status.exitstatus)
71
+ end
59
72
 
60
- private
73
+ private
61
74
 
62
- # rubocop:disable Lint/RescueException
63
- def capture(stdin, stdout, stderr, thread, &block)
64
- trap = trap("INT") { handle_sigint(thread.pid) }
65
- readers = outputs(stdout, stderr)
75
+ # rubocop:disable Lint/RescueException
76
+ def capture(stdin, stdout, stderr, thread, &block)
77
+ register_capture(thread)
78
+ registered = true
79
+ readers = outputs(stdout, stderr)
66
80
 
67
- collect_capture(stdin, thread, readers, &block)
68
- rescue Exception
69
- cleanup_failed_capture(stdin, thread, readers)
70
- raise
71
- ensure
72
- close_input(stdin)
73
- trap("INT", trap) if trap
74
- end
75
- # rubocop:enable Lint/RescueException
81
+ collect_capture(stdin, thread, readers, &block)
82
+ rescue Exception
83
+ cleanup_failed_capture(stdin, thread, readers)
84
+ raise
85
+ ensure
86
+ finish_capture(stdin, registered)
87
+ end
88
+ # rubocop:enable Lint/RescueException
76
89
 
77
- def cleanup_failed_capture(stdin, thread, readers)
78
- close_input(stdin)
79
- stop_process(thread)
80
- Array(readers).each(&:join)
81
- end
90
+ def finish_capture(stdin, registered)
91
+ close_input(stdin)
92
+ Signals.unregister(self) if registered
93
+ end
82
94
 
83
- def close_input(stdin)
84
- stdin.close unless stdin.closed?
85
- rescue IOError
86
- nil
87
- end
95
+ def register_capture(thread)
96
+ Signals.register(self, thread.pid)
97
+ end
88
98
 
89
- def collect_capture(stdin, thread, readers, &block)
90
- inputs(stdin, thread, &block)
91
- close_input(stdin)
99
+ def cleanup_failed_capture(stdin, thread, readers)
100
+ close_input(stdin)
101
+ stop_process(thread)
102
+ Array(readers).each(&:join)
103
+ end
92
104
 
93
- [readers[0].value, readers[1].value, thread.value]
94
- end
105
+ def close_input(stdin)
106
+ stdin.close unless stdin.closed?
107
+ rescue IOError
108
+ nil
109
+ end
110
+
111
+ def collect_capture(stdin, thread, readers, &block)
112
+ inputs(stdin, thread, &block)
113
+ close_input(stdin)
95
114
 
96
- def handle_sigint(pid)
97
- message, signal = if @coathooks > 1
98
- ["SIGINT received again. Force quitting...", "KILL"]
99
- else
100
- ["SIGINT received.", "TERM"]
115
+ [readers[0].value, readers[1].value, thread.value]
101
116
  end
102
117
 
103
- warn("\n#{message}")
104
- ::Process.kill(signal, pid)
105
- @coathooks += 1
106
- rescue Errno::ESRCH
107
- warn("No process to kill.")
108
- end
118
+ def handle_sigint(pid)
119
+ @coathooks += 1
120
+ message, signal = if @coathooks > 1
121
+ ["SIGINT received again. Force quitting...", "KILL"]
122
+ else
123
+ ["SIGINT received.", "TERM"]
124
+ end
125
+
126
+ warn("\n#{message}")
127
+ ::Process.kill(signal, pid)
128
+ rescue Errno::ESRCH
129
+ warn("No process to kill.")
130
+ end
109
131
 
110
- def inputs(stdin, thread, &block)
111
- stdin.instance_exec(thread, &block) if block
112
- end
132
+ def inputs(stdin, thread, &block)
133
+ stdin.instance_exec(thread, &block) if block
134
+ end
113
135
 
114
- def kill_process(signal, pid)
115
- ::Process.kill(signal, pid)
116
- rescue Errno::ESRCH
117
- nil
118
- end
136
+ def kill_process(signal, pid)
137
+ ::Process.kill(signal, pid)
138
+ rescue Errno::ESRCH
139
+ nil
140
+ end
119
141
 
120
- def outputs(stdout, stderr)
121
- [
122
- Thread.new { stdout.readlines.map(&:chomp) },
123
- Thread.new { stderr.readlines.map(&:chomp) }
124
- ]
125
- end
142
+ def outputs(stdout, stderr)
143
+ [
144
+ Thread.new { stdout.readlines.map(&:chomp) },
145
+ Thread.new { stderr.readlines.map(&:chomp) }
146
+ ]
147
+ end
126
148
 
127
- def stop_process(thread)
128
- return unless thread&.alive?
149
+ def stop_process(thread)
150
+ return unless thread&.alive?
129
151
 
130
- kill_process("TERM", thread.pid)
131
- return if thread.join(1)
152
+ kill_process("TERM", thread.pid)
153
+ return if thread.join(1)
132
154
 
133
- kill_process("KILL", thread.pid)
134
- thread.join
155
+ kill_process("KILL", thread.pid)
156
+ thread.join
157
+ end
135
158
  end
136
- end
137
159
 
138
- extend self
160
+ extend self
139
161
 
140
- # @overload run(*args)
141
- # Runs a command through a fresh runner.
142
- # @param args [Array<String>] command and arguments
143
- # @return [Sevgi::Test::Shell::Result]
144
- def run(...) = Runner.new.run(...)
162
+ # @overload run(*args)
163
+ # Runs a command through a fresh runner.
164
+ # @param args [Array<String>] command and arguments
165
+ # @return [Sevgi::Showcase::Test::Shell::Result]
166
+ def run(...) = Runner.new.run(...)
167
+ end
145
168
  end
146
169
  end
147
170
  end
@@ -1,53 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sevgi
4
- # Minitest helpers used by the showcase component.
5
- # @api private
6
- module Test
7
- # Showcase script suite collection.
4
+ module Showcase
5
+ # Minitest helpers used by the showcase component.
8
6
  # @api private
9
- class Suite
10
- # Creates a suite from a showcase root directory.
11
- # @param rootdir [String] showcase root directory
12
- # @return [void]
13
- def initialize(rootdir)
14
- @scripts = load(rootdir)
15
- end
16
-
17
- # Returns scripts belonging to a suite.
18
- # @param suite [String, Symbol] suite name
19
- # @return [Array<Sevgi::Test::Script>]
20
- def [](suite)
21
- (@cache ||= {})[suite] ||= @scripts.select { |script| script.suite == suite.to_s }
22
- end
23
-
24
- # Returns all suite names.
25
- # @return [Array<String>]
26
- def suites
27
- @suites ||= @scripts.map(&:suite).uniq
28
- end
29
-
30
- # Suite names that are expected to be invalid examples.
31
- NON_VALIDS = ["gotcha"].freeze
32
-
33
- # Returns scripts expected to be invalid.
34
- # @return [Array<Sevgi::Test::Script>]
35
- def non_valids
36
- NON_VALIDS.map { self[it] }.flatten
37
- end
38
-
39
- # Returns scripts expected to render successfully.
40
- # @return [Array<Sevgi::Test::Script>]
41
- def valids
42
- suites.reject { NON_VALIDS.include?(it) }.map { self[it] }.flatten
43
- end
44
-
45
- alias to_a valids
46
-
47
- private
48
-
49
- def load(rootdir)
50
- ::Dir["#{rootdir}/**/*.sevgi"].grep_v(%r{(/_|/lib/|/library/)}).map { Script.new(it) }
7
+ module Test
8
+ # Showcase script suite collection.
9
+ # @api private
10
+ class Suite
11
+ # Creates a suite from a showcase root directory.
12
+ # @param rootdir [String] showcase root directory
13
+ # @return [void]
14
+ def initialize(rootdir)
15
+ @scripts = load(rootdir)
16
+ end
17
+
18
+ # Returns scripts belonging to a suite.
19
+ # @param suite [String, Symbol] suite name
20
+ # @return [Array<Sevgi::Showcase::Test::Script>]
21
+ def [](suite)
22
+ (@cache ||= {})[suite] ||= @scripts.select { |script| script.suite == suite.to_s }
23
+ end
24
+
25
+ # Returns all suite names.
26
+ # @return [Array<String>]
27
+ def suites
28
+ @suites ||= @scripts.map(&:suite).uniq
29
+ end
30
+
31
+ # Suite names that are expected to be invalid examples.
32
+ NON_VALIDS = ["gotcha"].freeze
33
+
34
+ # Returns scripts expected to be invalid.
35
+ # @return [Array<Sevgi::Showcase::Test::Script>]
36
+ def non_valids
37
+ NON_VALIDS.map { self[it] }.flatten
38
+ end
39
+
40
+ # Returns scripts expected to render successfully.
41
+ # @return [Array<Sevgi::Showcase::Test::Script>]
42
+ def valids
43
+ suites.reject { NON_VALIDS.include?(it) }.map { self[it] }.flatten
44
+ end
45
+
46
+ alias to_a valids
47
+
48
+ private
49
+
50
+ def load(rootdir)
51
+ ::Dir["#{rootdir}/**/*.sevgi"].grep_v(%r{(/_|/lib/|/library/)}).map { Script.new(it) }
52
+ end
51
53
  end
52
54
  end
53
55
  end
@@ -3,3 +3,9 @@
3
3
  require_relative "minitest/script"
4
4
  require_relative "minitest/shell"
5
5
  require_relative "minitest/suite"
6
+
7
+ module Sevgi
8
+ module Showcase
9
+ private_constant :Test
10
+ end
11
+ end
@@ -4,6 +4,6 @@ module Sevgi
4
4
  # Showcase component namespace.
5
5
  module Showcase
6
6
  # Current showcase component version.
7
- VERSION = "0.94.0"
7
+ VERSION = "0.98.2"
8
8
  end
9
9
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "showcase/dark"
4
- require_relative "showcase/minitest"
3
+ require "sevgi"
5
4
 
6
5
  require_relative "showcase/version"