sevgi 0.95.0 → 0.98.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,20 +4,106 @@ require "sevgi/derender"
4
4
 
5
5
  module Sevgi
6
6
  module Toplevel
7
+ # Converts inline SVG/XML content into a derender node.
8
+ # @param content [String] SVG/XML source content
9
+ # @param id [String, Symbol, nil] optional SVG id selecting a node inside the source
10
+ # @param omit [String, Symbol, Array<String, Symbol>, nil] exact attribute name or names omitted from the selected
11
+ # subtree after id selection
12
+ # @return [Sevgi::Derender::Node] selected node in the derender tree
13
+ # @raise [Sevgi::ArgumentError] when content is malformed or rootless, or the id is absent
14
+ # @see Sevgi.Decompile
15
+ # @see Sevgi::Derender.decompile
16
+ def Decompile(content, id: nil, omit: nil) = Derender.decompile(content, id:, omit:)
17
+
7
18
  # Converts an SVG/XML file into a derender node.
8
19
  # @param file [String] path to the source SVG/XML file
9
- # @param id [String, nil] optional SVG id selecting a node inside the source
20
+ # @param id [String, Symbol, nil] optional SVG id selecting a node inside the source
21
+ # @param omit [String, Symbol, Array<String, Symbol>, nil] exact attribute name or names omitted from the selected
22
+ # subtree after id selection
10
23
  # @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
24
+ # @raise [Sevgi::ArgumentError] when the file is absent, malformed, or rootless, or the id is absent
25
+ # @raise [SystemCallError] when the file cannot be read
26
+ # @see Sevgi.DecompileFile
12
27
  # @see Sevgi::Derender.decompile_file
13
- def Decompile(file, id = nil) = Derender.decompile_file(file, id:)
28
+ def DecompileFile(file, id: nil, omit: nil) = Derender.decompile_file(file, id:, omit:)
29
+
30
+ # Converts inline SVG/XML content into Sevgi DSL Ruby source.
31
+ # @param content [String] SVG/XML source content
32
+ # @param id [String, Symbol, nil] optional SVG id selecting a node inside the source
33
+ # @param omit [String, Symbol, Array<String, Symbol>, nil] exact attribute name or names omitted from the selected
34
+ # subtree after id selection
35
+ # @return [String] formatted Sevgi DSL source
36
+ # @raise [Sevgi::ArgumentError] when content is malformed or rootless, or the id is absent
37
+ # @raise [Sevgi::PanicError] when generated Ruby source cannot be formatted
38
+ # @see Sevgi.Derender
39
+ # @see Sevgi::Derender.derender
40
+ def Derender(content, id: nil, omit: nil) = Derender.derender(content, id:, omit:)
14
41
 
15
42
  # Converts an SVG/XML file into Sevgi DSL Ruby source.
16
43
  # @param file [String] path to the source SVG/XML file
17
- # @param id [String, nil] optional SVG id selecting a node inside the source
44
+ # @param id [String, Symbol, nil] optional SVG id selecting a node inside the source
45
+ # @param omit [String, Symbol, Array<String, Symbol>, nil] exact attribute name or names omitted from the selected
46
+ # subtree after id selection
18
47
  # @return [String] formatted Sevgi DSL source
19
- # @raise [Sevgi::ArgumentError] when the file cannot be found or the id is absent
48
+ # @raise [Sevgi::ArgumentError] when the file is absent, malformed, or rootless, or the id is absent
49
+ # @raise [Sevgi::PanicError] when generated Ruby source cannot be formatted
50
+ # @raise [SystemCallError] when the file cannot be read
51
+ # @see Sevgi.DerenderFile
20
52
  # @see Sevgi::Derender.derender_file
