sevgi 0.93.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f8e80618af5d1d88338b10db45b911cbca256c113ec3b7458b5b4e6e3a9ba7e
4
- data.tar.gz: f879baa77b005b91cb674d350d8ed79551f2be36132c7039f188ddb64ba58779
3
+ metadata.gz: ceb2e9987cc3e1ee86f9a7e8961214a60b81685e89b2f2d9dc5986e3b2eed8f6
4
+ data.tar.gz: 50240698f875654431f4caf9681ddf8b163971c5f7d4ef9781849be17541e37d
5
5
  SHA512:
6
- metadata.gz: fbce000770d881a72ac107ba52b39aa6bdc9ffcbfaacd2d2609cb7c6fef7251a0976fee6989c3a97b87973507dbaecfedcce78dfe72a59e80eea124c8e53a641
7
- data.tar.gz: 7a4dc86d8c75f75092a9e159fa4e023aeefa14cc1feb338f4e561095d331e535d45dc2da4e39e5b289d78444cf7c9d731393914290f917dd98366a4f30ab2d1b
6
+ metadata.gz: 9ddd81f3154b0bab5f2fa17890301f97fc960572874621bc42634f04caf826664c73a19082b9e8222a7155f2dcb5132f99a01afdb8ee841c6d6376bf162020b2
7
+ data.tar.gz: a8902dc527a3bcc3f26bce6f2eb8ecd442a26b4628db70c39011523bf409628f148c3288c7efc357b96b57fc67bc8aa640f7420d278494f8c32d2616bf9b615e
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ # Changelog
2
+
3
+ Sevgi follows the root Sevgi release notes:
4
+ https://github.com/roktas/sevgi/blob/main/CHANGELOG.md
data/LICENSE ADDED
@@ -0,0 +1,5 @@
1
+ Sevgi is distributed under the GNU General Public License v3.0 or later.
2
+
3
+ SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ Full project license: https://github.com/roktas/sevgi/blob/main/LICENSE
data/README.md CHANGED
@@ -1,5 +1,61 @@
1
1
  # Sevgi
2
2
 
3
- Provides the top-level API and `.sevgi` script runner.
3
+ Top-level API and `.sevgi` script runner.
4
4
 
5
- See the root [README](../README.md) and the documentation site for usage.
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
@@ -5,17 +5,24 @@ require "sevgi"
5
5
  # Extends FileUtils with Sevgi script helpers for Rake tasks.
6
6
  module FileUtils
7
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
+ #
8
14
  # @param file [String] Sevgi script file, with or without `.sevgi` extension
9
15
  # @param args [Array] positional arguments exposed to the script as `ARGA`
10
16
  # @param kwargs [Hash] keyword arguments exposed to the script as `ARGH`
11
17
  # @return [Sevgi::Executor::Scope, nil] execution scope, or nil for an empty file
12
18
  # @raise [Sevgi::ArgumentError] when the script file cannot be found
19
+ # @see Sevgi::Executor.execute_file
13
20
  def sevgi(file, *args, **kwargs)
14
- Sevgi.execute_file(F.existing!(file, [EXTENSION])) do |mod|
15
- include(Sevgi)
21
+ Sevgi::Executor.execute_file(Sevgi::F.existing!(file, [Sevgi::EXTENSION])) do
22
+ extend(Sevgi)
16
23
 
17
- mod.const_set(:ARGA, args).freeze
18
- mod.const_set(:ARGH, kwargs).freeze
24
+ const_set(:ARGA, args).freeze
25
+ const_set(:ARGH, kwargs).freeze
19
26
  end
20
27
  end
21
28
  end
@@ -56,8 +56,7 @@ module Sevgi
56
56
  # Runs the `sevgi` command-line interface.
57
57
  # @param argv [Array<String>, String, nil] command-line arguments
58
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
59
+ # @raise [Sevgi::Executor::Error] when `--exception` or `SEVGI_VOMIT` requests raw executor errors
61
60
  # @raise [SystemExit] when command-line usage or script execution aborts
62
61
  def call(argv)
63
62
  return puts(help) if (options = Options.parse(argv = Array(argv))).help
