sevgi-standard 0.73.2 → 0.93.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd86945f53c28030148a5d7e40a05c370ea9743f92cf1c264f3c30741d65baa8
4
- data.tar.gz: d4012c0190f449a989c10f35d60153c397dabd9536d5567caa3956c1e3f0db68
3
+ metadata.gz: 845a552c81846d5e0183f1ef2243ea0ddcf90a94a8bcbe64edab77df9e6ac877
4
+ data.tar.gz: dd99636019944aaee3d6481c44fc67b48c834718a1f843afd22bfb5c1553ec78
5
5
  SHA512:
6
- metadata.gz: 65a9c7367b4ebc2e34fe647d71cfe50ee0038ab3acdf688a2e5460d9bd500677ca93c91daa63c08e1b958c6a6b993e864cca65ef0ee38c6bd61b78fbb532846f
7
- data.tar.gz: 0b787afc81c3edf324bc272c7d25d22ff7a60d05b48fc8708981dc5ae2e8c6587a178b8a35cb9fba6ca2dd2edc4edc889190ded91a488aa47e007d70272a5d8d
6
+ metadata.gz: d1d2d534bab867b6357bb7623f498d60a82965446c06c0553cc37a63529a8e783024ff65f10422cdd382dec9f19497f1c435965a4e72bfd3afcb2afb0e228afa
7
+ data.tar.gz: aae602ac61b37d6c4de6549bb2763d02fd3a1c3de3157dc8f6bc831d1a93a0f357a125be55fb221d0acf3a7a9c2480fc3d8965e8f1b0b3dde5cded9fdb39f070
data/README.md CHANGED
@@ -0,0 +1,5 @@
1
+ # Sevgi Standard
2
+
3
+ Provides SVG element, attribute, and conformance data used by Sevgi validation.
4
+
5
+ See the root [README](../README.md) and the documentation site for usage.
@@ -2,22 +2,39 @@
2
2
 
3
3
  module Sevgi
4
4
  module Standard
5
+ # Validates one SVG element usage against the standard data.
6
+ # @api private
5
7
  class Conform
6
8
  require_relative "model"
7
9
 
8
- attr_reader :element, :spec
10
+ # @return [Symbol] SVG element name
11
+ attr_reader :element
9
12
 
13
+ # @return [Hash] expanded standard specification for the element
14
+ attr_reader :spec
15
+
16
+ # Builds a validator for one SVG element.
17
+ # @param element [Symbol] SVG element name
18
+ # @return [void]
19
+ # @raise [Sevgi::InvalidElementsError] when the element is unknown
20
+ # @raise [Sevgi::PanicError] when the element model is missing or unimplemented
10
21
  def initialize(element)
11
22
  InvalidElementsError.(element) unless (@spec = Specification[@element = element])
12
23
 
13
- raise ModelError, "No model specified: #{element}" unless spec[:model]
14
- raise ModelError, "Model unimplemented: #{spec[:model]}" unless Model.const_defined?(spec[:model])
24
+ PanicError.("No model specified: #{element}") unless spec[:model]
25
+ PanicError.("Model unimplemented: #{spec[:model]}") unless Model.const_defined?(spec[:model])
15
26
 
16
27
  extend(Model.const_get(spec[:model]))
17
28
 
18
- raise NoMethodError, "#{self.class}#apply must be implemented" unless respond_to?(:apply)
29
+ PanicError.("#{self.class}#apply must be implemented") unless respond_to?(:apply)
19
30
  end
20
31
 
32
+ # Validates one usage of the configured SVG element.
33
+ # @param attributes [Array<Symbol>, nil] attribute names used by the element
34
+ # @param cdata [String, nil] character data content
35
+ # @param elements [Array<Symbol>, nil] child element names
36
+ # @return [Boolean] true when the usage conforms
37
+ # @raise [Sevgi::ValidationError] when the usage violates the standard data
21
38
  def call(attributes: nil, cdata: nil, elements: nil)
22
39
  if attributes
23
40
  unrecognized = attributes - spec[:attributes]
@@ -32,11 +49,19 @@ module Sevgi
32
49
 
33
50
  @cache = {}
34
51
 
