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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +275 -2
- data/LICENSE +672 -3
- data/README.md +9 -17
- data/lib/sevgi/showcase/dark.rb +4 -0
- data/lib/sevgi/showcase/minitest/script.rb +59 -57
- data/lib/sevgi/showcase/minitest/shell.rb +136 -113
- data/lib/sevgi/showcase/minitest/suite.rb +48 -46
- data/lib/sevgi/showcase/minitest.rb +6 -0
- data/lib/sevgi/showcase/version.rb +1 -1
- data/lib/sevgi/showcase.rb +1 -2
- data/srv/checkers.sevgi +79 -0
- data/srv/checkers.svg +117 -0
- data/srv/clover.sevgi +19 -0
- data/srv/clover.svg +11 -0
- data/srv/clover.yml +3 -0
- data/srv/copperplate.sevgi +50 -0
- data/srv/copperplate.svg +99 -0
- data/srv/copperplate.yml +5 -0
- data/srv/{gear-wheel.sevgi → gear.sevgi} +7 -9
- data/srv/gear.svg +11 -0
- data/srv/gear.yml +2 -0
- data/srv/{grid-cells.sevgi → grid.sevgi} +9 -9
- data/srv/{grid-cells.svg → grid.svg} +13 -1
- data/srv/heart.sevgi +17 -0
- data/srv/{heart-mask.svg → heart.svg} +3 -3
- data/srv/heart.yml +3 -0
- data/srv/logo.sevgi +39 -0
- data/srv/logo.svg +29 -0
- data/srv/logo.yml +4 -0
- data/srv/logos.sevgi +34 -0
- data/srv/logos.svg +38 -0
- data/srv/logos.yml +4 -0
- data/srv/{meter-face.sevgi → meter.sevgi} +6 -6
- data/srv/meter.svg +18 -0
- data/srv/pacman.sevgi +18 -0
- data/srv/pacman.svg +15 -0
- data/srv/pokey.sevgi +14 -0
- data/srv/pokey.svg +6 -0
- data/srv/{ruler-line.sevgi → ruler-.sevgi} +7 -7
- data/srv/ruler.sevgi +61 -0
- data/srv/ruler.svg +445 -0
- data/srv/{snow-flake.sevgi → snowflake.sevgi} +2 -2
- data/srv/{snow-flake.svg → snowflake.svg} +1 -1
- data/srv/snowflake.yml +2 -0
- data/srv/squared.sevgi +42 -0
- data/srv/squared.svg +71 -0
- data/srv/squared.yml +4 -0
- data/srv/stars.sevgi +19 -0
- data/srv/stars.svg +30 -0
- data/srv/stars.yml +3 -0
- data/srv/tulips.sevgi +27 -0
- data/srv/tulips.svg +19 -0
- data/srv/tulips.yml +3 -0
- metadata +57 -36
- data/srv/checker-board.sevgi +0 -96
- data/srv/checker-board.svg +0 -117
- data/srv/gear-wheel.svg +0 -12
- data/srv/gear-wheel.yml +0 -3
- data/srv/heart-mask.sevgi +0 -17
- data/srv/heart-mask.yml +0 -3
- data/srv/meter-face.svg +0 -13
- data/srv/pacman-pokey.sevgi +0 -17
- data/srv/pacman-pokey.svg +0 -9
- data/srv/pacman-single.sevgi +0 -17
- data/srv/pacman-single.svg +0 -14
- data/srv/ruler-hline.sevgi +0 -55
- data/srv/ruler-hline.svg +0 -443
- data/srv/snow-flake.yml +0 -1
- /data/srv/{checker-board.yml → checkers.yml} +0 -0
- /data/srv/{grid-cells.yml → grid.yml} +0 -0
- /data/srv/{meter-face.yml → meter.yml} +0 -0
- /data/srv/{pacman-pokey.yml → pacman.yml} +0 -0
- /data/srv/{pacman-single.yml → pokey.yml} +0 -0
- /data/srv/{ruler-line.svg → ruler-.svg} +0 -0
- /data/srv/{ruler-hline.yml → ruler-.yml} +0 -0
- /data/srv/{ruler-line.yml → ruler.yml} +0 -0
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Sevgi Showcase
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
##
|
|
11
|
+
## Contents
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
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>
|
data/lib/sevgi/showcase/dark.rb
CHANGED
|
@@ -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
|
-
|
|
5
|
-
|
|
6
|
-
module Test
|
|
7
|
-
# Executable showcase script descriptor.
|
|
4
|
+
module Showcase
|
|
5
|
+
# Minitest helpers used by the showcase component.
|
|
8
6
|
# @api private
|
|
9
|
-
|
|
10
|
-
#
|
|
11
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
# Returns the script directory.
|
|
21
|
+
# @return [String]
|
|
22
|
+
def dir = @dir ||= ::File.dirname(file)
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
# Returns the script basename.
|
|
25
|
+
# @return [String]
|
|
26
|
+
def file! = ::File.basename(file)
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
# Returns the script name without extension.
|
|
29
|
+
# @return [String]
|
|
30
|
+
def name = @name ||= ::File.basename(file, ".*")
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
# Returns the script suite name.
|
|
39
|
+
# @return [String]
|
|
40
|
+
def suite = @suite ||= ::File.basename(dir)
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
# Reports whether the expected SVG output exists.
|
|
43
|
+
# @return [Boolean]
|
|
44
|
+
def svg? = ::File.exist?(svg)
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
# Returns the expected SVG output path.
|
|
47
|
+
# @return [String]
|
|
48
|
+
def svg = @svg ||= ::File.expand_path("#{dir}/#{name}.svg")
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
# Returns the expected SVG output basename.
|
|
51
|
+
# @return [String]
|
|
52
|
+
def svg! = ::File.basename(svg)
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
# Returns the optional YAML metadata path.
|
|
55
|
+
# @return [String]
|
|
56
|
+
def yml = @yml ||= ::File.expand_path("#{dir}/#{name}.yml")
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
# Returns the optional YAML metadata basename.
|
|
59
|
+
# @return [String]
|
|
60
|
+
def yml! = ::File.basename(yml)
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
71
|
+
private
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
7
|
-
|
|
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
|
|
12
|
-
# Shell
|
|
10
|
+
module Test
|
|
11
|
+
# Shell runner helpers for showcase tests.
|
|
13
12
|
# @api private
|
|
14
|
-
|
|
15
|
-
#
|
|
16
|
-
# @
|
|
17
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
#
|
|
47
|
-
#
|
|
48
|
-
# @
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
58
|
-
|
|
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
|
-
|
|
73
|
+
private
|
|
61
74
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
end
|
|
90
|
+
def finish_capture(stdin, registered)
|
|
91
|
+
close_input(stdin)
|
|
92
|
+
Signals.unregister(self) if registered
|
|
93
|
+
end
|
|
82
94
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
nil
|
|
87
|
-
end
|
|
95
|
+
def register_capture(thread)
|
|
96
|
+
Signals.register(self, thread.pid)
|
|
97
|
+
end
|
|
88
98
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
94
|
-
|
|
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
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
132
|
+
def inputs(stdin, thread, &block)
|
|
133
|
+
stdin.instance_exec(thread, &block) if block
|
|
134
|
+
end
|
|
113
135
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
136
|
+
def kill_process(signal, pid)
|
|
137
|
+
::Process.kill(signal, pid)
|
|
138
|
+
rescue Errno::ESRCH
|
|
139
|
+
nil
|
|
140
|
+
end
|
|
119
141
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
128
|
-
|
|
149
|
+
def stop_process(thread)
|
|
150
|
+
return unless thread&.alive?
|
|
129
151
|
|
|
130
|
-
|
|
131
|
-
|
|
152
|
+
kill_process("TERM", thread.pid)
|
|
153
|
+
return if thread.join(1)
|
|
132
154
|
|
|
133
|
-
|
|
134
|
-
|
|
155
|
+
kill_process("KILL", thread.pid)
|
|
156
|
+
thread.join
|
|
157
|
+
end
|
|
135
158
|
end
|
|
136
|
-
end
|
|
137
159
|
|
|
138
|
-
|
|
160
|
+
extend self
|
|
139
161
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
module Test
|
|
7
|
-
# Showcase script suite collection.
|
|
4
|
+
module Showcase
|
|
5
|
+
# Minitest helpers used by the showcase component.
|
|
8
6
|
# @api private
|
|
9
|
-
|
|
10
|
-
#
|
|
11
|
-
# @
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|