21
- def Derender(file, id = nil) = Derender.derender_file(file, id:)
53
+ def DerenderFile(file, id: nil, omit: nil) = Derender.derender_file(file, id:, omit:)
54
+
55
+ # Evaluates inline SVG/XML content under a graphics element, including the selected node.
56
+ # @param content [String] SVG/XML source content
57
+ # @param element [Sevgi::Graphics::Element] target graphics element
58
+ # @param id [String, Symbol, nil] optional SVG id selecting a node inside the source
59
+ # @param omit [String, Symbol, Array<String, Symbol>, nil] exact attribute name or names omitted from the selected
60
+ # subtree after id selection
61
+ # @return [Sevgi::Graphics::Element, nil] included selected/root element, or nil when it produces no output
62
+ # @raise [Sevgi::ArgumentError] when content is malformed or rootless, or the id is absent
63
+ # @see Sevgi.Evaluate
64
+ # @see Sevgi::Derender.evaluate
65
+ def Evaluate(content, element, id: nil, omit: nil) = Derender.evaluate(content, element, id:, omit:)
66
+
67
+ # Evaluates only the selected node's children from inline SVG/XML content under a graphics element.
68
+ # @param content [String] SVG/XML source content
69
+ # @param element [Sevgi::Graphics::Element] target graphics element
70
+ # @param id [String, Symbol, nil] optional SVG id selecting a node inside the source
71
+ # @param omit [String, Symbol, Array<String, Symbol>, nil] exact attribute name or names omitted from the selected
72
+ # subtree after id selection
73
+ # @return [Array<Sevgi::Graphics::Element>] immutable included-child snapshot
74
+ # @raise [Sevgi::ArgumentError] when content is malformed or rootless, or the id is absent
75
+ # @see Sevgi.EvaluateChildren
76
+ # @see Sevgi::Derender.evaluate_children
77
+ def EvaluateChildren(content, element, id: nil, omit: nil)
78
+ Derender.evaluate_children(content, element, id:, omit:)
79
+ end
80
+
81
+ # Evaluates only the selected node's children from an SVG/XML file under a graphics element.
82
+ # @param file [String] path to the source SVG/XML file
83
+ # @param element [Sevgi::Graphics::Element] target graphics element
84
+ # @param id [String, Symbol, nil] optional SVG id selecting a node inside the source
85
+ # @param omit [String, Symbol, Array<String, Symbol>, nil] exact attribute name or names omitted from the selected
86
+ # subtree after id selection
87
+ # @return [Array<Sevgi::Graphics::Element>] immutable included-child snapshot
88
+ # @raise [Sevgi::ArgumentError] when the file is absent, malformed, or rootless, or the id is absent
89
+ # @raise [SystemCallError] when the file cannot be read
90
+ # @see Sevgi.EvaluateChildrenFile
91
+ # @see Sevgi::Derender.evaluate_children_file
92
+ def EvaluateChildrenFile(file, element, id: nil, omit: nil)
93
+ Derender.evaluate_children_file(file, element, id:, omit:)
94
+ end
95
+
96
+ # Evaluates an SVG/XML file under a graphics element, including the selected node.
97
+ # @param file [String] path to the source SVG/XML file
98
+ # @param element [Sevgi::Graphics::Element] target graphics element
99
+ # @param id [String, Symbol, nil] optional SVG id selecting a node inside the source
100
+ # @param omit [String, Symbol, Array<String, Symbol>, nil] exact attribute name or names omitted from the selected
101
+ # subtree after id selection
102
+ # @return [Sevgi::Graphics::Element, nil] included selected/root element, or nil when it produces no output
103
+ # @raise [Sevgi::ArgumentError] when the file is absent, malformed, or rootless, or the id is absent
104
+ # @raise [SystemCallError] when the file cannot be read
105
+ # @see Sevgi.EvaluateFile
106
+ # @see Sevgi::Derender.evaluate_file
107
+ def EvaluateFile(file, element, id: nil, omit: nil) = Derender.evaluate_file(file, element, id:, omit:)
22
108
  end
23
109
  end
@@ -8,22 +8,61 @@ module Sevgi
8
8
  private_constant :BootBlock
9
9
 
