Module: Sevgi::Graphics::Mixtures::Core

Extended by:
Forwardable
Defined in:
lib/sevgi/graphics/mixtures/core.rb

Overview

Core SVG tree and attribute DSL helpers.

Instance Method Summary collapse

Instance Method Details

#<<(element) ⇒ Sevgi::Graphics::Element

Appends an element as a child.

Parameters:

Returns:



233
234
235
# File 'lib/sevgi/graphics/mixtures/core.rb', line 233

def <<(element)
  Append(element)
end

#Adopt(new_parent = nil, index: -1)) ⇒ Sevgi::Graphics::Element

Moves this element under a new parent.

Parameters:

  • new_parent (Sevgi::Graphics::Element, nil) (defaults to: nil)

    target parent or current parent

  • index (Integer) (defaults to: -1))

    insertion index

Returns:

Raises:

  • (Sevgi::ArgumentError)

    when the target parent has a different element class

  • (Sevgi::ArgumentError)

    when the target parent is this element or one of its descendants



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/sevgi/graphics/mixtures/core.rb', line 56

def Adopt(new_parent = nil, index: -1)
  tap do
    if new_parent
      unless instance_of?(new_parent.class)
        ArgumentError.("Element type does not match the new parent type: #{self.class}")
      end
    else
      new_parent = parent
    end

    Adoption.validate(self, new_parent)

    self.Orphan()
    (@parent = new_parent).children.insert(index, self)
  end
end

#AdoptFirst(new_parent = nil) ⇒ Sevgi::Graphics::Element

Moves this element to the beginning of a parent.

Parameters:

Returns:

Raises:

  • (Sevgi::ArgumentError)

    when the target parent has a different element class

  • (Sevgi::ArgumentError)

    when the target parent is this element or one of its descendants



79
80
81
# File 'lib/sevgi/graphics/mixtures/core.rb', line 79

def AdoptFirst(*)
  Adopt(*, index: 0)
end

#Append(*elements) ⇒ Sevgi::Graphics::Element

Appends existing elements as children.

Parameters:

Returns:



86
87
88
# File 'lib/sevgi/graphics/mixtures/core.rb', line 86

def Append(*elements)
  tap { elements.each { it.Adopt(self) } }
end

#Classify(*classes) ⇒ Sevgi::Graphics::Element

Adds CSS classes without duplicating existing values.

Parameters:

  • classes (Array<String, Symbol, Array>)

    class tokens; strings are split on whitespace

Returns:



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/sevgi/graphics/mixtures/core.rb', line 93

def Classify(*classes)
  tap do
    tokens = proc do |value|
      case value
      when nil
        []
      when ::Array
        value.flat_map { tokens.call(it) }
      else
        value.to_s.split
      end
    end

    self[:class] = [*tokens.call(self[:class]), *tokens.call(classes)].uniq
  end
end

#Defaults(**attributes) ⇒ Sevgi::Graphics::Element

Assigns default attributes only when they are absent.

Parameters:

  • attributes (Hash)

    default attributes

Returns:



113
114
115
116
117
118
119
120
121
# File 'lib/sevgi/graphics/mixtures/core.rb', line 113

def Defaults(**attributes)
  tap do
    attributes.each do |key, value|
      next if has?(key)

      self[key] = value
    end
  end
end

#Element(tag, *contents, **attributes, &block) ⇒ Sevgi::Graphics::Element

Builds a child element with an explicit tag name.

Parameters:

  • tag (Symbol, String)

    SVG tag name

  • contents (Array<Object>)

    text or content objects; non-content objects are stringified and XML-encoded

  • attributes (Hash)

    SVG attributes

Returns:



128
129
130
# File 'lib/sevgi/graphics/mixtures/core.rb', line 128

def Element(tag, *contents, **attributes, &block)
  self.class.send(:new, tag.to_sym, contents: Content.contents(*contents), attributes:, parent: self, &block)
end

#Forward(receiver, method, *args, **kwargs) ⇒ Object

Forwards this element as the first argument to another receiver.

Returns forwarded call result.

Parameters:

  • receiver (Object)

    target receiver

  • method (Symbol, String)

    method name

  • args (Array<Object>)

    additional arguments

  • kwargs (Hash)

    additional keyword arguments

