Class: AsciiChem::Model::Node
- Inherits:
-
Object
- Object
- AsciiChem::Model::Node
show all
- Defined in:
- lib/asciichem/model/node.rb
Overview
Base class for every model node. Provides:
- structural equality (class + declared attributes)
- visitor dispatch (`accept`)
- convenience formatter shortcuts (`to_mathml`, `to_text`, ...)
Subclasses declare their comparable attributes by overriding
value_attributes (returns a hash of { name => value }). This
avoids reflective instance-variable access and keeps equality
correct as fields are added.
Class Method Summary
collapse
-
.short_name ⇒ Object
Snake-case form of the class basename, used to derive the visitor method (Atom -> visit_atom, ElectronConfiguration -> visit_electron_configuration).
Instance Method Summary
collapse
Class Method Details
.short_name ⇒ Object
Snake-case form of the class basename, used to derive the visitor
method (Atom -> visit_atom, ElectronConfiguration ->
visit_electron_configuration). Memoised per class so the
string transformation runs once per class, not once per visit.
78
79
80
81
82
83
84
85
|
# File 'lib/asciichem/model/node.rb', line 78
def self.short_name
@short_name ||= begin
snake = name.split("::").last
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
snake.downcase
end
end
|
Instance Method Details
#==(other) ⇒ Object
Also known as:
eql?
24
25
26
|
# File 'lib/asciichem/model/node.rb', line 24
def ==(other)
other.is_a?(self.class) && value_attributes == other.value_attributes
end
|
#accept(visitor) ⇒ Object
15
16
17
18
19
20
21
22
|
# File 'lib/asciichem/model/node.rb', line 15
def accept(visitor)
visitor.public_send(:"visit_#{self.class.short_name}", self)
rescue NoMethodError => e
raise unless e.name == :"visit_#{self.class.short_name}"
raise NotImplementedError,
"#{visitor.class} does not implement visit_#{self.class.short_name}"
end
|
#children ⇒ Object
Child nodes for traversal. Default: no children. Container
classes (Formula, Molecule, Group, Reaction, ReactionCascade)
override to expose their contents; leaves (Atom, Bond,
EmbeddedMath, Text) inherit the empty default.
Used by Linter::Base#walk. Adding a new container class means
defining children on it — no edits to the linter.
70
71
72
|
# File 'lib/asciichem/model/node.rb', line 70
def children
[]
end
|
#diagnostic_label ⇒ Object
Human-readable label for linter diagnostics. Default: the
class basename with words separated by spaces
("Reaction Cascade", "Electron Configuration").
Subclasses with identifying fields override (e.g. Atom
includes its element symbol). Keeping this on the model keeps
the linter OCP-clean — adding a new model class means
overriding the method on that class, not editing
Linter::Diagnostic's case statement.
95
96
97
|
# File 'lib/asciichem/model/node.rb', line 95
def diagnostic_label
self.class.short_name.split("_").map(&:capitalize).join(" ")
end
|
#hash ⇒ Object
29
30
31
|
# File 'lib/asciichem/model/node.rb', line 29
def hash
[self.class, value_attributes].hash
end
|
#value_attributes ⇒ Object
Subclasses override to expose the attributes that participate in
equality. Default: empty (so two bare Nodes are equal).
59
60
61
|
# File 'lib/asciichem/model/node.rb', line 59
def value_attributes
{}
end
|