10
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.
11
+ #
12
+ # @example Execute source in an isolated scope
13
+ # result = Sevgi.execute("F.pluralize('cat')")
14
+ # result.value # => "cats"
15
+ # @param string [String] source to evaluate
16
+ # @param file [String, nil] source file name used for errors and backtraces
17
+ # @param line [Integer, nil] starting source line used for errors and backtraces
18
+ # @param require [String, nil] optional Ruby library to require before execution
19
+ # @param main [Boolean] whether to install the DSL through Ruby's top-level main object
20
+ # @return [Sevgi::Executor::Result] immutable execution result
21
+ # @raise [Sevgi::ArgumentError] when source, file, line, required library, or main mode is invalid
22
+ # @note Script and required-library failures are captured in {Sevgi::Executor::Result#error}.
23
+ # @note The default isolated mode does not modify Ruby's top-level main object. `main: true` preserves the command-line
24
+ # default by installing Sevgi through main before evaluating source in the managed script scope.
25
+ # @note Empty source without `require:` is a strict no-op; the DSL boot block is unused.
15
26
  # @note Reentrant and concurrent calls keep independent executor scope stacks per fiber.
16
- # @see Sevgi::Executor.execute
17
- def self.execute(*args, **kwargs) = Executor.execute(*args, **kwargs, &BootBlock)
27
+ # @see https://sevgi.roktas.dev/execution/ Execution guide
28
+ def self.execute(string, file: nil, line: nil, require: nil, main: false)
29
+ Executor.__send__(:execute, string, file:, line:, require:, receiver: execution_receiver(main), &BootBlock)
30
+ end
18
31
 
19
32
  # 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.
33
+ # @param file [String] source file to read and execute
34
+ # @param as [String, nil] source basename used for evaluation, diagnostics, and caller-derived output defaults;
35
+ # its extension is replaced with `.sevgi` and the input file's directory is retained
36
+ # @param require [String, nil] optional Ruby library to require before execution
37
+ # @param main [Boolean] whether to install the DSL through Ruby's top-level main object
38
+ # @return [Sevgi::Executor::Result] immutable execution result
39
+ # @raise [Sevgi::ArgumentError] when file, logical source name, required library, or main mode is invalid
40
+ # @note File-read, script, and required-library failures are captured in {Sevgi::Executor::Result#error}.
41
+ # @note The default isolated mode does not modify Ruby's top-level main object. `main: true` preserves the command-line
42
+ # default by installing Sevgi through main before evaluating source in the managed script scope.
43
+ # @note An empty file without `require:` is a strict no-op; the DSL boot block is unused.
24
44
  # @note Reentrant and concurrent calls keep independent executor scope stacks per fiber.
25
- # @see Sevgi::Executor.execute_file
26
- def self.execute_file(*args, **kwargs) = Executor.execute_file(*args, **kwargs, &BootBlock)
45
+ # @see https://sevgi.roktas.dev/execution/ Execution guide
46
+ def self.execute_file(file, as: nil, require: nil, main: false)
47
+ as = source_name(file, as) if as
48
+ Executor.__send__(:execute_file, file, as:, require:, receiver: execution_receiver(main), &BootBlock)
49
+ end
50
+
51
+ def self.execution_receiver(main)
52
+ ArgumentError.("Sevgi main mode must be true or false") unless [true, false].include?(main)
53
+
54
+ TOPLEVEL_BINDING.receiver if main
55
+ end
56
+
57
+ def self.source_name(file, name)
58
+ ArgumentError.("Sevgi file must be a String") unless file.is_a?(::String)
59
+ ArgumentError.("Sevgi file name must be a non-empty String") unless name.is_a?(::String) && !name.empty?
60
+ ArgumentError.("Sevgi file name must not include a directory") unless ::File.basename(name) == name
61
+
62
+ ::File.join(::File.dirname(file), F.subext(".sevgi", name))
63
+ end
64
+
65
+ private_class_method :execution_receiver, :source_name
27
66
 
28
67
  module Toplevel
29
68
  # Loads one or more Sevgi files relative to the caller's source file.
@@ -32,13 +71,16 @@ module Sevgi
32
71
  # @raise [Sevgi::PanicError] when called without an active executor scope
33
72
  # @raise [Sevgi::Error] when a file cannot be located
34
73
  # @note `Load` resolves against the active executor scope in the current fiber.