52
+ # Validates one SVG element usage, using cached element validators.
53
+ # @param element [Symbol] SVG element name
54
+ # @param attributes [Array<Symbol>, nil] attribute names used by the element
55
+ # @param cdata [String, nil] character data content
56
+ # @param elements [Array<Symbol>, nil] child element names
57
+ # @return [Boolean] true when the usage conforms or the element is ignored
58
+ # @raise [Sevgi::ValidationError] when the usage violates the standard data
59
+ # @raise [Sevgi::PanicError] when the standard data refers to an invalid model
35
60
  def self.call(element, attributes: nil, cdata: nil, elements: nil)
36
61
  Element.ignore?(element) or
37
62
  (@cache[element] ||= new(element)).call(
38
63
  attributes: Attribute.concerns(attributes),
39
- elements: Element.concerns(elements),
64
+ elements: Element.concerns(elements || []),
40
65
  cdata:
41
66
  )
42
67
  end
@@ -1,9 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Recognized color keyword names: https://www.w3.org/TR/2003/REC-SVG11-20030114/types.html#ColorKeywords
4
-
5
3
  module Sevgi
6
4
  module Standard
5
+ # Recognized SVG color keyword names mapped to hexadecimal RGB values.
7
6
  Color = {
8
7
  aliceblue: "#F0F8FF",
9
8
  antiquewhite: "#FAEBD7",
@@ -154,7 +153,7 @@ module Sevgi
154
153
  yellowgreen: "#9ACD32"
155
154
  }
156
155
  .tap do |color|
157
- def color.valid?(name) = key?(name.to_sym)
156
+ def color.valid?(name) = name.respond_to?(:to_sym) && key?(name.to_sym)
158
157
 
159
158
  color.each { |name, hex| color.define_singleton_method(name) { hex } }
160
159
  end
@@ -4,19 +4,45 @@ require_relative "data/color"
4
4
 
5
5
  module Sevgi
6
6
  module Standard
7
+ # Shared set operations for SVG standard data lists.
8
+ # @api private
7
9
  module Common
10
+ # Returns every known name in this data list.
11
+ # @return [Set<Symbol>]
8
12
  def all = @all ||= Set[*data.values.flatten.uniq.sort]
9
13
 
14
+ # Checks whether a name belongs to a group.
15
+ # @param name [Symbol] item name
16
+ # @param group [Symbol] group name
17
+ # @return [Boolean]
10
18
  def is?(name, group) = self[group].include?(name)
11
19
 
20
+ # Keeps names that belong to any requested group.
21
+ # @param names [Array<Symbol>] names to filter
22
+ # @param groups [Array<Symbol>] group names
23
+ # @return [Array<Symbol>] filtered names
12
24
  def pick(names, *groups) = names.select { |name| groups.any? { is?(name, it) } }
13
25
 
26
+ # Returns all names or names from selected groups.
27
+ # @param groups [Array<Symbol>] group names
28
+ # @return [Set<Symbol>] selected names
14
29
  def set(*groups) = groups.empty? ? all : Set[*data.values_at(*groups).flatten.compact.uniq.sort]
15
30
 
31
+ # Removes names that belong to any requested group.
32
+ # @param names [Array<Symbol>] names to filter
33
+ # @param groups [Array<Symbol>] group names
34
+ # @return [Array<Symbol>] filtered names
16
35
  def unpick(names, *groups) = names.reject { |name| groups.any? { is?(name, it) } }
17
36
 
37
+ # Removes ignored names from a list.
38
+ # @param names [Array<Symbol>, nil] names to filter
39
+ # @return [Array<Symbol>, nil] names that should be validated
18
40
  def concerns(names) = names ? names.reject { ignore?(it) } : names
19
41
 
42
+ # Installs low-level list helpers into data modules.
43
+ # @param base [Module] data module receiving helpers
44
+ # @return [void]
45
+ # @api private
20
46
  def self.extended(base) = base.extend(List)
21
47
  end
22
48
 
@@ -46,11 +72,21 @@ module Sevgi
46
72
 
47
73
  require_relative "data/specification"
48
74
 
75
+ # Returns expanded standard specification data for one element.
76
+ # @param name [Symbol] SVG element name
77
+ # @return [Hash, nil] expanded specification data
49
78
  def [](name) = expand(name)
50
79
 