@@ -32,6 +32,10 @@ module Sevgi
32
32
  .map { |line| line.delete_prefix("#{::Dir.pwd}/") }
33
33
  end
34
34
 
35
+ # Returns the original exception as the wrapped cause.
36
+ # @return [Exception]
37
+ def cause = error
38
+
35
39
  # Returns the script load stack active at failure time.
36
40
  # @return [Array<String>] script file names in load order
37
41
  def stack = scope.stack
@@ -3,10 +3,10 @@
3
3
  module Sevgi
4
4
  class Executor
5
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.
6
9
  class Scope
7
- # Error raised by low-level executor scope operations.
8
- Error = Class.new(::Sevgi::Error)
9
-
10
10
  # @!attribute [r] scope
11
11
  # @return [Module] isolated module where script source is evaluated
12
12
  # @!attribute [r] recent
@@ -18,6 +18,8 @@ module Sevgi
18
18
  # Creates a script execution scope.
19
19
  # @param scope [Module, nil] existing module to evaluate source in
20
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.
21
23
  def initialize(scope = nil)
22
24
  @scope = scope || main
23
25
  @recent = nil
@@ -51,17 +53,34 @@ module Sevgi
51
53
  end
52
54
  end
53
55
 
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
+
54
67
  # Loads a file into this existing execution scope.
55
68
  # @param file [String] source file to read and evaluate
56
69
  # @yield optional boot block that installs DSL methods before evaluation
57
70
  # @yieldreturn [void]
58
71
  # @return [Sevgi::Executor::Scope] self, with recent or error populated
59
- # @raise [Errno::ENOENT] when the file cannot be read
72
+ # @note File-read failures are captured as {Sevgi::Executor::Error} on this scope.
60
73
  # @api private
61
- def load(file, &block) = call(Source.load(file), &block)
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
62
80
 
63
81
  # Returns the unique source stack for this execution.
64
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.
65
84
  def stack = @stack.keys
66
85
 
67
86
  # Returns the most recently pushed source.
@@ -69,9 +88,6 @@ module Sevgi
69
88
  # @api private
70
89
  def peek = @stack[@stack.keys.last]
71
90
 
72
- # Constant name used for the temporary script module under {Sevgi}.
73
- MAIN_MODULE = :Main
74
-
75
91
  private
76
92
 
77
93
  def boot(receiver, &boot)
@@ -86,10 +102,7 @@ module Sevgi
86
102
  end
87
103
 
88
104
  def main
89
- Module.new.tap do |mod|
90
- Sevgi.send(:remove_const, MAIN_MODULE) if Sevgi.const_defined?(MAIN_MODULE)
91
- Sevgi.const_set(MAIN_MODULE, mod)
92
- end
105
+ Module.new.tap { |mod| mod.define_singleton_method(:name) { "Sevgi::Main" } }
93
106
  end
94
107
 
95
108
  def push(source)
@@ -11,14 +11,24 @@ module Sevgi
11
11
  #
12
12
  # The executor is used by script mode and by the `Load` DSL word to preserve a
13
13
  # useful load stack while keeping DSL methods out of the caller's global object
14
- # whenever possible.
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.
15
19
  class Executor
16
20
  include Singleton
17
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
+
18
27
  # Loads a script file inside the current executor scope.
19
28
  # @param file [String] path to a Sevgi script file
20
29
  # @return [Sevgi::Executor::Scope] current execution scope
21
30
  # @raise [Sevgi::PanicError] when there is no active executor scope
31
+ # @note Uses the active executor scope from the current fiber.
22
32
  # @api private
23
33
  def self.load(file, ...)
24
34
  PanicError.("box stack empty; create a box first") unless instance.current
@@ -35,20 +45,11 @@ module Sevgi
35
45
  # @yield optional boot block that installs DSL methods before evaluation
36
46
  # @yieldreturn [void]
37
47
  # @return [Sevgi::Executor::Scope, nil] execution scope, or nil for empty source
38
- # @raise [LoadError] when the optional required library cannot be loaded
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.
39
51
  def self.execute(string, file: nil, line: nil, require: nil, receiver: nil, &block)