74
+ # @note Ordinary library code should use Ruby `require`; `Load` is available only during Sevgi script execution.
75
+ # @see Sevgi.Load
76
+ # @see Sevgi.execute_file
35
77
  def Load(*files)
36
78
  start = ::File.dirname(caller_locations(1..1).first.path)
37
79
 
38
80
  files.each do |file|
39
81
  location = F.locate(file, start)
40
82
 
41
- ::Sevgi::Executor.load(location.file)
83
+ ::Sevgi::Executor.__send__(:load, location.file)
42
84
  end
43
85
  end
44
86
  end
@@ -4,14 +4,97 @@ require "sevgi/graphics"
4
4
 
5
5
  module Sevgi
6
6
  module Toplevel
7
+ # @overload Canvas(paper, **overrides)
8
+ # Builds a canvas from a paper profile with optional field overrides.
9
+ # @param paper [Sevgi::Graphics::Paper, Symbol, String] paper object or registered profile
10
+ # @param overrides [Hash] canvas field overrides
11
+ # @return [Sevgi::Graphics::Canvas]
12
+ # @raise [Sevgi::ArgumentError] when the paper or an override is invalid
13
+ # @overload Canvas(width:, height:, unit: "mm", name: :custom, margins: [])
14
+ # Builds a canvas from an explicit size.
15
+ # @param width [Numeric] canvas width
16
+ # @param height [Numeric] canvas height
17
+ # @param unit [Symbol, String] SVG unit
18
+ # @param name [Symbol, String] paper name
19
+ # @param margins [Array<Numeric>] margin shorthand values
20
+ # @return [Sevgi::Graphics::Canvas]
21
+ # @raise [Sevgi::ArgumentError] when a required field is omitted or a value is invalid
22
+ # @example Build a paper-backed canvas
23
+ # canvas = Sevgi.Canvas :a4, margins: [12, 10]
24
+ # @see Sevgi.Canvas
25
+ # @see Sevgi::Graphics.canvas
26
+ def Canvas(...) = Graphics.canvas(...)
27
+
28
+ # @overload Document(name)
29
+ # Looks up an existing document profile by name.
30
+ # @param name [Symbol, String] profile name
31
+ # @return [Class] document class
32
+ # @raise [Sevgi::ArgumentError] when the profile is unknown
33
+ # @overload Document(name, preambles: Undefined, attributes: Undefined)
34
+ # Defines or validates a named document profile.
35
+ # @param name [Symbol, String] profile name
36
+ # @param preambles [Array<String>, nil, Sevgi::Undefined] document preamble lines
37
+ # @param attributes [Hash, nil, Sevgi::Undefined] default root attributes
38
+ # @return [Class] document class
39
+ # @raise [Sevgi::ArgumentError] when a profile conflicts or metadata is invalid
40
+ # @overload Document(preambles: Undefined, attributes: Undefined)
41
+ # Defines an anonymous document profile without registering it globally.
42
+ # @param preambles [Array<String>, nil, Sevgi::Undefined] document preamble lines
43
+ # @param attributes [Hash, nil, Sevgi::Undefined] default root attributes
44
+ # @return [Class] anonymous document class
45
+ # @raise [Sevgi::ArgumentError] when metadata is invalid
46
+ # @return [Class] document class
47
+ # @example Define and use a document profile
48
+ # Sevgi.Document :icon, attributes: {viewBox: "0 0 24 24"}
49
+ # drawing = Sevgi.SVG(:icon) { circle cx: 12, cy: 12, r: 10 }
50
+ # @see Sevgi.Document
51
+ # @see Sevgi::Graphics.document
52
+ def Document(...) = Graphics.document(...)
53
+
54
+ # Defines or replaces a document profile.
55
+ # @param name [Symbol, String] profile name
56
+ # @param preambles [Array<String>, nil] document preamble lines
57
+ # @param attributes [Hash, nil] default root attributes
58
+ # @return [Class] document class
59
+ # @raise [Sevgi::ArgumentError] when the name or metadata is invalid
60
+ # @see Sevgi.Document!
61
+ # @see Sevgi::Graphics.document!
62
+ def Document!(name, preambles: [], attributes: {})
63
+ Graphics.document!(name, preambles:, attributes:)
64
+ end
65
+
66
+ # Builds an SVG document through the full Sevgi top-level DSL.
67
+ # @param document [Symbol, String, Class] document profile name or document class
68
+ # @param canvas [Sevgi::Graphics::Canvas, Sevgi::Graphics::Paper, Symbol, String, Sevgi::Undefined, nil] optional
69
+ # canvas or paper profile
70
+ # @param attributes [Hash] root SVG attributes
71
+ # @yield the document block evaluated in the SVG document context
72
+ # @yieldreturn [void]
73
+ # @return [Sevgi::Graphics::Document::Proto] SVG document object
74
+ # @raise [Sevgi::ArgumentError] when the document, paper, or canvas arguments are invalid
75
+ # @see Sevgi.SVG
76
+ # @see Sevgi::Graphics.SVG
77
+ def SVG(document = :default, canvas = Undefined, **attributes, &block)
78
+ Graphics.SVG(document, canvas, **attributes, &block)
79
+ end
80
+
81
+ # @overload Mixin(mod, document = Sevgi::Graphics::Document::Base)
82
+ # Adds a named graphics mixture to a document class.
83
+ # @param mod [Symbol, String] named mixture to mix into the document
84
+ # @param document [Class] document class receiving the mixture
85
+ # @return [nil]
86
+ # @raise [NameError] when a named mixture cannot be resolved
87
+ # @see Sevgi.Mixin
88
+ # @see Sevgi::Graphics::Mixtures.mixin
7
89
  # @overload Mixin(mod, document = Sevgi::Graphics::Document::Base, &block)