Returns:

  • (Object)

    forwarded call result



139
140
141
# File 'lib/sevgi/graphics/mixtures/core.rb', line 139

def Forward(receiver, method, ...)
  receiver.public_send(method, self, ...)
end

#Is?(name) ⇒ Boolean

Reports whether this element has the given SVG name.

Parameters:

  • name (Symbol, String)

    SVG name

Returns:

  • (Boolean)


146
147
148
# File 'lib/sevgi/graphics/mixtures/core.rb', line 146

def Is?(name)
  self.name() == name.to_sym
end

#OrphanArray<Sevgi::Graphics::Element>?

Removes this element from its parent.

Returns:



152
153
154
# File 'lib/sevgi/graphics/mixtures/core.rb', line 152

def Orphan
  parent.children&.delete(self) unless Root?()
end

#Prepend(*elements) ⇒ Sevgi::Graphics::Element

Prepends existing elements as children.

Parameters:

Returns:



159
160
161
# File 'lib/sevgi/graphics/mixtures/core.rb', line 159

def Prepend(*elements)
  tap { elements.each { it.AdoptFirst(self) } }
end

#RootSevgi::Graphics::Element

Returns the root document element.



165
166
167
168
169
170
# File 'lib/sevgi/graphics/mixtures/core.rb', line 165

def Root
  element = self
  element = element.parent until element.Root?()

  element
end

#Root?Boolean

Reports whether this element is the root document element.

Returns:

  • (Boolean)


174
175
176
# File 'lib/sevgi/graphics/mixtures/core.rb', line 174

def Root?
  self.class.root?(self)
end

#Stay(value) ⇒ Sevgi::Graphics::Mixtures::Stop

Wraps a traversal return value as a stop token.

Parameters:

  • value (Object)

    value returned from traversal

Returns:



182
# File 'lib/sevgi/graphics/mixtures/core.rb', line 182

def Stay(...) = Stop.new(...)

#Traverse(depth = 0, leave = nil, &block) ⇒ Sevgi::Graphics::Element, Object

Traverses the subtree depth-first.

Parameters:

  • depth (Integer) (defaults to: 0)

    starting depth

  • leave (Proc, nil) (defaults to: nil)

    optional leave callback

Returns:

Raises:

  • (Sevgi::ArgumentError)

    when no block is given



189
190
191
192
193
# File 'lib/sevgi/graphics/mixtures/core.rb', line 189

def Traverse(depth = 0, leave = nil, &block)
  ArgumentError.("Block required") unless block

  Traversal.call(self, depth, leave, &block)
end

#TraverseUp(height = 0, &block) ⇒ Object?

Traverses ancestors from this element to the root.

Parameters:

  • height (Integer) (defaults to: 0)

    starting height

Returns:

  • (Object, nil)

    value passed through Stay, or nil

Raises:

  • (Sevgi::ArgumentError)

    when no block is given



199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/sevgi/graphics/mixtures/core.rb', line 199

def TraverseUp(height = 0, &block)
  ArgumentError.("Block required") unless block

  element = self

  loop do
    yield(element, height).tap { return it.value if it.is_a?(Stop) }

    break if element.Root?()

    element = element.parent
    height += 1
  end
end

#With(*args, **kwargs, &block) ⇒ Sevgi::Graphics::Element

Evaluates a block in the parent element context.

Parameters:

  • args (Array<Object>)

    optional receiver override followed by block arguments

  • kwargs (Hash)

    keyword arguments passed to the block

Returns:



218
219
220
# File 'lib/sevgi/graphics/mixtures/core.rb', line 218

def With(*args, **kwargs, &block)
  tap { (args.shift || self).parent.instance_exec(*args, **kwargs, &block) }
end

#Within(*args, **kwargs, &block) ⇒ Sevgi::Graphics::Element

Evaluates a block in this element context.

Parameters:

  • args (Array<Object>)

    optional receiver override followed by block arguments

  • kwargs (Hash)

    keyword arguments passed to the block

Returns:



226
227
228
# File 'lib/sevgi/graphics/mixtures/core.rb', line 226

def Within(*args, **kwargs, &block)
  tap { (args.shift || self).instance_exec(*args, **kwargs, &block) }
end