40
- return if string.empty?
41
-
42
- interrupt = Signal.trap("INT") { Kernel.abort("") }
43
-
44
- ::Kernel.require(require) if require
45
-
46
- scope = instance.create
47
- catch(:result) { scope.call(Source.new(string:, file:, line:), receiver, &block) }
48
-
49
- ensure
50
- Signal.trap("INT", interrupt) if interrupt
51
- instance.shutdown if scope
52
+ execute_source(Source.new(string:, file:, line:), require:, receiver:, &block)
52
53
  end
53
54
 
54
55
  # Executes a file inside a managed Sevgi script scope.
@@ -58,10 +59,18 @@ module Sevgi
58
59
  # @yield optional boot block that installs DSL methods before evaluation
59
60
  # @yieldreturn [void]
60
61
  # @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
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.
63
65
  def self.execute_file(file, require: nil, receiver: nil, &block)
64
- execute(::File.read(file), file: file, line: 1, require:, receiver:, &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
72
+
73
+ execute_source(source, require:, receiver:, &block)
65
74
  end
66
75
 
67
76
  # Removes the current executor scope.
@@ -71,22 +80,92 @@ module Sevgi
71
80
  instance.shutdown
72
81
  end
73
82
 
74
- def initialize = @scopes = []
83
+ def initialize
84
+ @signal_count = 0
85
+ @signal_mutex = Mutex.new
86
+ @signal_previous = nil
87
+ end
75
88
 
76
89
  # Creates and pushes a new executor scope.
77
90
  # @param scope [Module, nil] existing module scope to reuse
78
91
  # @return [Sevgi::Executor::Scope] created scope
79
92
  # @api private
80
- def create(scope = nil) = Scope.new(scope).tap { @scopes << it }
93
+ def create(scope = nil) = Scope.new(scope).tap { scopes << it }
81
94
 
82
95
  # Returns the active executor scope.
83
96
  # @return [Sevgi::Executor::Scope, nil] active scope, if any
84
97
  # @api private
85
- def current = @scopes.last
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
112
+ end
113
+ end
86
114
 
87
115
  # Removes the active executor scope.
116
+ # @param scope [Sevgi::Executor::Scope, nil] exact scope to remove, or nil to pop the current scope
88
117
  # @return [Sevgi::Executor::Scope, nil] removed scope
89
118
  # @api private
90
- def shutdown = @scopes.pop
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)
124
+ end
125
+
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
135
+ end
136
+
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
144
+ end
145
+
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
164
+
165
+ private_class_method :capture_error, :execute_source, :run_source
166
+
167
+ private
168
+
169
+ def scopes = Thread.current[SCOPE_KEY] ||= []
91
170
  end
92
171
  end
@@ -11,7 +11,8 @@ module Sevgi
11
11
  # @param args [Array] arguments forwarded to {Sevgi::Executor.execute}
12
12
  # @param kwargs [Hash] keyword arguments forwarded to {Sevgi::Executor.execute}
13
13
  # @return [Sevgi::Executor::Scope, nil] execution result, or nil for empty source
14
- # @raise [LoadError] when the optional `require:` library cannot be loaded
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.
15
16
  # @see Sevgi::Executor.execute
16
17
  def self.execute(*args, **kwargs) = Executor.execute(*args, **kwargs, &BootBlock)
17
18
 
@@ -19,8 +20,8 @@ module Sevgi
19
20
  # @param args [Array] arguments forwarded to {Sevgi::Executor.execute_file}
20
21
  # @param kwargs [Hash] keyword arguments forwarded to {Sevgi::Executor.execute_file}
21
22
  # @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
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.
24
25
  # @see Sevgi::Executor.execute_file
25
26
  def self.execute_file(*args, **kwargs) = Executor.execute_file(*args, **kwargs, &BootBlock)
26
27
 
@@ -30,6 +31,7 @@ module Sevgi
30
31
  # @return [Array<String>] the input file list
31
32
  # @raise [Sevgi::PanicError] when called without an active executor scope
32
33
  # @raise [Sevgi::Error] when a file cannot be located
34
+ # @note `Load` resolves against the active executor scope in the current fiber.
33
35
  def Load(*files)