8
- # Adds graphics mixture methods to a document class.
90
+ # Adds a named graphics mixture and an anonymous extension to a document class.
9
91
  # @param mod [Symbol, String] named mixture to mix into the document
10
92
  # @param document [Class] document class receiving the mixture
11
93
  # @yield optional anonymous mixture module body
12
94
  # @yieldreturn [void]
13
- # @return [void]
95
+ # @return [Module] anonymous mixture
14
96
  # @raise [NameError] when a named mixture cannot be resolved
97
+ # @see Sevgi.Mixin
15
98
  # @see Sevgi::Graphics::Mixtures.mixin
16
99
  # @overload Mixin(document = Sevgi::Graphics::Document::Base, &block)
17
100
  # Adds an anonymous graphics mixture to a document class.
@@ -20,6 +103,7 @@ module Sevgi
20
103
  # @yieldreturn [void]
21
104
  # @return [Module] anonymous mixture
22
105
  # @raise [Sevgi::ArgumentError] when no named mixture or block is given
106
+ # @see Sevgi.Mixin
23
107
  # @see Sevgi::Graphics::Mixtures.mixin
24
108
  def Mixin(...) = Graphics::Mixtures.mixin(...)
25
109
 
@@ -27,10 +111,11 @@ module Sevgi
27
111
  # Defines or validates a named paper profile for DSL use.
28
112
  # @param width [Numeric] paper width
29
113
  # @param height [Numeric] paper height
30
- # @param name [Symbol] paper profile name
114
+ # @param name [Symbol, String] paper profile name
31
115
  # @param unit [String, Symbol] size unit
32
- # @return [Symbol] the paper profile name
116
+ # @return [Symbol, String] the original paper profile name
33
117
  # @raise [Sevgi::ArgumentError] when the profile is invalid or an existing profile differs
118
+ # @see Sevgi.Paper
34
119
  # @see Sevgi::Graphics#paper
35
120
  def Paper(...) = Graphics.paper(...)
36
121
 
@@ -38,10 +123,11 @@ module Sevgi
38
123
  # Defines or overwrites a named paper profile for DSL use.
39
124
  # @param width [Numeric] paper width
40
125
  # @param height [Numeric] paper height
41
- # @param name [Symbol] paper profile name
126
+ # @param name [Symbol, String] paper profile name
42
127
  # @param unit [String, Symbol] size unit
