sevgi 0.73.2 → 0.93.1
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/README.md +5 -0
- data/lib/sevgi/binaries/rake.rb +6 -0
- data/lib/sevgi/binaries/sevgi.rb +17 -1
- data/lib/sevgi/executor/error.rb +16 -1
- data/lib/sevgi/executor/scope.rb +39 -4
- data/lib/sevgi/executor/source.rb +19 -0
- data/lib/sevgi/executor.rb +47 -5
- data/lib/sevgi/toplevel/derender.rb +14 -6
- data/lib/sevgi/toplevel/executor.rb +18 -0
- data/lib/sevgi/toplevel/function.rb +6 -0
- data/lib/sevgi/toplevel/graphics.rb +37 -6
- data/lib/sevgi/toplevel/sundries.rb +6 -0
- data/lib/sevgi/toplevel.rb +19 -1
- data/lib/sevgi/version.rb +2 -1
- data/lib/sevgi.rb +30 -2
- metadata +13 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f8e80618af5d1d88338b10db45b911cbca256c113ec3b7458b5b4e6e3a9ba7e
|
|
4
|
+
data.tar.gz: f879baa77b005b91cb674d350d8ed79551f2be36132c7039f188ddb64ba58779
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fbce000770d881a72ac107ba52b39aa6bdc9ffcbfaacd2d2609cb7c6fef7251a0976fee6989c3a97b87973507dbaecfedcce78dfe72a59e80eea124c8e53a641
|
|
7
|
+
data.tar.gz: 7a4dc86d8c75f75092a9e159fa4e023aeefa14cc1feb338f4e561095d331e535d45dc2da4e39e5b289d78444cf7c9d731393914290f917dd98366a4f30ab2d1b
|
data/README.md
CHANGED
data/lib/sevgi/binaries/rake.rb
CHANGED
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
require "sevgi"
|
|
4
4
|
|
|
5
|
+
# Extends FileUtils with Sevgi script helpers for Rake tasks.
|
|
5
6
|
module FileUtils
|
|
6
7
|
# Thin DSL wrapper to call a script without spawning a shell.
|
|
8
|
+
# @param file [String] Sevgi script file, with or without `.sevgi` extension
|
|
9
|
+
# @param args [Array] positional arguments exposed to the script as `ARGA`
|
|
10
|
+
# @param kwargs [Hash] keyword arguments exposed to the script as `ARGH`
|
|
11
|
+
# @return [Sevgi::Executor::Scope, nil] execution scope, or nil for an empty file
|
|
12
|
+
# @raise [Sevgi::ArgumentError] when the script file cannot be found
|
|
7
13
|
def sevgi(file, *args, **kwargs)
|
|
8
14
|
Sevgi.execute_file(F.existing!(file, [EXTENSION])) do |mod|
|
|
9
15
|
include(Sevgi)
|
data/lib/sevgi/binaries/sevgi.rb
CHANGED
|
@@ -3,15 +3,25 @@
|
|
|
3
3
|
require "sevgi"
|
|
4
4
|
|
|
5
5
|
module Sevgi
|
|
6
|
+
# Command-line entrypoint implementations shipped with Sevgi.
|
|
6
7
|
module Binaries
|
|
8
|
+
# Implements the `sevgi` executable.
|
|
7
9
|
module Sevgi
|
|
8
10
|
extend self
|
|
9
11
|
|
|
12
|
+
# Executable name used in help output.
|
|
10
13
|
PROGNAME = "sevgi"
|
|
11
14
|
|
|
15
|
+
# Error raised for invalid command-line usage.
|
|
12
16
|
Error = Class.new(::Sevgi::Error)
|
|
13
17
|
|
|
18
|
+
# Parsed command-line options for the `sevgi` executable.
|
|
19
|
+
# @api private
|
|
14
20
|
Options = Struct.new(:require, :nomain, :vomit, :help, :version) do
|
|
21
|
+
# Parses command-line options and removes them from the argv array.
|
|
22
|
+
# @param argv [Array<String>] mutable command-line argument array
|
|
23
|
+
# @return [Sevgi::Binaries::Sevgi::Options] parsed options
|
|
24
|
+
# @raise [Sevgi::Binaries::Sevgi::Error] when an option is not recognized
|
|
15
25
|
def self.parse(argv)
|
|
16
26
|
new.tap do |options|
|
|
17
27
|
argv.first.start_with?("-") ? option(argv, options) : break until argv.empty?
|
|
@@ -25,7 +35,7 @@ module Sevgi
|
|
|
25
35
|
case (arg = argv.shift)
|
|
26
36
|
when "-r", "--require"
|
|
27
37
|
options.require = argv.shift
|
|
28
|
-
when "-
|
|
38
|
+
when "-n", "--nomain"
|
|
29
39
|
options.nomain = true
|
|
30
40
|
when "-x", "--exception"
|
|
31
41
|
options.vomit = true
|
|
@@ -43,6 +53,12 @@ module Sevgi
|
|
|
43
53
|
|
|
44
54
|
private_constant :Options
|
|
45
55
|
|
|
56
|
+
# Runs the `sevgi` command-line interface.
|
|
57
|
+
# @param argv [Array<String>, String, nil] command-line arguments
|
|
58
|
+
# @return [nil]
|
|
59
|
+
# @raise [LoadError] when a required Ruby library cannot be loaded
|
|
60
|
+
# @raise [Sevgi::Executor::Error] when `--exception` or `SEVGI_VOMIT` requests raw errors
|
|
61
|
+
# @raise [SystemExit] when command-line usage or script execution aborts
|
|
46
62
|
def call(argv)
|
|
47
63
|
return puts(help) if (options = Options.parse(argv = Array(argv))).help
|
|
48
64
|
return puts(::Sevgi::VERSION) if options.version
|
data/lib/sevgi/executor/error.rb
CHANGED
|
@@ -2,9 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
module Sevgi
|
|
4
4
|
class Executor
|
|
5
|
+
# Wraps an exception raised while executing Sevgi script source.
|
|
5
6
|
class Error < ::Sevgi::Error
|
|
7
|
+
# @!attribute [r] error
|
|
8
|
+
# @return [Exception] original exception raised by script execution
|
|
9
|
+
# @!attribute [r] scope
|
|
10
|
+
# @return [Sevgi::Executor::Scope] executor scope active when the error was captured
|
|
6
11
|
attr_reader :error, :scope
|
|
7
12
|
|
|
13
|
+
# Builds an executor error wrapper.
|
|
14
|
+
# @param error [Exception] original exception
|
|
15
|
+
# @param scope [Sevgi::Executor::Scope] executor scope active at failure time
|
|
16
|
+
# @return [void]
|
|
8
17
|
def initialize(error, scope)
|
|
9
18
|
@error = error
|
|
10
19
|
@scope = scope
|
|
@@ -12,13 +21,19 @@ module Sevgi
|
|
|
12
21
|
super(error.message)
|
|
13
22
|
end
|
|
14
23
|
|
|
24
|
+
# Returns backtrace entries that belong to the Sevgi load stack.
|
|
25
|
+
# @return [Array<String>] filtered backtrace lines relative to the current directory
|
|
15
26
|
def backtrace!
|
|
27
|
+
sources = stack.map { ::File.expand_path(it) }
|
|
28
|
+
|
|
16
29
|
error
|
|
17
30
|
.backtrace
|
|
18
|
-
.select {
|
|
31
|
+
.select { sources.include?(::File.expand_path(it.split(":", 2).first)) }
|
|
19
32
|
.map { |line| line.delete_prefix("#{::Dir.pwd}/") }
|
|
20
33
|
end
|
|
21
34
|
|
|
35
|
+
# Returns the script load stack active at failure time.
|
|
36
|
+
# @return [Array<String>] script file names in load order
|
|
22
37
|
def stack = scope.stack
|
|
23
38
|
end
|
|
24
39
|
end
|
data/lib/sevgi/executor/scope.rb
CHANGED
|
@@ -2,11 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
module Sevgi
|
|
4
4
|
class Executor
|
|
5
|
+
# Holds one isolated Sevgi script execution scope and its result.
|
|
5
6
|
class Scope
|
|
7
|
+
# Error raised by low-level executor scope operations.
|
|
6
8
|
Error = Class.new(::Sevgi::Error)
|
|
7
9
|
|
|
10
|
+
# @!attribute [r] scope
|
|
11
|
+
# @return [Module] isolated module where script source is evaluated
|
|
12
|
+
# @!attribute [r] recent
|
|
13
|
+
# @return [Object, nil] last expression result from the executed source
|
|
14
|
+
# @!attribute [r] error
|
|
15
|
+
# @return [Sevgi::Executor::Error, nil] captured execution error
|
|
8
16
|
attr_reader :scope, :recent, :error
|
|
9
17
|
|
|
18
|
+
# Creates a script execution scope.
|
|
19
|
+
# @param scope [Module, nil] existing module to evaluate source in
|
|
20
|
+
# @return [void]
|
|
10
21
|
def initialize(scope = nil)
|
|
11
22
|
@scope = scope || main
|
|
12
23
|
@recent = nil
|
|
@@ -14,16 +25,22 @@ module Sevgi
|
|
|
14
25
|
@stack = {}
|
|
15
26
|
end
|
|
16
27
|
|
|
28
|
+
# Reports whether execution captured an error.
|
|
29
|
+
# @return [Boolean] true when execution failed
|
|
17
30
|
def error? = !error.nil?
|
|
18
31
|
|
|
32
|
+
# Executes one source object in this scope.
|
|
33
|
+
# @param source [Sevgi::Executor::Source] source to evaluate
|
|
34
|
+
# @param receiver [Object, nil] receiver used while booting the DSL
|
|
35
|
+
# @yield optional boot block that installs DSL methods before evaluation
|
|
36
|
+
# @yieldreturn [void]
|
|
37
|
+
# @return [Sevgi::Executor::Scope] self, with recent or error populated
|
|
38
|
+
# @api private
|
|
19
39
|
def call(source, receiver = nil, &boot)
|
|
20
40
|
push(source)
|
|
21
41
|
|
|
22
42
|
tap do
|
|
23
|
-
@recent =
|
|
24
|
-
boot(receiver, &boot)
|
|
25
|
-
evaluate(source)
|
|
26
|
-
end
|
|
43
|
+
@recent = run(source, receiver, &boot)
|
|
27
44
|
|
|
28
45
|
# rubocop:disable Lint/RescueException
|
|
29
46
|
rescue Exception => e
|
|
@@ -34,12 +51,25 @@ module Sevgi
|
|
|
34
51
|
end
|
|
35
52
|
end
|
|
36
53
|
|
|
54
|
+
# Loads a file into this existing execution scope.
|
|
55
|
+
# @param file [String] source file to read and evaluate
|
|
56
|
+
# @yield optional boot block that installs DSL methods before evaluation
|
|
57
|
+
# @yieldreturn [void]
|
|
58
|
+
# @return [Sevgi::Executor::Scope] self, with recent or error populated
|
|
59
|
+
# @raise [Errno::ENOENT] when the file cannot be read
|
|
60
|
+
# @api private
|
|
37
61
|
def load(file, &block) = call(Source.load(file), &block)
|
|
38
62
|
|
|
63
|
+
# Returns the unique source stack for this execution.
|
|
64
|
+
# @return [Array<String>] source file keys in load order
|
|
39
65
|
def stack = @stack.keys
|
|
40
66
|
|
|
67
|
+
# Returns the most recently pushed source.
|
|
68
|
+
# @return [Sevgi::Executor::Source, nil] most recent source object
|
|
69
|
+
# @api private
|
|
41
70
|
def peek = @stack[@stack.keys.last]
|
|
42
71
|
|
|
72
|
+
# Constant name used for the temporary script module under {Sevgi}.
|
|
43
73
|
MAIN_MODULE = :Main
|
|
44
74
|
|
|
45
75
|
private
|
|
@@ -65,6 +95,11 @@ module Sevgi
|
|
|
65
95
|
def push(source)
|
|
66
96
|
tap { @stack[source.key] = source }
|
|
67
97
|
end
|
|
98
|
+
|
|
99
|
+
def run(source, receiver, &boot)
|
|
100
|
+
boot(receiver, &boot)
|
|
101
|
+
evaluate(source)
|
|
102
|
+
end
|
|
68
103
|
end
|
|
69
104
|
end
|
|
70
105
|
end
|
|
@@ -2,13 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
module Sevgi
|
|
4
4
|
class Executor
|
|
5
|
+
# Describes Ruby source evaluated by the Sevgi executor.
|
|
6
|
+
# @api private
|
|
5
7
|
Source = Data.define(:string, :file, :line) do
|
|
8
|
+
# @overload call(string:, file: nil, line: nil)
|
|
9
|
+
# Builds a source object.
|
|
10
|
+
# @param string [String] Ruby source string
|
|
11
|
+
# @param file [String, nil] source file name for diagnostics
|
|
12
|
+
# @param line [Integer, nil] starting source line for diagnostics
|
|
13
|
+
# @return [Sevgi::Executor::Source] source object
|
|
6
14
|
def self.call(...) = new(...)
|
|
7
15
|
|
|
16
|
+
# Builds a source object from a file.
|
|
17
|
+
# @param file [String] source file to read
|
|
18
|
+
# @return [Sevgi::Executor::Source] source object with file contents
|
|
19
|
+
# @raise [Errno::ENOENT] when the file cannot be read
|
|
8
20
|
def self.load(file) = new(string: ::File.read(file), file: file, line: 1)
|
|
9
21
|
|
|
22
|
+
# Creates a source object.
|
|
23
|
+
# @param string [String] Ruby source string
|
|
24
|
+
# @param file [String, nil] source file name for diagnostics
|
|
25
|
+
# @param line [Integer, nil] starting source line for diagnostics
|
|
26
|
+
# @return [void]
|
|
10
27
|
def initialize(string:, file: nil, line: nil) = super(string:, file: file || "sevgi", line: line || 1)
|
|
11
28
|
|
|
29
|
+
# Returns the stack key used for this source.
|
|
30
|
+
# @return [String] source file name
|
|
12
31
|
def key = file
|
|
13
32
|
end
|
|
14
33
|
end
|
data/lib/sevgi/executor.rb
CHANGED
|
@@ -7,44 +7,86 @@ require_relative "executor/scope"
|
|
|
7
7
|
require_relative "executor/source"
|
|
8
8
|
|
|
9
9
|
module Sevgi
|
|
10
|
+
# Executes Sevgi script source inside an isolated module scope.
|
|
11
|
+
#
|
|
12
|
+
# The executor is used by script mode and by the `Load` DSL word to preserve a
|
|
13
|
+
# useful load stack while keeping DSL methods out of the caller's global object
|
|
14
|
+
# whenever possible.
|
|
10
15
|
class Executor
|
|
11
16
|
include Singleton
|
|
12
17
|
|
|
18
|
+
# Loads a script file inside the current executor scope.
|
|
19
|
+
# @param file [String] path to a Sevgi script file
|
|
20
|
+
# @return [Sevgi::Executor::Scope] current execution scope
|
|
21
|
+
# @raise [Sevgi::PanicError] when there is no active executor scope
|
|
22
|
+
# @api private
|
|
13
23
|
def self.load(file, ...)
|
|
14
24
|
PanicError.("box stack empty; create a box first") unless instance.current
|
|
15
25
|
|
|
16
26
|
instance.current.load(file, ...)
|
|
17
27
|
end
|
|
18
28
|
|
|
29
|
+
# Executes Ruby source inside a managed Sevgi script scope.
|
|
30
|
+
# @param string [String] source to evaluate
|
|
31
|
+
# @param file [String, nil] source file name used for errors and backtraces
|
|
32
|
+
# @param line [Integer, nil] starting source line used for errors and backtraces
|
|
33
|
+
# @param require [String, nil] optional Ruby library to require before execution
|
|
34
|
+
# @param receiver [Object, nil] receiver used while booting the DSL
|
|
35
|
+
# @yield optional boot block that installs DSL methods before evaluation
|
|
36
|
+
# @yieldreturn [void]
|
|
37
|
+
# @return [Sevgi::Executor::Scope, nil] execution scope, or nil for empty source
|
|
38
|
+
# @raise [LoadError] when the optional required library cannot be loaded
|
|
19
39
|
def self.execute(string, file: nil, line: nil, require: nil, receiver: nil, &block)
|
|
20
40
|
return if string.empty?
|
|
21
41
|
|
|
22
|
-
Signal.trap("INT") { Kernel.abort("") }
|
|
42
|
+
interrupt = Signal.trap("INT") { Kernel.abort("") }
|
|
23
43
|
|
|
24
44
|
::Kernel.require(require) if require
|
|
25
45
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
end
|
|
46
|
+
scope = instance.create
|
|
47
|
+
catch(:result) { scope.call(Source.new(string:, file:, line:), receiver, &block) }
|
|
29
48
|
|
|
30
49
|
ensure
|
|
31
|
-
|
|
50
|
+
Signal.trap("INT", interrupt) if interrupt
|
|
51
|
+
instance.shutdown if scope
|
|
32
52
|
end
|
|
33
53
|
|
|
54
|
+
# Executes a file inside a managed Sevgi script scope.
|
|
55
|
+
# @param file [String] source file to read and execute
|
|
56
|
+
# @param require [String, nil] optional Ruby library to require before execution
|
|
57
|
+
# @param receiver [Object, nil] receiver used while booting the DSL
|
|
58
|
+
# @yield optional boot block that installs DSL methods before evaluation
|
|
59
|
+
# @yieldreturn [void]
|
|
60
|
+
# @return [Sevgi::Executor::Scope, nil] execution scope, or nil for an empty file
|
|
61
|
+
# @raise [Errno::ENOENT] when the file cannot be read
|
|
62
|
+
# @raise [LoadError] when the optional required library cannot be loaded
|
|
34
63
|
def self.execute_file(file, require: nil, receiver: nil, &block)
|
|
35
64
|
execute(::File.read(file), file: file, line: 1, require:, receiver:, &block)
|
|
36
65
|
end
|
|
37
66
|
|
|
67
|
+
# Removes the current executor scope.
|
|
68
|
+
# @return [Sevgi::Executor::Scope, nil] removed scope
|
|
69
|
+
# @api private
|
|
38
70
|
def self.shutdown
|
|
39
71
|
instance.shutdown
|
|
40
72
|
end
|
|
41
73
|
|
|
42
74
|
def initialize = @scopes = []
|
|
43
75
|
|
|
76
|
+
# Creates and pushes a new executor scope.
|
|
77
|
+
# @param scope [Module, nil] existing module scope to reuse
|
|
78
|
+
# @return [Sevgi::Executor::Scope] created scope
|
|
79
|
+
# @api private
|
|
44
80
|
def create(scope = nil) = Scope.new(scope).tap { @scopes << it }
|
|
45
81
|
|
|
82
|
+
# Returns the active executor scope.
|
|
83
|
+
# @return [Sevgi::Executor::Scope, nil] active scope, if any
|
|
84
|
+
# @api private
|
|
46
85
|
def current = @scopes.last
|
|
47
86
|
|
|
87
|
+
# Removes the active executor scope.
|
|
88
|
+
# @return [Sevgi::Executor::Scope, nil] removed scope
|
|
89
|
+
# @api private
|
|
48
90
|
def shutdown = @scopes.pop
|
|
49
91
|
end
|
|
50
92
|
end
|
|
@@ -4,12 +4,20 @@ require "sevgi/derender"
|
|
|
4
4
|
|
|
5
5
|
module Sevgi
|
|
6
6
|
module Toplevel
|
|
7
|
-
|
|
7
|
+
# Converts an SVG/XML file into a derender node.
|
|
8
|
+
# @param file [String] path to the source SVG/XML file
|
|
9
|
+
# @param id [String, nil] optional SVG id selecting a node inside the source
|
|
10
|
+
# @return [Sevgi::Derender::Node] selected node in the derender tree
|
|
11
|
+
# @raise [Sevgi::ArgumentError] when the file cannot be found or the id is absent
|
|
12
|
+
# @see Sevgi::Derender.decompile_file
|
|
13
|
+
def Decompile(file, id = nil) = Derender.decompile_file(file, id:)
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
# Converts an SVG/XML file into Sevgi DSL Ruby source.
|
|
16
|
+
# @param file [String] path to the source SVG/XML file
|
|
17
|
+
# @param id [String, nil] optional SVG id selecting a node inside the source
|
|
18
|
+
# @return [String] formatted Sevgi DSL source
|
|
19
|
+
# @raise [Sevgi::ArgumentError] when the file cannot be found or the id is absent
|
|
20
|
+
# @see Sevgi::Derender.derender_file
|
|
21
|
+
def Derender(file, id = nil) = Derender.derender_file(file, id:)
|
|
14
22
|
end
|
|
15
23
|
end
|
|
@@ -7,11 +7,29 @@ module Sevgi
|
|
|
7
7
|
|
|
8
8
|
private_constant :BootBlock
|
|
9
9
|
|
|
10
|
+
# Executes Sevgi script source with the full top-level DSL installed.
|
|
11
|
+
# @param args [Array] arguments forwarded to {Sevgi::Executor.execute}
|
|
12
|
+
# @param kwargs [Hash] keyword arguments forwarded to {Sevgi::Executor.execute}
|
|
13
|
+
# @return [Sevgi::Executor::Scope, nil] execution result, or nil for empty source
|
|
14
|
+
# @raise [LoadError] when the optional `require:` library cannot be loaded
|
|
15
|
+
# @see Sevgi::Executor.execute
|
|
10
16
|
def self.execute(*args, **kwargs) = Executor.execute(*args, **kwargs, &BootBlock)
|
|
11
17
|
|
|
18
|
+
# Executes a Sevgi script file with the full top-level DSL installed.
|
|
19
|
+
# @param args [Array] arguments forwarded to {Sevgi::Executor.execute_file}
|
|
20
|
+
# @param kwargs [Hash] keyword arguments forwarded to {Sevgi::Executor.execute_file}
|
|
21
|
+
# @return [Sevgi::Executor::Scope, nil] execution result, or nil for an empty file
|
|
22
|
+
# @raise [Errno::ENOENT] when the file cannot be read
|
|
23
|
+
# @raise [LoadError] when the optional `require:` library cannot be loaded
|
|
24
|
+
# @see Sevgi::Executor.execute_file
|
|
12
25
|
def self.execute_file(*args, **kwargs) = Executor.execute_file(*args, **kwargs, &BootBlock)
|
|
13
26
|
|
|
14
27
|
module Toplevel
|
|
28
|
+
# Loads one or more Sevgi files relative to the caller's source file.
|
|
29
|
+
# @param files [Array<String>] Sevgi script files to locate and execute
|
|
30
|
+
# @return [Array<String>] the input file list
|
|
31
|
+
# @raise [Sevgi::PanicError] when called without an active executor scope
|
|
32
|
+
# @raise [Sevgi::Error] when a file cannot be located
|
|
15
33
|
def Load(*files)
|
|
16
34
|
start = ::File.dirname(caller_locations(1..1).first.path)
|
|
17
35
|
|
|
@@ -4,6 +4,12 @@ require "sevgi/function"
|
|
|
4
4
|
|
|
5
5
|
module Sevgi
|
|
6
6
|
module Toplevel
|
|
7
|
+
# Function helper namespace promoted as `F` by the full top-level DSL.
|
|
8
|
+
#
|
|
9
|
+
# @example Use helper functions inside script mode
|
|
10
|
+
# SVG do
|
|
11
|
+
# text F.pluralize("axis")
|
|
12
|
+
# end
|
|
7
13
|
module Function
|
|
8
14
|
extend Sevgi::Function::Color
|
|
9
15
|
extend Sevgi::Function::Math
|
|
@@ -4,14 +4,45 @@ require "sevgi/graphics"
|
|
|
4
4
|
|
|
5
5
|
module Sevgi
|
|
6
6
|
module Toplevel
|
|
7
|
+
# @overload Mixin(mod, document = Sevgi::Graphics::Document::Base, &block)
|
|
8
|
+
# Adds graphics mixture methods to a document class.
|
|
9
|
+
# @param mod [Symbol, String] named mixture to mix into the document
|
|
10
|
+
# @param document [Class] document class receiving the mixture
|
|
11
|
+
# @yield optional anonymous mixture module body
|
|
12
|
+
# @yieldreturn [void]
|
|
13
|
+
# @return [void]
|
|
14
|
+
# @raise [NameError] when a named mixture cannot be resolved
|
|
15
|
+
# @see Sevgi::Graphics::Mixtures.mixin
|
|
16
|
+
# @overload Mixin(document = Sevgi::Graphics::Document::Base, &block)
|
|
17
|
+
# Adds an anonymous graphics mixture to a document class.
|
|
18
|
+
# @param document [Class] document class receiving the mixture
|
|
19
|
+
# @yield anonymous mixture module body
|
|
20
|
+
# @yieldreturn [void]
|
|
21
|
+
# @return [Module] anonymous mixture
|
|
22
|
+
# @raise [Sevgi::ArgumentError] when no named mixture or block is given
|
|
23
|
+
# @see Sevgi::Graphics::Mixtures.mixin
|
|
7
24
|
def Mixin(...) = Graphics::Mixtures.mixin(...)
|
|
8
25
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
26
|
+
# @overload Paper(width, height, name = :custom, unit: "mm")
|
|
27
|
+
# Defines or validates a named paper profile for DSL use.
|
|
28
|
+
# @param width [Numeric] paper width
|
|
29
|
+
# @param height [Numeric] paper height
|
|
30
|
+
# @param name [Symbol] paper profile name
|
|
31
|
+
# @param unit [String, Symbol] size unit
|
|
32
|
+
# @return [Symbol] the paper profile name
|
|
33
|
+
# @raise [Sevgi::ArgumentError] when the profile is invalid or an existing profile differs
|
|
34
|
+
# @see Sevgi::Graphics#paper
|
|
35
|
+
def Paper(...) = Graphics.paper(...)
|
|
12
36
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
37
|
+
# @overload Paper!(width, height, name = :custom, unit: "mm")
|
|
38
|
+
# Defines or overwrites a named paper profile for DSL use.
|
|
39
|
+
# @param width [Numeric] paper width
|
|
40
|
+
# @param height [Numeric] paper height
|
|
41
|
+
# @param name [Symbol] paper profile name
|
|
42
|
+
# @param unit [String, Symbol] size unit
|
|
43
|
+
# @return [Symbol] the paper profile name
|
|
44
|
+
# @raise [Sevgi::ArgumentError] when the profile is invalid or the paper name is reserved
|
|
45
|
+
# @see Sevgi::Graphics#paper!
|
|
46
|
+
def Paper!(...) = Graphics.paper!(...)
|
|
16
47
|
end
|
|
17
48
|
end
|
|
@@ -4,6 +4,12 @@ require "sevgi/sundries"
|
|
|
4
4
|
|
|
5
5
|
module Sevgi
|
|
6
6
|
module Toplevel
|
|
7
|
+
# Builds a drawable grid from a graphics canvas.
|
|
8
|
+
# @param canvas [Sevgi::Graphics::Canvas] canvas defining page size and margins
|
|
9
|
+
# @param unit [Numeric] minor grid unit
|
|
10
|
+
# @param multiple [Numeric] number of minor units in each major interval
|
|
11
|
+
# @return [Sevgi::Sundries::Grid] grid fitted to the canvas
|
|
12
|
+
# @raise [Sevgi::ArgumentError] when canvas, unit, multiple, or fitting span is invalid
|
|
7
13
|
def Grid(canvas, unit:, multiple:)
|
|
8
14
|
ArgumentError.("Must be a Canvas: #{canvas}") unless canvas.is_a?(Graphics::Canvas)
|
|
9
15
|
|
data/lib/sevgi/toplevel.rb
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Sevgi
|
|
4
|
+
# Shared implementation for the full Sevgi top-level DSL.
|
|
5
|
+
#
|
|
6
|
+
# This module is installed by `include Sevgi` or `extend Sevgi`; it should not
|
|
7
|
+
# normally be included directly.
|
|
8
|
+
#
|
|
9
|
+
# @see Sevgi
|
|
4
10
|
module Toplevel
|
|
5
11
|
@constants = {}
|
|
6
12
|
|
|
@@ -12,7 +18,11 @@ module Sevgi
|
|
|
12
18
|
def inject(base)
|
|
13
19
|
return if base.instance_variable_defined?("@_toplevel_injected_")
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
target = base.is_a?(::Module) ? base : base.class
|
|
22
|
+
@constants.each do |name, constant|
|
|
23
|
+
target.const_set(name, constant) unless target.const_defined?(name, false)
|
|
24
|
+
end
|
|
25
|
+
|
|
16
26
|
base.instance_variable_set("@_toplevel_injected_", true)
|
|
17
27
|
end
|
|
18
28
|
|
|
@@ -21,11 +31,19 @@ module Sevgi
|
|
|
21
31
|
end
|
|
22
32
|
end
|
|
23
33
|
|
|
34
|
+
# Injects promoted constants when the DSL is included.
|
|
35
|
+
# @param base [Module] the class or module receiving promoted constants
|
|
36
|
+
# @return [void]
|
|
37
|
+
# @api private
|
|
24
38
|
def self.included(base)
|
|
25
39
|
super
|
|
26
40
|
inject(base)
|
|
27
41
|
end
|
|
28
42
|
|
|
43
|
+
# Injects promoted constants when the DSL is extended.
|
|
44
|
+
# @param base [Object] the object or module receiving promoted constants
|
|
45
|
+
# @return [void]
|
|
46
|
+
# @api private
|
|
29
47
|
def self.extended(base)
|
|
30
48
|
super
|
|
31
49
|
inject(base)
|
data/lib/sevgi/version.rb
CHANGED
data/lib/sevgi.rb
CHANGED
|
@@ -9,20 +9,48 @@ require "sevgi/sundries"
|
|
|
9
9
|
|
|
10
10
|
require "sevgi/version"
|
|
11
11
|
|
|
12
|
-
# Minimal
|
|
12
|
+
# Minimal top-level SVG interface installed by `require "sevgi"`.
|
|
13
|
+
# @return [Module] the graphics module used as the SVG DSL namespace
|
|
13
14
|
SVG = Sevgi::Graphics
|
|
15
|
+
|
|
16
|
+
# @overload SVG(document = :default, canvas = Undefined, **attributes, &block)
|
|
17
|
+
# Builds an SVG document through the default top-level DSL entrypoint.
|
|
18
|
+
# @param document [Symbol, Class] document profile name or document class
|
|
19
|
+
# @param canvas [Object] optional canvas or paper profile argument
|
|
20
|
+
# @param attributes [Hash] root SVG attributes
|
|
21
|
+
# @yield the document block evaluated in the SVG document context
|
|
22
|
+
# @yieldreturn [void]
|
|
23
|
+
# @return [Sevgi::Graphics::Document::Proto] a rendered SVG document object
|
|
24
|
+
# @raise [Sevgi::ArgumentError] when the document, paper, or canvas arguments are invalid
|
|
14
25
|
def SVG(...) = Sevgi::Graphics.SVG(...)
|
|
15
26
|
|
|
16
|
-
#
|
|
27
|
+
# Full top-level API for Sevgi library and script consumers.
|
|
28
|
+
#
|
|
29
|
+
# Including or extending this module installs DSL methods such as `Paper`, `Load`,
|
|
30
|
+
# and `Grid`, plus convenience constants such as `F`, `Geometry`, `Origin`, and
|
|
31
|
+
# `Export`.
|
|
32
|
+
#
|
|
33
|
+
# @example Include the DSL in an object
|
|
34
|
+
# class Drawing
|
|
35
|
+
# include Sevgi
|
|
36
|
+
# end
|
|
17
37
|
module Sevgi
|
|
18
38
|
# See sevgi/toplevel/*.rb files for details
|
|
19
39
|
require_relative "sevgi/toplevel"
|
|
20
40
|
|
|
41
|
+
# Installs the full toplevel DSL into an including class or module.
|
|
42
|
+
# @param base [Module] the class or module receiving the DSL methods
|
|
43
|
+
# @return [void]
|
|
44
|
+
# @api private
|
|
21
45
|
def self.included(base)
|
|
22
46
|
super
|
|
23
47
|
base.include(Toplevel)
|
|
24
48
|
end
|
|
25
49
|
|
|
50
|
+
# Installs the full toplevel DSL into an extending object or module.
|
|
51
|
+
# @param base [Object] the receiver extended with the DSL methods
|
|
52
|
+
# @return [void]
|
|
53
|
+
# @api private
|
|
26
54
|
def self.extended(base)
|
|
27
55
|
super
|
|
28
56
|
base.extend(Toplevel)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sevgi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.93.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Recai Oktaş
|
|
@@ -15,84 +15,84 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - '='
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.93.1
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - '='
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.
|
|
25
|
+
version: 0.93.1
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: sevgi-function
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - '='
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 0.
|
|
32
|
+
version: 0.93.1
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - '='
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 0.
|
|
39
|
+
version: 0.93.1
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: sevgi-geometry
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
44
|
- - '='
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: 0.
|
|
46
|
+
version: 0.93.1
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - '='
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: 0.
|
|
53
|
+
version: 0.93.1
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: sevgi-graphics
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
58
|
- - '='
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: 0.
|
|
60
|
+
version: 0.93.1
|
|
61
61
|
type: :runtime
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - '='
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: 0.
|
|
67
|
+
version: 0.93.1
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
69
|
name: sevgi-standard
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
71
71
|
requirements:
|
|
72
72
|
- - '='
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: 0.
|
|
74
|
+
version: 0.93.1
|
|
75
75
|
type: :runtime
|
|
76
76
|
prerelease: false
|
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
|
79
79
|
- - '='
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: 0.
|
|
81
|
+
version: 0.93.1
|
|
82
82
|
- !ruby/object:Gem::Dependency
|
|
83
83
|
name: sevgi-sundries
|
|
84
84
|
requirement: !ruby/object:Gem::Requirement
|
|
85
85
|
requirements:
|
|
86
86
|
- - '='
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
|
-
version: 0.
|
|
88
|
+
version: 0.93.1
|
|
89
89
|
type: :runtime
|
|
90
90
|
prerelease: false
|
|
91
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
92
92
|
requirements:
|
|
93
93
|
- - '='
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: 0.
|
|
95
|
+
version: 0.93.1
|
|
96
96
|
description: Provides a scriptable DSL with utilities for creating SVG content with
|
|
97
97
|
Ruby.
|
|
98
98
|
email: roktas@gmail.com
|