34
36
  start = ::File.dirname(caller_locations(1..1).first.path)
35
37
 
@@ -4,20 +4,13 @@ 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.
7
+ # Promotes the canonical function helper namespace as `F` in the full top-level DSL.
8
8
  #
9
9
  # @example Use helper functions inside script mode
10
10
  # SVG do
11
11
  # text F.pluralize("axis")
12
12
  # end
13
- module Function
14
- extend Sevgi::Function::Color
15
- extend Sevgi::Function::Math
16
- extend Sevgi::Function::Pluralize
17
- extend Sevgi::Function::Shell
18
- extend Sevgi::Function::UI
19
- end
20
-
21
- promote Function, :F
13
+ # @see Sevgi::Function
14
+ promote Sevgi::Function, :F
22
15
  end
23
16
  end
@@ -7,9 +7,12 @@ module Sevgi
7
7
  # Builds a drawable grid from a graphics canvas.
8
8
  # @param canvas [Sevgi::Graphics::Canvas] canvas defining page size and margins
9
9
  # @param unit [Numeric] minor grid unit
10
- # @param multiple [Numeric] number of minor units in each major interval
10
+ # @param multiple [Integer] number of minor units in each major interval
11
11
  # @return [Sevgi::Sundries::Grid] grid fitted to the canvas
12
- # @raise [Sevgi::ArgumentError] when canvas, unit, multiple, or fitting span is invalid
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
13
16
  def Grid(canvas, unit:, multiple:)
14
17
  ArgumentError.("Must be a Canvas: #{canvas}") unless canvas.is_a?(Graphics::Canvas)
15
18
 
@@ -18,9 +18,10 @@ module Sevgi
18
18
  def inject(base)
19
19
  return if base.instance_variable_defined?("@_toplevel_injected_")
20
20
 
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)
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
24
25
  end
25
26
 
26
27
  base.instance_variable_set("@_toplevel_injected_", true)
@@ -31,7 +32,7 @@ module Sevgi
31
32
  end
32
33
  end
33
34
 
34
- # Injects promoted constants when the DSL is included.
35
+ # Injects promoted constants when the DSL is included in a module or class.
35
36
  # @param base [Module] the class or module receiving promoted constants
36
37
  # @return [void]
37
38
  # @api private
@@ -40,9 +41,11 @@ module Sevgi
40
41
  inject(base)
41
42
  end
42
43
 
43
- # Injects promoted constants when the DSL is extended.
44
- # @param base [Object] the object or module receiving promoted constants
44
+ # Injects promoted constants when the DSL is extended by a module.
45
+ # @param base [Object] the object or module receiving the DSL methods
45
46
  # @return [void]
47
+ # @note Constants are promoted only to modules/classes. Extending an ordinary object installs methods without
48
+ # writing constants to `Object`.
46
49
  # @api private
47
50
  def self.extended(base)
48
51
  super
data/lib/sevgi/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Sevgi
4
4
  # Current version of the Sevgi top-level gem.
5
- VERSION = "0.93.1"
5
+ VERSION = "0.94.0"
6
6
  end
data/lib/sevgi.rb CHANGED
@@ -26,9 +26,11 @@ def SVG(...) = Sevgi::Graphics.SVG(...)
26
26
 
27
27
  # Full top-level API for Sevgi library and script consumers.
28
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`.
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`.
32
34
  #
33
35
  # @example Include the DSL in an object
34
36
  # class Drawing
@@ -50,6 +52,7 @@ module Sevgi
50
52
  # Installs the full toplevel DSL into an extending object or module.
51
53
  # @param base [Object] the receiver extended with the DSL methods
52
54
  # @return [void]
55
+ # @note Promoted constants are written only when base is a module or class.
53
56
  # @api private
54
57
  def self.extended(base)
55
58
  super
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.93.1
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.93.1
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.93.1
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.93.1
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.93.1
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.93.1
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.93.1
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.93.1
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.93.1
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.93.1
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.93.1
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.93.1
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.93.1
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.pre.preview1
138
+ version: 3.4.0
137
139
  required_rubygems_version: !ruby/object:Gem::Requirement
138
140
  requirements:
139
141
  - - ">="