43
- # @return [Symbol] the paper profile name
128
+ # @return [Symbol, String] the original paper profile name
44
129
  # @raise [Sevgi::ArgumentError] when the profile is invalid or the paper name is reserved
130
+ # @see Sevgi.Paper!
45
131
  # @see Sevgi::Graphics#paper!
46
132
  def Paper!(...) = Graphics.paper!(...)
47
133
  end
@@ -4,7 +4,12 @@ require "sevgi/sundries"
4
4
 
5
5
  module Sevgi
6
6
  module Toplevel
7
- # Builds a drawable grid from a graphics canvas.
7
+ # Builds a drawable grid fitted inside a graphics canvas.
8
+ #
9
+ # The canvas margins are minimum clearances. Any span left after fitting
10
+ # whole major intervals is shared equally between the opposite margins, so
11
+ # their requested difference is preserved. The returned grid starts at
12
+ # `(0, 0)`; {Sevgi::Sundries::Grid#canvas} exposes the fitted page margins.
8
13
  # @param canvas [Sevgi::Graphics::Canvas] canvas defining page size and margins
9
14
  # @param unit [Numeric] minor grid unit
10
15
  # @param multiple [Integer] number of minor units in each major interval
@@ -13,13 +18,16 @@ module Sevgi
13
18
  # @raise [Sevgi::ArgumentError] when unit is not a finite positive number
14
19
  # @raise [Sevgi::ArgumentError] when multiple is not a positive integer
15
20
  # @raise [Sevgi::ArgumentError] when canvas dimensions, margins, and grid intervals cannot fit
21
+ # @see Sevgi.Grid
22
+ # @see Sevgi::Sundries::Grid
16
23
  def Grid(canvas, unit:, multiple:)
17
24
  ArgumentError.("Must be a Canvas: #{canvas}") unless canvas.is_a?(Graphics::Canvas)
18
25
 
19
- Sundries::Grid[
20
- Sundries::Ruler.new(brut: canvas.width, unit:, multiple:, margin: canvas.left),
21
- Sundries::Ruler.new(brut: canvas.height, unit:, multiple:, margin: canvas.top)
22
- ]
26
+ Sundries::Grid.new(
27
+ x: Sundries::Ruler.new(brut: canvas.width, unit:, multiple:, margins: [canvas.left, canvas.right]),
28
+ y: Sundries::Ruler.new(brut: canvas.height, unit:, multiple:, margins: [canvas.top, canvas.bottom]),
29
+ canvas:
30
+ )
23
31
  end
24
32
 
25
33
  promote Sundries::Export
@@ -7,21 +7,19 @@ module Sevgi
7
7
  # normally be included directly.
8
8
  #
9
9
  # @see Sevgi
10
+ # @see https://sevgi.roktas.dev/library-mode/ Library mode guide
11
+ # @see https://sevgi.roktas.dev/script-mode/ Script mode guide
10
12
  module Toplevel
11
- @constants = {}
13
+ @promotions = {}
12
14
 
13
15
  class << self
14
- # Returns an immutable snapshot of promoted constants.
15
- # @return [Hash<Symbol, Object>]
16
- def constants = @constants.dup.freeze
17
-
18
16
  private
19
17
 
20
18
  def inject(base)
21
19
  return if base.instance_variable_defined?("@_toplevel_injected_")
22
20
 
23
21
  if base.is_a?(::Module)
24
- @constants.each do |name, constant|
22
+ @promotions.each do |name, constant|
25
23
  base.const_set(name, constant) unless base.const_defined?(name, false)
26
24
  end
27
25
  end
@@ -30,7 +28,7 @@ module Sevgi
30
28
  end
31
29
 
32
30
  def promote(constant, symbol = Undefined)
33
- @constants[Undefined.default(symbol, constant.to_s.split("::").last.to_sym)] = constant
31
+ @promotions[Undefined.default(symbol, constant.to_s.split("::").last.to_sym)] = constant
34
32
  end
35
33
  end
36
34
 
@@ -53,6 +51,8 @@ module Sevgi
53
51
  super
54
52
  inject(base)
55
53
  end
54
+
55
+ private_class_method :extended, :included
56
56
  end
57
57
 
