sevgi 0.73.2 → 0.94.0
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 +4 -0
- data/LICENSE +5 -0
- data/README.md +61 -0
- data/lib/sevgi/binaries/rake.rb +17 -4
- data/lib/sevgi/binaries/sevgi.rb +16 -1
- data/lib/sevgi/executor/error.rb +20 -1
- data/lib/sevgi/executor/scope.rb +61 -13
- data/lib/sevgi/executor/source.rb +19 -0
- data/lib/sevgi/executor.rb +136 -15
- data/lib/sevgi/toplevel/derender.rb +14 -6
- data/lib/sevgi/toplevel/executor.rb +20 -0
- data/lib/sevgi/toplevel/function.rb +8 -9
- data/lib/sevgi/toplevel/graphics.rb +37 -6
- data/lib/sevgi/toplevel/sundries.rb +9 -0
- data/lib/sevgi/toplevel.rb +22 -1
- data/lib/sevgi/version.rb +2 -1
- data/lib/sevgi.rb +33 -2
- metadata +16 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ceb2e9987cc3e1ee86f9a7e8961214a60b81685e89b2f2d9dc5986e3b2eed8f6
|
|
4
|
+
data.tar.gz: 50240698f875654431f4caf9681ddf8b163971c5f7d4ef9781849be17541e37d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9ddd81f3154b0bab5f2fa17890301f97fc960572874621bc42634f04caf826664c73a19082b9e8222a7155f2dcb5132f99a01afdb8ee841c6d6376bf162020b2
|
|
7
|
+
data.tar.gz: a8902dc527a3bcc3f26bce6f2eb8ecd442a26b4628db70c39011523bf409628f148c3288c7efc357b96b57fc67bc8aa640f7420d278494f8c32d2616bf9b615e
|
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
data/README.md
CHANGED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Sevgi
|
|
2
|
+
|
|
3
|
+
Top-level API and `.sevgi` script runner.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
gem install sevgi
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Require
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
require "sevgi"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Example
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
SVG(:minimal) do
|
|
21
|
+
rect(width: 3, height: 5)
|
|
22
|
+
end.call
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Executable
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
sevgi drawing.sevgi
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Ruby compatibility
|
|
32
|
+
|
|
33
|
+
Requires Ruby 3.4.0 or newer. CI verifies Ruby 3.4 and the current development Ruby from `.ruby-version`.
|
|
34
|
+
|
|
35
|
+
## Native prerequisites
|
|
36
|
+
|
|
37
|
+
The top-level gem installs Sevgi's standard components without native export gems. SVG-only scripts need no native
|
|
38
|
+
packages beyond the standard Ruby dependencies.
|
|
39
|
+
|
|
40
|
+
PDF/PNG export helpers come from `sevgi-sundries` and lazily load the optional Ruby gems `cairo`, `rsvg2`, and
|
|
41
|
+
`hexapdf`. On Debian/Ubuntu:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
sudo apt-get update
|
|
45
|
+
sudo apt-get install -y libcairo2-dev libgdk-pixbuf-2.0-dev libgirepository1.0-dev libglib2.0-dev librsvg2-dev pkg-config
|
|
46
|
+
gem install cairo rsvg2 hexapdf
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
On macOS with Homebrew:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
brew install cairo gdk-pixbuf gobject-introspection librsvg pkg-config
|
|
53
|
+
gem install cairo rsvg2 hexapdf
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Links
|
|
57
|
+
|
|
58
|
+
- Documentation: https://sevgi.roktas.dev
|
|
59
|
+
- API documentation: https://www.rubydoc.info/gems/sevgi
|
|
60
|
+
- Source: https://github.com/roktas/sevgi/tree/main/toplevel
|
|
61
|
+
- Changelog: https://github.com/roktas/sevgi/blob/main/CHANGELOG.md
|
data/lib/sevgi/binaries/rake.rb
CHANGED
|
@@ -2,14 +2,27 @@
|
|
|
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
|
+
#
|
|
9
|
+
# The script receives positional arguments through `ARGA` and keyword arguments through `ARGH`.
|
|
10
|
+
#
|
|
11
|
+
# @example Run a Sevgi script from a Rake task
|
|
12
|
+
# sevgi "drawings/card", "front", theme: :dark
|
|
13
|
+
#
|
|
14
|
+
# @param file [String] Sevgi script file, with or without `.sevgi` extension
|
|
15
|
+
# @param args [Array] positional arguments exposed to the script as `ARGA`
|
|
16
|
+
# @param kwargs [Hash] keyword arguments exposed to the script as `ARGH`
|
|
17
|
+
# @return [Sevgi::Executor::Scope, nil] execution scope, or nil for an empty file
|
|
18
|
+
# @raise [Sevgi::ArgumentError] when the script file cannot be found
|
|
19
|
+
# @see Sevgi::Executor.execute_file
|
|
7
20
|
def sevgi(file, *args, **kwargs)
|
|
8
|
-
Sevgi.execute_file(F.existing!(file, [EXTENSION])) do
|
|
9
|
-
|
|
21
|
+
Sevgi::Executor.execute_file(Sevgi::F.existing!(file, [Sevgi::EXTENSION])) do
|
|
22
|
+
extend(Sevgi)
|
|
10
23
|
|
|
11
|
-
|
|
12
|
-
|
|
24
|
+
const_set(:ARGA, args).freeze
|
|
25
|
+
const_set(:ARGH, kwargs).freeze
|
|
13
26
|
end
|
|
14
27
|
end
|
|
15
28
|
end
|
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,11 @@ 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 [Sevgi::Executor::Error] when `--exception` or `SEVGI_VOMIT` requests raw executor errors
|
|
60
|
+
# @raise [SystemExit] when command-line usage or script execution aborts
|
|
46
61
|
def call(argv)
|
|
47
62
|
return puts(help) if (options = Options.parse(argv = Array(argv))).help
|
|
48
63
|
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,23 @@ 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 original exception as the wrapped cause.
|
|
36
|
+
# @return [Exception]
|
|
37
|
+
def cause = error
|
|
38
|
+
|
|
39
|
+
# Returns the script load stack active at failure time.
|
|
40
|
+
# @return [Array<String>] script file names in load order
|
|
22
41
|
def stack = scope.stack
|
|
23
42
|
end
|
|
24
43
|
end
|
data/lib/sevgi/executor/scope.rb
CHANGED
|
@@ -2,11 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
module Sevgi
|
|
4
4
|
class Executor
|
|
5
|
+
# Holds one isolated Sevgi script execution scope and its result.
|
|
6
|
+
#
|
|
7
|
+
# Scope objects belong to one executor run and are pushed onto the current
|
|
8
|
+
# fiber's executor stack. They are not shared between concurrent executions.
|
|
5
9
|
class Scope
|
|
6
|
-
|
|
7
|
-
|
|
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]
|
|
21
|
+
# @note When no module is supplied, the internal module reports its name as `Sevgi::Main` for readable Ruby error
|
|
22
|
+
# messages without publishing a process-global `Sevgi::Main` constant.
|
|
10
23
|
def initialize(scope = nil)
|
|
11
24
|
@scope = scope || main
|
|
12
25
|
@recent = nil
|
|
@@ -14,16 +27,22 @@ module Sevgi
|
|
|
14
27
|
@stack = {}
|
|
15
28
|
end
|
|
16
29
|
|
|
30
|
+
# Reports whether execution captured an error.
|
|
31
|
+
# @return [Boolean] true when execution failed
|
|
17
32
|
def error? = !error.nil?
|
|
18
33
|
|
|
34
|
+
# Executes one source object in this scope.
|
|
35
|
+
# @param source [Sevgi::Executor::Source] source to evaluate
|
|
36
|
+
# @param receiver [Object, nil] receiver used while booting the DSL
|
|
37
|
+
# @yield optional boot block that installs DSL methods before evaluation
|
|
38
|
+
# @yieldreturn [void]
|
|
39
|
+
# @return [Sevgi::Executor::Scope] self, with recent or error populated
|
|
40
|
+
# @api private
|
|
19
41
|
def call(source, receiver = nil, &boot)
|
|
20
42
|
push(source)
|
|
21
43
|
|
|
22
44
|
tap do
|
|
23
|
-
@recent =
|
|
24
|
-
boot(receiver, &boot)
|
|
25
|
-
evaluate(source)
|
|
26
|
-
end
|
|
45
|
+
@recent = run(source, receiver, &boot)
|
|
27
46
|
|
|
28
47
|
# rubocop:disable Lint/RescueException
|
|
29
48
|
rescue Exception => e
|
|
@@ -34,14 +53,41 @@ module Sevgi
|
|
|
34
53
|
end
|
|
35
54
|
end
|
|
36
55
|
|
|
37
|
-
|
|
56
|
+
# Captures a preprocessing failure for this scope.
|
|
57
|
+
# @param source [Sevgi::Executor::Source] source active when preprocessing failed
|
|
58
|
+
# @param error [Exception] original preprocessing exception
|
|
59
|
+
# @return [Sevgi::Executor::Scope] self, with error populated
|
|
60
|
+
# @api private
|
|
61
|
+
def capture(source, error)
|
|
62
|
+
push(source)
|
|
63
|
+
@error = Executor::Error.new(error, self)
|
|
64
|
+
self
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Loads a file into this existing execution scope.
|
|
68
|
+
# @param file [String] source file to read and evaluate
|
|
69
|
+
# @yield optional boot block that installs DSL methods before evaluation
|
|
70
|
+
# @yieldreturn [void]
|
|
71
|
+
# @return [Sevgi::Executor::Scope] self, with recent or error populated
|
|
72
|
+
# @note File-read failures are captured as {Sevgi::Executor::Error} on this scope.
|
|
73
|
+
# @api private
|
|
74
|
+
def load(file, &block)
|
|
75
|
+
call(Source.load(file), &block)
|
|
76
|
+
rescue ::SystemCallError => e
|
|
77
|
+
capture(Source.new(string: "", file:, line: 1), e)
|
|
78
|
+
throw(:result, self)
|
|
79
|
+
end
|
|
38
80
|
|
|
81
|
+
# Returns the unique source stack for this execution.
|
|
82
|
+
# @return [Array<String>] source file keys in load order
|
|
83
|
+
# @note The stack is owned by this scope and is not shared with concurrent executions.
|
|
39
84
|
def stack = @stack.keys
|
|
40
85
|
|
|
86
|
+
# Returns the most recently pushed source.
|
|
87
|
+
# @return [Sevgi::Executor::Source, nil] most recent source object
|
|
88
|
+
# @api private
|
|
41
89
|
def peek = @stack[@stack.keys.last]
|
|
42
90
|
|
|
43
|
-
MAIN_MODULE = :Main
|
|
44
|
-
|
|
45
91
|
private
|
|
46
92
|
|
|
47
93
|
def boot(receiver, &boot)
|
|
@@ -56,15 +102,17 @@ module Sevgi
|
|
|
56
102
|
end
|
|
57
103
|
|
|
58
104
|
def main
|
|
59
|
-
Module.new.tap
|
|
60
|
-
Sevgi.send(:remove_const, MAIN_MODULE) if Sevgi.const_defined?(MAIN_MODULE)
|
|
61
|
-
Sevgi.const_set(MAIN_MODULE, mod)
|
|
62
|
-
end
|
|
105
|
+
Module.new.tap { |mod| mod.define_singleton_method(:name) { "Sevgi::Main" } }
|
|
63
106
|
end
|
|
64
107
|
|
|
65
108
|
def push(source)
|
|
66
109
|
tap { @stack[source.key] = source }
|
|
67
110
|
end
|
|
111
|
+
|
|
112
|
+
def run(source, receiver, &boot)
|
|
113
|
+
boot(receiver, &boot)
|
|
114
|
+
evaluate(source)
|
|
115
|
+
end
|
|
68
116
|
end
|
|
69
117
|
end
|
|
70
118
|
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,165 @@ 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. Active scope stacks are isolated per Ruby fiber, so
|
|
15
|
+
# concurrent executions can perform nested `Load` calls without sharing scope
|
|
16
|
+
# state. The process SIGINT handler is shared by Ruby, so executor runs guard it
|
|
17
|
+
# with a reference-counted critical section and restore the previous handler
|
|
18
|
+
# after the last active execution finishes.
|
|
10
19
|
class Executor
|
|
11
20
|
include Singleton
|
|
12
21
|
|
|
22
|
+
# Thread-current key used for the fiber-local executor scope stack.
|
|
23
|
+
# @api private
|
|
24
|
+
SCOPE_KEY = :sevgi_executor_scopes
|
|
25
|
+
private_constant :SCOPE_KEY, :Source
|
|
26
|
+
|
|
27
|
+
# Loads a script file inside the current executor scope.
|
|
28
|
+
# @param file [String] path to a Sevgi script file
|
|
29
|
+
# @return [Sevgi::Executor::Scope] current execution scope
|
|
30
|
+
# @raise [Sevgi::PanicError] when there is no active executor scope
|
|
31
|
+
# @note Uses the active executor scope from the current fiber.
|
|
32
|
+
# @api private
|
|
13
33
|
def self.load(file, ...)
|
|
14
34
|
PanicError.("box stack empty; create a box first") unless instance.current
|
|
15
35
|
|
|
16
36
|
instance.current.load(file, ...)
|
|
17
37
|
end
|
|
18
38
|
|
|
39
|
+
# Executes Ruby source inside a managed Sevgi script scope.
|
|
40
|
+
# @param string [String] source to evaluate
|
|
41
|
+
# @param file [String, nil] source file name used for errors and backtraces
|
|
42
|
+
# @param line [Integer, nil] starting source line used for errors and backtraces
|
|
43
|
+
# @param require [String, nil] optional Ruby library to require before execution
|
|
44
|
+
# @param receiver [Object, nil] receiver used while booting the DSL
|
|
45
|
+
# @yield optional boot block that installs DSL methods before evaluation
|
|
46
|
+
# @yieldreturn [void]
|
|
47
|
+
# @return [Sevgi::Executor::Scope, nil] execution scope, or nil for empty source
|
|
48
|
+
# @note Required-library load failures are captured as {Sevgi::Executor::Error} on the returned scope.
|
|
49
|
+
# @note Reentrant and concurrent calls keep independent scope stacks per fiber. The temporary SIGINT handler remains
|
|
50
|
+
# process-global while any execution is active.
|
|
19
51
|
def self.execute(string, file: nil, line: nil, require: nil, receiver: nil, &block)
|
|
20
|
-
|
|
52
|
+
execute_source(Source.new(string:, file:, line:), require:, receiver:, &block)
|
|
53
|
+
end
|
|
21
54
|
|
|
22
|
-
|
|
55
|
+
# Executes a file inside a managed Sevgi script scope.
|
|
56
|
+
# @param file [String] source file to read and execute
|
|
57
|
+
# @param require [String, nil] optional Ruby library to require before execution
|
|
58
|
+
# @param receiver [Object, nil] receiver used while booting the DSL
|
|
59
|
+
# @yield optional boot block that installs DSL methods before evaluation
|
|
60
|
+
# @yieldreturn [void]
|
|
61
|
+
# @return [Sevgi::Executor::Scope, nil] execution scope, or nil for an empty file
|
|
62
|
+
# @note File-read and required-library load failures are captured as {Sevgi::Executor::Error} on the returned scope.
|
|
63
|
+
# @note Reentrant and concurrent calls keep independent scope stacks per fiber. The temporary SIGINT handler remains
|
|
64
|
+
# process-global while any execution is active.
|
|
65
|
+
def self.execute_file(file, require: nil, receiver: nil, &block)
|
|
66
|
+
source = nil
|
|
67
|
+
begin
|
|
68
|
+
source = Source.load(file)
|
|
69
|
+
rescue ::SystemCallError => e
|
|
70
|
+
return capture_error(Source.new(string: "", file:, line: 1), e)
|
|
71
|
+
end
|
|
23
72
|
|
|
24
|
-
|
|
73
|
+
execute_source(source, require:, receiver:, &block)
|
|
74
|
+
end
|
|
25
75
|
|
|
26
|
-
|
|
27
|
-
|
|
76
|
+
# Removes the current executor scope.
|
|
77
|
+
# @return [Sevgi::Executor::Scope, nil] removed scope
|
|
78
|
+
# @api private
|
|
79
|
+
def self.shutdown
|
|
80
|
+
instance.shutdown
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def initialize
|
|
84
|
+
@signal_count = 0
|
|
85
|
+
@signal_mutex = Mutex.new
|
|
86
|
+
@signal_previous = nil
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Creates and pushes a new executor scope.
|
|
90
|
+
# @param scope [Module, nil] existing module scope to reuse
|
|
91
|
+
# @return [Sevgi::Executor::Scope] created scope
|
|
92
|
+
# @api private
|
|
93
|
+
def create(scope = nil) = Scope.new(scope).tap { scopes << it }
|
|
94
|
+
|
|
95
|
+
# Returns the active executor scope.
|
|
96
|
+
# @return [Sevgi::Executor::Scope, nil] active scope, if any
|
|
97
|
+
# @api private
|
|
98
|
+
def current = scopes.last
|
|
99
|
+
|
|
100
|
+
# Restores the process SIGINT handler when the last active execution ends.
|
|
101
|
+
# @return [void]
|
|
102
|
+
# @api private
|
|
103
|
+
def restore
|
|
104
|
+
@signal_mutex.synchronize do
|
|
105
|
+
next if @signal_count.zero?
|
|
106
|
+
|
|
107
|
+
@signal_count -= 1
|
|
108
|
+
next unless @signal_count.zero?
|
|
109
|
+
|
|
110
|
+
Signal.trap("INT", @signal_previous)
|
|
111
|
+
@signal_previous = nil
|
|
28
112
|
end
|
|
113
|
+
end
|
|
29
114
|
|
|
30
|
-
|
|
31
|
-
|
|
115
|
+
# Removes the active executor scope.
|
|
116
|
+
# @param scope [Sevgi::Executor::Scope, nil] exact scope to remove, or nil to pop the current scope
|
|
117
|
+
# @return [Sevgi::Executor::Scope, nil] removed scope
|
|
118
|
+
# @api private
|
|
119
|
+
def shutdown(scope = nil)
|
|
120
|
+
return scopes.pop unless scope
|
|
121
|
+
return scopes.pop if scopes.last.equal?(scope)
|
|
122
|
+
|
|
123
|
+
scopes.delete(scope)
|
|
32
124
|
end
|
|
33
125
|
|
|
34
|
-
|
|
35
|
-
|
|
126
|
+
# Installs the process SIGINT handler while one or more executions are active.
|
|
127
|
+
# @return [void]
|
|
128
|
+
# @api private
|
|
129
|
+
def trap
|
|
130
|
+
@signal_mutex.synchronize do
|
|
131
|
+
@signal_previous = Signal.trap("INT") { Kernel.abort("") } if @signal_count.zero?
|
|
132
|
+
|
|
133
|
+
@signal_count += 1
|
|
134
|
+
end
|
|
36
135
|
end
|
|
37
136
|
|
|
38
|
-
def self.
|
|
39
|
-
instance.
|
|
137
|
+
def self.capture_error(source, error)
|
|
138
|
+
instance.trap
|
|
139
|
+
scope = instance.create
|
|
140
|
+
scope.capture(source, error)
|
|
141
|
+
ensure
|
|
142
|
+
instance.restore
|
|
143
|
+
instance.shutdown(scope) if scope
|
|
40
144
|
end
|
|
41
145
|
|
|
42
|
-
def
|
|
146
|
+
def self.execute_source(source, require:, receiver:, &block)
|
|
147
|
+
return if source.string.empty?
|
|
148
|
+
|
|
149
|
+
instance.trap
|
|
150
|
+
scope = instance.create
|
|
151
|
+
catch(:result) { run_source(scope, source, require, receiver, &block) }
|
|
152
|
+
|
|
153
|
+
ensure
|
|
154
|
+
instance.restore
|
|
155
|
+
instance.shutdown(scope) if scope
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def self.run_source(scope, source, library, receiver, &block)
|
|
159
|
+
::Kernel.require(library) if library
|
|
160
|
+
scope.call(source, receiver, &block)
|
|
161
|
+
rescue ::LoadError => e
|
|
162
|
+
scope.capture(source, e)
|
|
163
|
+
end
|
|
43
164
|
|
|
44
|
-
|
|
165
|
+
private_class_method :capture_error, :execute_source, :run_source
|
|
45
166
|
|
|
46
|
-
|
|
167
|
+
private
|
|
47
168
|
|
|
48
|
-
def
|
|
169
|
+
def scopes = Thread.current[SCOPE_KEY] ||= []
|
|
49
170
|
end
|
|
50
171
|
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,31 @@ 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
|
+
# @note Required-library load failures are captured as {Sevgi::Executor::Error} on the returned scope.
|
|
15
|
+
# @note Reentrant and concurrent calls keep independent executor scope stacks per fiber.
|
|
16
|
+
# @see Sevgi::Executor.execute
|
|
10
17
|
def self.execute(*args, **kwargs) = Executor.execute(*args, **kwargs, &BootBlock)
|
|
11
18
|
|
|
19
|
+
# Executes a Sevgi script file with the full top-level DSL installed.
|
|
20
|
+
# @param args [Array] arguments forwarded to {Sevgi::Executor.execute_file}
|
|
21
|
+
# @param kwargs [Hash] keyword arguments forwarded to {Sevgi::Executor.execute_file}
|
|
22
|
+
# @return [Sevgi::Executor::Scope, nil] execution result, or nil for an empty file
|
|
23
|
+
# @note File-read and required-library load failures are captured as {Sevgi::Executor::Error} on the returned scope.
|
|
24
|
+
# @note Reentrant and concurrent calls keep independent executor scope stacks per fiber.
|
|
25
|
+
# @see Sevgi::Executor.execute_file
|
|
12
26
|
def self.execute_file(*args, **kwargs) = Executor.execute_file(*args, **kwargs, &BootBlock)
|
|
13
27
|
|
|
14
28
|
module Toplevel
|
|
29
|
+
# Loads one or more Sevgi files relative to the caller's source file.
|
|
30
|
+
# @param files [Array<String>] Sevgi script files to locate and execute
|
|
31
|
+
# @return [Array<String>] the input file list
|
|
32
|
+
# @raise [Sevgi::PanicError] when called without an active executor scope
|
|
33
|
+
# @raise [Sevgi::Error] when a file cannot be located
|
|
34
|
+
# @note `Load` resolves against the active executor scope in the current fiber.
|
|
15
35
|
def Load(*files)
|
|
16
36
|
start = ::File.dirname(caller_locations(1..1).first.path)
|
|
17
37
|
|
|
@@ -4,14 +4,13 @@ require "sevgi/function"
|
|
|
4
4
|
|
|
5
5
|
module Sevgi
|
|
6
6
|
module Toplevel
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
promote Function, :F
|
|
7
|
+
# Promotes the canonical function helper namespace as `F` in the full top-level DSL.
|
|
8
|
+
#
|
|
9
|
+
# @example Use helper functions inside script mode
|
|
10
|
+
# SVG do
|
|
11
|
+
# text F.pluralize("axis")
|
|
12
|
+
# end
|
|
13
|
+
# @see Sevgi::Function
|
|
14
|
+
promote Sevgi::Function, :F
|
|
16
15
|
end
|
|
17
16
|
end
|
|
@@ -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,15 @@ 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 [Integer] number of minor units in each major interval
|
|
11
|
+
# @return [Sevgi::Sundries::Grid] grid fitted to the canvas
|
|
12
|
+
# @raise [Sevgi::ArgumentError] when canvas is not a graphics canvas
|
|
13
|
+
# @raise [Sevgi::ArgumentError] when unit is not a finite positive number
|
|
14
|
+
# @raise [Sevgi::ArgumentError] when multiple is not a positive integer
|
|
15
|
+
# @raise [Sevgi::ArgumentError] when canvas dimensions, margins, and grid intervals cannot fit
|
|
7
16
|
def Grid(canvas, unit:, multiple:)
|
|
8
17
|
ArgumentError.("Must be a Canvas: #{canvas}") unless canvas.is_a?(Graphics::Canvas)
|
|
9
18
|
|
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,12 @@ module Sevgi
|
|
|
12
18
|
def inject(base)
|
|
13
19
|
return if base.instance_variable_defined?("@_toplevel_injected_")
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
if base.is_a?(::Module)
|
|
22
|
+
@constants.each do |name, constant|
|
|
23
|
+
base.const_set(name, constant) unless base.const_defined?(name, false)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
16
27
|
base.instance_variable_set("@_toplevel_injected_", true)
|
|
17
28
|
end
|
|
18
29
|
|
|
@@ -21,11 +32,21 @@ module Sevgi
|
|
|
21
32
|
end
|
|
22
33
|
end
|
|
23
34
|
|
|
35
|
+
# Injects promoted constants when the DSL is included in a module or class.
|
|
36
|
+
# @param base [Module] the class or module receiving promoted constants
|
|
37
|
+
# @return [void]
|
|
38
|
+
# @api private
|
|
24
39
|
def self.included(base)
|
|
25
40
|
super
|
|
26
41
|
inject(base)
|
|
27
42
|
end
|
|
28
43
|
|
|
44
|
+
# Injects promoted constants when the DSL is extended by a module.
|
|
45
|
+
# @param base [Object] the object or module receiving the DSL methods
|
|
46
|
+
# @return [void]
|
|
47
|
+
# @note Constants are promoted only to modules/classes. Extending an ordinary object installs methods without
|
|
48
|
+
# writing constants to `Object`.
|
|
49
|
+
# @api private
|
|
29
50
|
def self.extended(base)
|
|
30
51
|
super
|
|
31
52
|
inject(base)
|
data/lib/sevgi/version.rb
CHANGED
data/lib/sevgi.rb
CHANGED
|
@@ -9,20 +9,51 @@ 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 this module in a class or module installs DSL methods such as
|
|
30
|
+
# `Paper`, `Load`, and `Grid`, plus convenience constants such as `F`,
|
|
31
|
+
# `Geometry`, `Origin`, and `Export`. Extending a module does the same.
|
|
32
|
+
# Extending an ordinary object installs the methods only and does not write
|
|
33
|
+
# promoted constants to `Object`.
|
|
34
|
+
#
|
|
35
|
+
# @example Include the DSL in an object
|
|
36
|
+
# class Drawing
|
|
37
|
+
# include Sevgi
|
|
38
|
+
# end
|
|
17
39
|
module Sevgi
|
|
18
40
|
# See sevgi/toplevel/*.rb files for details
|
|
19
41
|
require_relative "sevgi/toplevel"
|
|
20
42
|
|
|
43
|
+
# Installs the full toplevel DSL into an including class or module.
|
|
44
|
+
# @param base [Module] the class or module receiving the DSL methods
|
|
45
|
+
# @return [void]
|
|
46
|
+
# @api private
|
|
21
47
|
def self.included(base)
|
|
22
48
|
super
|
|
23
49
|
base.include(Toplevel)
|
|
24
50
|
end
|
|
25
51
|
|
|
52
|
+
# Installs the full toplevel DSL into an extending object or module.
|
|
53
|
+
# @param base [Object] the receiver extended with the DSL methods
|
|
54
|
+
# @return [void]
|
|
55
|
+
# @note Promoted constants are written only when base is a module or class.
|
|
56
|
+
# @api private
|
|
26
57
|
def self.extended(base)
|
|
27
58
|
super
|
|
28
59
|
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.94.0
|
|
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.94.0
|
|
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.94.0
|
|
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.94.0
|
|
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.94.0
|
|
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.94.0
|
|
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.94.0
|
|
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.94.0
|
|
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.94.0
|
|
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.94.0
|
|
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.94.0
|
|
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.94.0
|
|
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.94.0
|
|
96
96
|
description: Provides a scriptable DSL with utilities for creating SVG content with
|
|
97
97
|
Ruby.
|
|
98
98
|
email: roktas@gmail.com
|
|
@@ -101,6 +101,8 @@ executables:
|
|
|
101
101
|
extensions: []
|
|
102
102
|
extra_rdoc_files: []
|
|
103
103
|
files:
|
|
104
|
+
- CHANGELOG.md
|
|
105
|
+
- LICENSE
|
|
104
106
|
- README.md
|
|
105
107
|
- bin/sevgi
|
|
106
108
|
- lib/sevgi.rb
|
|
@@ -133,7 +135,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
133
135
|
requirements:
|
|
134
136
|
- - ">="
|
|
135
137
|
- !ruby/object:Gem::Version
|
|
136
|
-
version: 3.4.0
|
|
138
|
+
version: 3.4.0
|
|
137
139
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
140
|
requirements:
|
|
139
141
|
- - ">="
|