80
+ # Reports whether a name is a data group name.
81
+ # @param name [Symbol] element or group name
82
+ # @return [Boolean]
51
83
  def group?(name) = /[[:upper:]]/.match?(name[0])
52
84
 
53
- def model?(name, *models) = models.any? { (self[name] || {})[:model] == name }
85
+ # Checks whether an element uses one of the requested content models.
86
+ # @param name [Symbol] SVG element name
87
+ # @param models [Array<Symbol>] model names to match
88
+ # @return [Boolean]
89
+ def model?(name, *models) = models.any? { (self[name] || {})[:model] == it }
54
90
 
55
91
  private
56
92
 
@@ -58,7 +94,7 @@ module Sevgi
58
94
 
59
95
  def expand(name)
60
96
  @spec[name] ||= if data[name]
61
- data[name].dup.tap do |spec|
97
+ data[name].transform_values { |value| value.is_a?(::Array) ? value.dup : value }.tap do |spec|
62
98
  expand_names(spec[:elements], Element.data)
63
99
  expand_names(spec[:attributes], Attribute.data)
64
100
  end
@@ -1,7 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sevgi
4
+ # Base class for SVG standard validation errors.
4
5
  class ValidationError < Error
6
+ # Builds a validation error with a context label and offending values.
7
+ # @param context [Object] validation context included in the error message
8
+ # @param args [Array<Object>, Object] invalid values included in the error message
9
+ # @return [void]
5
10
  def initialize(context, args = [])
6
11
  @context = context
7
12
  @args = Array(args)
@@ -9,8 +14,18 @@ module Sevgi
9
14
  super()
10
15
  end
11
16
 
12
- def self.call(context, args = []) = raise new(context, args)
17
+ # Raises this validation error class.
18
+ # @param context [Object] validation context included in the error message
19
+ # @param args [Array<Object>, Object] invalid values included in the error message
20
+ # @return [void]
21
+ # @raise [Sevgi::ValidationError] always raises an instance of this class
22
+ def self.call(context, args = [])
23
+ raise new(context, args)
24
+ end
13
25
 
26
+ # Builds a validation error subclass with a fixed message fragment.
27
+ # @param message [String] message fragment placed after the validation context
28
+ # @return [Class<Sevgi::ValidationError>] validation error subclass
14
29
  def self.variant(message)
15
30
  Class.new(self) do
16
31
  define_method(:message) do
@@ -24,9 +39,14 @@ module Sevgi
24
39
  end
25
40
  end
26
41
 
42
+ # Raised when an element contains invalid attributes.
27
43
  InvalidAttributesError = ValidationError.variant("Invalid attribute(s)")
44
+ # Raised when an element contains invalid child elements.
28
45
  InvalidElementsError = ValidationError.variant("Invalid element(s)")
46
+ # Raised when character data appears where the SVG model forbids it.
29
47
  UnallowedCDataError = ValidationError.variant("Character data not allowed")
48
+ # Raised when an element contains disallowed child elements.
30
49
  UnallowedElementsError = ValidationError.variant("Element(s) not allowed")
50
+ # Raised when a conditional SVG content model requirement is not met.
31
51
  UnmetConditionError = ValidationError.variant("Condition unmet for the element")
32
52
  end
@@ -2,13 +2,28 @@
2
2
 
3
3
  module Sevgi
4
4
  module Standard
5
+ # Low-level import and lookup helper for static SVG standard data.
6
+ # @api private
5
7
  module List
8
+ # Looks up a data entry.
9
+ # @param name [Symbol] data key
10
+ # @return [Object, nil] stored data value
6
11
  def [](name) = data[name]
7
12
 
13
+ # Imports new data entries without overwriting existing keys.
14
+ # @param kwargs [Hash] entries to import
15
+ # @return [Hash] backing data hash
8
16
  def import(**kwargs) = data.merge!(kwargs.reject { |key, _| data.key?(key) })
9
17
 
18
+ # Reports whether a data entry exists.
19
+ # @param name [Symbol] data key
20
+ # @return [Boolean]
10
21
  def valid?(name) = data.key?(name)
11
22
 
23
+ # Initializes the backing data hash on extended modules.
24
+ # @param base [Module] module receiving list behavior
25
+ # @return [void]
26
+ # @api private
12
27
  def self.extended(base)
13
28
  super
14
29
 
