Class: Sevgi::Graphics::Element
- Inherits:
-
Object
- Object
- Sevgi::Graphics::Element
- Extended by:
- Ident
- Defined in:
- lib/sevgi/graphics/element.rb
Overview
SVG element node used by the graphics DSL.
Direct Known Subclasses
Defined Under Namespace
Modules: Ident
Constant Summary collapse
- RootParent =
Sentinel parent used by root SVG elements.
Object.new.tap { def it.inspect = "RootParent" }.freeze
Instance Attribute Summary collapse
-
#attributes ⇒ Sevgi::Graphics::Attributes
readonly
Returns the attribute store.
-
#children ⇒ Array<Sevgi::Graphics::Element>
readonly
Returns child elements.
-
#contents ⇒ Array<Sevgi::Graphics::Content>
readonly
Returns element content objects.
-
#name ⇒ Symbol
readonly
Returns the SVG element name.
-
#parent ⇒ Sevgi::Graphics::Element, Object
readonly
Returns the parent element or root sentinel.
Class Method Summary collapse
-
.element(name, parent:) { ... } ⇒ Sevgi::Graphics::Element
Builds an element node.
-
.root { ... } ⇒ Sevgi::Graphics::Element
Builds an SVG root element.
-
.root?(element) ⇒ Boolean
Reports whether an element is the root element.
-
.valid?(name) ⇒ Boolean
Reports whether an SVG element name is known.
Instance Method Summary collapse
-
#initialize(name, parent:, attributes: {}, contents: []) { ... } ⇒ void
constructor
Creates an element.
-
#method_missing(name) { ... } ⇒ Sevgi::Graphics::Element
Dispatches SVG element DSL calls and caches valid element methods.
-
#respond_to_missing?(name, include_private = false) ⇒ Boolean
private
Reports whether a missing method can dispatch to an SVG element.
Methods included from Ident
Constructor Details
#initialize(name, parent:, attributes: {}, contents: []) { ... } ⇒ void
Creates an element.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/sevgi/graphics/element.rb', line 94 def initialize(name, parent:, attributes: {}, contents: [], &block) unless name.is_a?(::String) || name.is_a?(::Symbol) ArgumentError.("XML element name must be a String or Symbol") end @name = XML.name(name, context: "XML element name").to_sym @attributes = Attributes.new(attributes) @children = [] @contents = contents @parent = parent parent.children << self unless self.class.root?(self) instance_exec(&block) if block end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name) { ... } ⇒ Sevgi::Graphics::Element
Dispatches SVG element DSL calls and caches valid element methods.
117 118 119 |
# File 'lib/sevgi/graphics/element.rb', line 117 def method_missing(name, *, &block) Element.valid?(tag = Element.id(name)) ? Dispatch.(self, name, tag, *, &block) : super end |
Instance Attribute Details
#attributes ⇒ Sevgi::Graphics::Attributes
Returns the attribute store.
71 72 73 |
# File 'lib/sevgi/graphics/element.rb', line 71 def attributes @attributes end |
#children ⇒ Array<Sevgi::Graphics::Element>
Returns child elements.
75 76 77 |
# File 'lib/sevgi/graphics/element.rb', line 75 def children @children end |
#contents ⇒ Array<Sevgi::Graphics::Content>
Returns element content objects.
79 80 81 |
# File 'lib/sevgi/graphics/element.rb', line 79 def contents @contents end |
#name ⇒ Symbol (readonly)
Returns the SVG element name.
67 68 69 |
# File 'lib/sevgi/graphics/element.rb', line 67 def name @name end |
#parent ⇒ Sevgi::Graphics::Element, Object
Returns the parent element or root sentinel.
83 84 85 |
# File 'lib/sevgi/graphics/element.rb', line 83 def parent @parent end |
Class Method Details
.element(name, parent:) { ... } ⇒ Sevgi::Graphics::Element
Builds an element node.
20 |
# File 'lib/sevgi/graphics/element.rb', line 20 def self.element(name, *, parent:, &block) = new(name, **Dispatch.parse(name, *), parent:, &block) |
.root { ... } ⇒ Sevgi::Graphics::Element
Builds an SVG root element.
27 |
# File 'lib/sevgi/graphics/element.rb', line 27 def self.root(*, &block) = element(:svg, *, parent: RootParent, &block) |
.root?(element) ⇒ Boolean
Reports whether an element is the root element.
32 |
# File 'lib/sevgi/graphics/element.rb', line 32 def self.root?(element) = element.parent == RootParent |
.valid?(name) ⇒ Boolean
Reports whether an SVG element name is known.
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/sevgi/graphics/element.rb', line 11 class Element # Builds an element node. # @param name [Symbol, String] SVG element name # @param parent [Sevgi::Graphics::Element] parent element # @yield evaluates the drawing DSL in the new element # @yieldreturn [Object] ignored block result # @return [Sevgi::Graphics::Element] # @raise [Sevgi::ArgumentError] when an argument cannot be parsed as attributes or content # @raise [Sevgi::ArgumentError] when the element name or an attribute is not valid XML def self.element(name, *, parent:, &block) = new(name, **Dispatch.parse(name, *), parent:, &block) # Builds an SVG root element. # @yield evaluates the drawing DSL in the root element # @yieldreturn [Object] ignored block result # @return [Sevgi::Graphics::Element] # @raise [Sevgi::ArgumentError] when a root attribute is not valid XML def self.root(*, &block) = element(:svg, *, parent: RootParent, &block) # Reports whether an element is the root element. # @param element [Sevgi::Graphics::Element] element to test # @return [Boolean] def self.root?(element) = element.parent == RootParent class << self require "sevgi/standard" # Reports whether an SVG element name is known. # @param name [Symbol] SVG element name # @return [Boolean] def valid?(name) = Standard.element?(name) rescue ::LoadError => e raise unless e.path == "sevgi/standard" # Reports whether an SVG element name is known. # @return [Boolean] def valid?(...) = true end private_class_method :new # Sentinel parent used by root SVG elements. RootParent = Object.new.tap { def it.inspect = "RootParent" }.freeze # SVG element method-name normalization. # @api private module Ident # Normalizes a Ruby method name into an SVG element id. # @param given [Symbol, String] method name # @return [Symbol] def id(given) = (@id ||= {})[given] ||= given.to_s.tr("_", "-").to_sym end extend Ident # Returns the SVG element name. # @return [Symbol] attr_reader :name # Returns the attribute store. # @return [Sevgi::Graphics::Attributes] attr_reader :attributes # Returns child elements. # @return [Array<Sevgi::Graphics::Element>] attr_reader :children # Returns element content objects. # @return [Array<Sevgi::Graphics::Content>] attr_reader :contents # Returns the parent element or root sentinel. # @return [Sevgi::Graphics::Element, Object] parent element or root sentinel attr_reader :parent # Creates an element. # @param name [Symbol] SVG element name # @param parent [Sevgi::Graphics::Element, Object] parent element or root sentinel # @param attributes [Hash] SVG attributes # @param contents [Array<Sevgi::Graphics::Content>] content objects # @yield evaluates the drawing DSL in the new element # @yieldreturn [Object] ignored block result # @return [void] # @raise [Sevgi::ArgumentError] when the element name or an attribute is not valid XML def initialize(name, parent:, attributes: {}, contents: [], &block) unless name.is_a?(::String) || name.is_a?(::Symbol) ArgumentError.("XML element name must be a String or Symbol") end @name = XML.name(name, context: "XML element name").to_sym @attributes = Attributes.new(attributes) @children = [] @contents = contents @parent = parent parent.children << self unless self.class.root?(self) instance_exec(&block) if block end # Dispatches SVG element DSL calls and caches valid element methods. # @param name [Symbol] missing method name # @yield evaluates the drawing DSL in the dispatched child element # @yieldreturn [Object] ignored block result # @return [Sevgi::Graphics::Element] # @raise [NameError] when the name is not a valid SVG element # @raise [Sevgi::ArgumentError] when an argument cannot be parsed as attributes or content def method_missing(name, *, &block) Element.valid?(tag = Element.id(name)) ? Dispatch.(self, name, tag, *, &block) : super end # Reports whether a missing method can dispatch to an SVG element. # @param name [Symbol] queried method name # @param include_private [Boolean] standard Ruby respond_to? flag # @return [Boolean] # @api private def respond_to_missing?(name, include_private = false) Element.valid?(Element.id(name)) || super end # Element method-missing parser and cache. # @api private module Dispatch extend self # Dispatches an element DSL call. # @param element [Sevgi::Graphics::Element] parent element # @param method [Symbol] Ruby method name # @param tag [Symbol] SVG element name # @return [Sevgi::Graphics::Element] def call(element, method, tag, *, &) unless Element.method_defined?(method) Element.class_exec do define_method(method) { |*args, &block| self.class.element(tag, *args, parent: self, &block) } end end element.public_send(method, *, &) end # Parses element DSL arguments. # @param name [Symbol] SVG element name # @param args [Array<Object>] positional DSL arguments # @return [Hash] parsed :attributes and :contents # @raise [Sevgi::ArgumentError] when an argument is not a Hash, String, or Content def parse(name, *args) attributes, contents = {}, [] args.each do |arg| case arg when ::Hash attributes = arg when ::String contents << Content.encoded(arg) when Content contents << arg else ArgumentError.("Argument of element '#{name}' must be a Hash or String: #{arg}") end end {attributes:, contents:} end end private_constant :Dispatch protected attr_writer :attributes, :children, :contents, :parent end |
Instance Method Details
#respond_to_missing?(name, include_private = false) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Reports whether a missing method can dispatch to an SVG element.
126 127 128 |
# File 'lib/sevgi/graphics/element.rb', line 126 def respond_to_missing?(name, include_private = false) Element.valid?(Element.id(name)) || super end |