58
58
  extend Toplevel
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.95.0"
5
+ VERSION = "0.98.2"
6
6
  end
data/lib/sevgi.rb CHANGED
@@ -9,36 +9,121 @@ require "sevgi/sundries"
9
9
 
10
10
  require "sevgi/version"
11
11
 
12
- # Minimal top-level SVG interface installed by `require "sevgi"`.
13
- # @return [Module] the graphics module used as the SVG DSL namespace
14
- SVG = Sevgi::Graphics
12
+ require_relative "sevgi/toplevel"
13
+ require_relative "sevgi/svg"
14
+
15
+ # SVG-domain facade installed by `require "sevgi"`.
16
+ # @return [Module] the public SVG operations and types
17
+ SVG = Sevgi::SVG
15
18
 
16
19
  # @overload SVG(document = :default, canvas = Undefined, **attributes, &block)
17
20
  # 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
21
+ # @param document [Symbol, String, Class] document profile name or document class
22
+ # @param canvas [Sevgi::Graphics::Canvas, Sevgi::Graphics::Paper, Symbol, String, Sevgi::Undefined, nil] optional
23
+ # canvas or paper profile
20
24
  # @param attributes [Hash] root SVG attributes
21
25
  # @yield the document block evaluated in the SVG document context
22
26
  # @yieldreturn [void]
23
27
  # @return [Sevgi::Graphics::Document::Proto] a rendered SVG document object
24
28
  # @raise [Sevgi::ArgumentError] when the document, paper, or canvas arguments are invalid
25
- def SVG(...) = Sevgi::Graphics.SVG(...)
29
+ # @example Build through the global library entrypoint
30
+ # SVG(:minimal) { circle r: 4 }.Render
31
+ # @see Sevgi.SVG
32
+ # @see Sevgi::Toplevel#SVG
33
+ def SVG(...) = Sevgi.SVG(...)
26
34
 
27
35
  # Full top-level API for Sevgi library and script consumers.