@@ -2,8 +2,16 @@
2
2
 
3
3
  module Sevgi
4
4
  module Standard
5
+ # SVG content model validators used by {Conform}.
6
+ # @api private
5
7
  module Model
8
+ # Validates elements that allow only character data.
6
9
  module CDataOnly
10
+ # Applies this content model.
11
+ # @param cdata [String, nil] character data content
12
+ # @param elements [Array<Symbol>] child element names
13
+ # @return [nil]
14
+ # @raise [Sevgi::UnallowedElementsError] when child elements are present
7
15
  def apply(cdata:, elements:)
8
16
  _cdata = cdata
9
17
 
@@ -11,7 +19,13 @@ module Sevgi
11
19
  end
12
20
  end
13
21
 
22
+ # Validates elements that allow character data or selected child elements.
14
23
  module CDataOrSomeElements
24
+ # Applies this content model.
25
+ # @param cdata [String, nil] character data content
26
+ # @param elements [Array<Symbol>] child element names
27
+ # @return [nil]
28
+ # @raise [Sevgi::UnallowedElementsError] when unallowed child elements are present
15
29
  def apply(cdata:, elements:)
16
30
  _cdata = cdata
17
31
 
@@ -20,7 +34,14 @@ module Sevgi
20
34
  end
21
35
  end
22
36
 
37
+ # Validates empty elements.
23
38
  module NoneElements
39
+ # Applies this content model.
40
+ # @param cdata [String, nil] character data content
41
+ # @param elements [Array<Symbol>] child element names
42
+ # @return [nil]
43
+ # @raise [Sevgi::UnallowedCDataError] when character data is present
44
+ # @raise [Sevgi::UnallowedElementsError] when child elements are present
24
45
  def apply(cdata:, elements:)
25
46
  UnallowedCDataError.(element, cdata) if cdata
26
47
 
@@ -28,7 +49,14 @@ module Sevgi
28
49
  end
29
50
  end
30
51
 
52
+ # Validates elements that allow selected child elements but no character data.
31
53
  module SomeElements
54
+ # Applies this content model.
55
+ # @param cdata [String, nil] character data content
56
+ # @param elements [Array<Symbol>] child element names
57
+ # @return [nil]
58
+ # @raise [Sevgi::UnallowedCDataError] when character data is present
59
+ # @raise [Sevgi::UnallowedElementsError] when unallowed child elements are present
32
60
  def apply(cdata:, elements:)
33
61
  UnallowedCDataError.(element, cdata) if cdata
34
62
 
@@ -37,8 +65,15 @@ module Sevgi
37
65
  end
38
66
  end
39
67
 
68
+ # Validates the special `feDiffuseLighting` content model.
40
69
  module SpecialFeDiffuseLighting
41
- # Any number of Descriptive elements and exactly one FilterLightSource element, in any order.
70
+ # Applies this content model.
71
+ # @param cdata [String, nil] character data content
72
+ # @param elements [Array<Symbol>] child element names
73
+ # @return [nil]
74
+ # @raise [Sevgi::UnallowedCDataError] when character data is present
75
+ # @raise [Sevgi::UnallowedElementsError] when unallowed child elements are present
76
+ # @raise [Sevgi::UnmetConditionError] when required light source elements are absent
42
77
  def apply(cdata:, elements:)
43
78
  UnallowedCDataError.(element, cdata) if cdata
44
79
 
@@ -52,8 +87,15 @@ module Sevgi
52
87
  end
53
88
  end
54
89
 
90
+ # Validates the special `feSpecularLighting` content model.
55
91
  module SpecialFeSpecularLighting
56
- # Exactly one FilterLightSource element first and any number of Descriptive elements in any order.
92
+ # Applies this content model.
93
+ # @param cdata [String, nil] character data content
94
+ # @param elements [Array<Symbol>] child element names
95
+ # @return [nil]
96
+ # @raise [Sevgi::UnallowedCDataError] when character data is present
97
+ # @raise [Sevgi::UnallowedElementsError] when unallowed child elements are present
98
+ # @raise [Sevgi::UnmetConditionError] when the first child is not a light source
57
99
  def apply(cdata:, elements:)
58
100
  UnallowedCDataError.(element, cdata) if cdata
59
101
 
@@ -67,8 +109,15 @@ module Sevgi
67
109
  end