28
36
  #
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`.
37
+ # `require "sevgi"` installs the global document builder `SVG(...)` and the independent {Sevgi::SVG} facade. SVG-domain
38
+ # operations use capitalized method names on that facade, such as `SVG.Canvas`, `SVG.Paper`, and `SVG.Derender`; types
39
+ # and namespaces use Ruby's double-colon notation, such as `SVG::Canvas`. The same operations are available on `Sevgi`
40
+ # for consumers that prefer the full toolkit namespace. Process-level operations such as {Sevgi.execute} remain on
41
+ # `Sevgi` rather than the SVG facade.
42
+ #
43
+ # Including this module in a class or module installs the DSL methods plus convenience constants `F`, `Geometry`,
44
+ # `Origin`, and `Export`; script execution provides the same promoted scope. Extending a module does the same. Extending
45
+ # an ordinary object installs methods only and does not write promoted constants to `Object`. Focused component requires
46
+ # expose their namespaced component APIs instead of this full top-level surface.
47
+ #
48
+ # `Load` is meaningful only during {Sevgi.execute}, {Sevgi.execute_file}, or command-line script execution. It resolves
49
+ # nested `.sevgi` files through the active executor scope; it is not a general-purpose replacement for Ruby `require`.
34
50
  #
35
51
  # @example Include the DSL in an object
36
52
  # class Drawing
37
53
  # include Sevgi
38
54
  # end
55
+ # @see https://sevgi.roktas.dev/library-mode/ Library mode guide
56
+ # @see https://sevgi.roktas.dev/script-mode/ Script mode guide
57
+ # @see https://sevgi.roktas.dev/execution/ Embedded execution guide
39
58
  module Sevgi
40
- # See sevgi/toplevel/*.rb files for details
41
- require_relative "sevgi/toplevel"
59
+ # @!method self.Canvas(...)
60
+ # Builds a canvas from a paper profile or explicit dimensions.
61
+ # @return [Sevgi::Graphics::Canvas] canvas value
62
+ # @see Sevgi::Toplevel#Canvas
63
+ # @!method self.Document(...)
64
+ # Defines, validates, or looks up a document profile.
65
+ # @return [Class] document class
66
+ # @see Sevgi::Toplevel#Document
67
+ # @!method self.Document!(...)
68
+ # Defines or replaces a document profile.
69
+ # @return [Class] document class
70
+ # @see Sevgi::Toplevel#Document!
71
+ # @!method self.SVG(...)
72
+ # Builds an SVG document through the explicit namespaced top-level API.
73
+ # @return [Sevgi::Graphics::Document::Proto] SVG document object
74
+ # @see Sevgi::Toplevel#SVG
75
+ # @!method self.Mixin(...)
76
+ # Extends a document profile with a named or anonymous graphics mixture.
77
+ # @return [Module, nil] anonymous mixture when supplied, otherwise nil
78
+ # @see Sevgi::Toplevel#Mixin
79
+ # @!method self.Paper(...)
80
+ # Defines or validates a named paper profile.
81
+ # @return [Symbol, String] original paper profile name
82
+ # @see Sevgi::Toplevel#Paper
83
+ # @!method self.Paper!(...)
84
+ # Defines or overwrites a named paper profile.
85
+ # @return [Symbol, String] original paper profile name
86
+ # @see Sevgi::Toplevel#Paper!
87
+ # @!method self.Grid(...)
88
+ # Fits a drawable grid to a graphics canvas.
89
+ # @return [Sevgi::Sundries::Grid] fitted grid
90
+ # @see Sevgi::Toplevel#Grid
91
+ # @!method self.Decompile(...)
92
+ # Converts inline SVG/XML into an immutable Derender node.
93
+ # @return [Sevgi::Derender::Node] selected node
94
+ # @see Sevgi::Toplevel#Decompile
95
+ # @!method self.DecompileFile(...)
96
+ # Converts an SVG/XML file into an immutable Derender node.
97
+ # @return [Sevgi::Derender::Node] selected node
98
+ # @see Sevgi::Toplevel#DecompileFile
99
+ # @!method self.Derender(...)
100
+ # Converts inline SVG/XML into formatted Sevgi DSL source.
101
+ # @return [String] formatted Sevgi DSL source
102
+ # @see Sevgi::Toplevel#Derender
103
+ # @!method self.DerenderFile(...)
104
+ # Converts an SVG/XML file into formatted Sevgi DSL source.
105
+ # @return [String] formatted Sevgi DSL source
106
+ # @see Sevgi::Toplevel#DerenderFile
107
+ # @!method self.Evaluate(...)
108
+ # Includes an inline SVG/XML node under a graphics element.
109
+ # @return [Sevgi::Graphics::Element, nil] included element, or nil when no output is produced
110
+ # @see Sevgi::Toplevel#Evaluate
111
+ # @!method self.EvaluateFile(...)
112
+ # Includes an SVG/XML file node under a graphics element.
113
+ # @return [Sevgi::Graphics::Element, nil] included element, or nil when no output is produced
114
+ # @see Sevgi::Toplevel#EvaluateFile
115
+ # @!method self.EvaluateChildren(...)
116
+ # Includes only an inline SVG/XML node's children under a graphics element.
117
+ # @return [Array<Sevgi::Graphics::Element>] immutable included-child snapshot
118
+ # @see Sevgi::Toplevel#EvaluateChildren
119
+ # @!method self.EvaluateChildrenFile(...)
120
+ # Includes only an SVG/XML file node's children under a graphics element.
121
+ # @return [Array<Sevgi::Graphics::Element>] immutable included-child snapshot
122
+ # @see Sevgi::Toplevel#EvaluateChildrenFile
123
+ # @!method self.Load(...)
124
+ # Loads nested `.sevgi` files through the active executor scope.
125
+ # @return [Array<String>] requested file names
126
+ # @see Sevgi::Toplevel#Load
42
127
 
43
128
  # Installs the full toplevel DSL into an including class or module.
44
129
  # @param base [Module] the class or module receiving the DSL methods
@@ -58,4 +143,6 @@ module Sevgi
58
143
  super
59
144
  base.extend(Toplevel)
60
145
  end
146
+
147
+ private_class_method :extended, :included
61
148
  end