68
110
  end
69
111
 
112
+ # Validates the special `font-face` content model.
70
113
  module SpecialFontFace
71
- # Any number of Descriptive elements and at most one font-face element in any order.
114
+ # Applies this content model.
115
+ # @param cdata [String, nil] character data content
116
+ # @param elements [Array<Symbol>] child element names
117
+ # @return [nil]
118
+ # @raise [Sevgi::UnallowedCDataError] when character data is present
119
+ # @raise [Sevgi::UnallowedElementsError] when unallowed child elements are present
120
+ # @raise [Sevgi::UnmetConditionError] when more than one `font-face` child is present
72
121
  def apply(cdata:, elements:)
73
122
  UnallowedCDataError.(element, cdata) if cdata
74
123
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Sevgi
4
4
  module Standard
5
- VERSION = "0.73.2"
5
+ # Current version of the Sevgi standard gem.
6
+ VERSION = "0.93.1"
6
7
  end
7
8
  end
@@ -11,21 +11,55 @@ require_relative "standard/conform"
11
11
  require_relative "standard/version"
12
12
 
13
13
  module Sevgi
14
+ # SVG standard data and validation helpers.
14
15
  module Standard
15
16
  extend self
16
17
 
18
+ # @overload attributes(*groups)
19
+ # Returns SVG attributes, optionally restricted to one or more attribute groups.
20
+ # @param groups [Array<Symbol>] attribute group names
21
+ # @return [Set<Symbol>] attribute names
17
22
  def attributes(...) = Attribute.set(...)
18
23
 
19
- def attribute?(name) = Attribute.all.include?(name.to_sym)
24
+ # Reports whether an attribute name is recognized by the SVG standard data.
25
+ # @param name [Object] attribute name
26
+ # @return [Boolean]
27
+ def attribute?(name) = name.respond_to?(:to_sym) && Attribute.all.include?(name.to_sym)
20
28
 
29
+ # @overload conform(element, attributes: nil, cdata: nil, elements: nil)
30
+ # Validates an SVG element usage against the standard data.
31
+ # @param element [Symbol] SVG element name
32
+ # @param attributes [Array<Symbol>, nil] attribute names used by the element
33
+ # @param cdata [String, nil] character data content
34
+ # @param elements [Array<Symbol>, nil] child element names
35
+ # @return [Boolean] true when the usage conforms
36
+ # @raise [Sevgi::ValidationError] when the usage violates the standard data
37
+ # @raise [Sevgi::PanicError] when the standard data refers to an invalid model
21
38
  def conform(...) = Conform.(...)
22
39
 
40
+ # @overload elements(*groups)
41
+ # Returns SVG elements, optionally restricted to one or more element groups.
42
+ # @param groups [Array<Symbol>] element group names
43
+ # @return [Set<Symbol>] element names
23
44
  def elements(...) = Element.set(...)
24
45
 
25
- def element?(name) = Element.all.include?(name.to_sym)
46
+ # Reports whether an element name is recognized by the SVG standard data.
47
+ # @param name [Object] element name
48
+ # @return [Boolean]
49
+ def element?(name) = name.respond_to?(:to_sym) && Element.all.include?(name.to_sym)
26
50
 
51
+ # @overload model?(name, *models)
52
+ # Checks whether an element uses one of the requested content models.
53
+ # @param name [Symbol] SVG element name
54
+ # @param models [Array<Symbol>] model names to match
55
+ # @return [Boolean]
27
56
  def model?(...) = Specification.model?(...)
28
57
 
29
- def [](name) = Specification[name.to_sym]
58
+ # Returns the expanded standard contract for an SVG element.
59
+ # @param name [Object] SVG element name
60
+ # @return [Hash, nil] expanded specification data, or nil when name is invalid or unknown
61
+ def specification(name) = name.respond_to?(:to_sym) ? Specification[name.to_sym] : nil
62
+
63
+ alias [] specification
30
64
  end
31
65
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sevgi-standard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.73.2
4
+ version: 0.93.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Recai Oktaş
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.73.2
18
+ version: 0.93.1
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.73.2
25
+ version: 0.93.1
26
26
  description: Validates elements and attributes according to the SVG specification.
27
27
  email: roktas@gmail.com
28
28
  executables: []