Class: Sashite::Qpi::Identifier
- Inherits:
-
Object
- Object
- Sashite::Qpi::Identifier
- Defined in:
- lib/sashite/qpi/identifier.rb
Overview
Represents an identifier in QPI (Qualified Piece Identifier) format.
QPI is pure composition of SIN and PIN primitives with one constraint: both components must represent the same player (side).
## Minimal API Design
The Identifier class provides only 5 core methods:
-
new(sin, pin) — create from components with validation
-
sin — access SIN component
-
pin — access PIN component
-
to_s — serialize to QPI string
-
flip — flip both components (only convenience method)
Additionally, component replacement methods:
-
with_sin(new_sin) — create identifier with different SIN
-
with_pin(new_pin) — create identifier with different PIN
All other operations use the component APIs directly:
-
qpi.sin.family — access Piece Style
-
qpi.sin.side — access Piece Side
-
qpi.pin.type — access Piece Name
-
qpi.pin.state — access Piece State
-
qpi.pin.terminal? — access Terminal Status
## Why Only flip as Convenience?
flip is the ONLY transformation that naturally operates on both SIN and PIN components simultaneously. All other transformations work through component replacement:
qpi.with_sin(qpi.sin.with_family(:S)) # Transform SIN
qpi.with_pin(qpi.pin.with_type(:Q)) # Transform PIN
qpi.with_pin(qpi.pin.with_terminal(true)) # Transform PIN
This avoids arbitrary conveniences and maintains a clear principle.
Constant Summary collapse
- SEPARATOR =
Component separator for string representation
":"- ERROR_INVALID_QPI =
Error messages
"Invalid QPI string: %s"- ERROR_SEMANTIC_MISMATCH =
"SIN and PIN components must have same side: sin.side=%s, pin.side=%s"- ERROR_MISSING_SEPARATOR =
"QPI string must contain exactly one colon separator: %s"
Instance Attribute Summary collapse
-
#pin ⇒ Pin::Identifier
readonly
The PIN component.
-
#sin ⇒ Sin::Identifier
readonly
The SIN component.
Class Method Summary collapse
-
.parse(qpi_string) ⇒ Identifier
Parse a QPI string into an Identifier object.
-
.valid?(qpi_string) ⇒ Boolean
Check if a string is a valid QPI notation.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
(also: #eql?)
Custom equality comparison.
-
#flip ⇒ Identifier
Create a new identifier with both components flipped.
-
#hash ⇒ Integer
Custom hash implementation for use in collections.
-
#initialize(sin, pin) ⇒ Identifier
constructor
Create a new identifier from SIN and PIN components.
-
#to_s ⇒ String
Convert the identifier to its QPI string representation.
-
#with_pin(new_pin) ⇒ Identifier
Create a new identifier with different PIN component.
-
#with_sin(new_sin) ⇒ Identifier
Create a new identifier with different SIN component.
Constructor Details
#initialize(sin, pin) ⇒ Identifier
Create a new identifier from SIN and PIN components
91 92 93 94 95 96 97 98 |
# File 'lib/sashite/qpi/identifier.rb', line 91 def initialize(sin, pin) validate_semantic_consistency(sin, pin) @sin = sin @pin = pin freeze end |
Instance Attribute Details
#pin ⇒ Pin::Identifier (readonly)
Returns the PIN component.
79 80 81 |
# File 'lib/sashite/qpi/identifier.rb', line 79 def pin @pin end |
#sin ⇒ Sin::Identifier (readonly)
Returns the SIN component.
76 77 78 |
# File 'lib/sashite/qpi/identifier.rb', line 76 def sin @sin end |
Class Method Details
.parse(qpi_string) ⇒ Identifier
Parse a QPI string into an Identifier object
110 111 112 113 114 115 116 117 118 |
# File 'lib/sashite/qpi/identifier.rb', line 110 def self.parse(qpi_string) string_value = String(qpi_string) sin_part, pin_part = split_components(string_value) sin_identifier = Sin::Identifier.parse(sin_part) pin_identifier = Pin::Identifier.parse(pin_part) new(sin_identifier, pin_identifier) end |
.valid?(qpi_string) ⇒ Boolean
Check if a string is a valid QPI notation
128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/sashite/qpi/identifier.rb', line 128 def self.valid?(qpi_string) return false unless qpi_string.is_a?(::String) sin_part, pin_part = split_components(qpi_string) return false unless Sashite::Sin.valid?(sin_part) && Sashite::Pin.valid?(pin_part) sin_identifier = Sashite::Sin.parse(sin_part) pin_identifier = Sashite::Pin.parse(pin_part) sin_identifier.side == pin_identifier.side rescue ArgumentError false end |
Instance Method Details
#==(other) ⇒ Boolean Also known as: eql?
Custom equality comparison
197 198 199 200 201 |
# File 'lib/sashite/qpi/identifier.rb', line 197 def ==(other) return false unless other.is_a?(self.class) @sin == other.sin && @pin == other.pin end |
#flip ⇒ Identifier
Create a new identifier with both components flipped
This is the ONLY convenience method because it’s the only transformation that naturally operates on both components.
189 190 191 |
# File 'lib/sashite/qpi/identifier.rb', line 189 def flip self.class.new(@sin.flip, @pin.flip) end |
#hash ⇒ Integer
Custom hash implementation for use in collections
209 210 211 |
# File 'lib/sashite/qpi/identifier.rb', line 209 def hash [self.class, @sin, @pin].hash end |
#to_s ⇒ String
Convert the identifier to its QPI string representation
147 148 149 |
# File 'lib/sashite/qpi/identifier.rb', line 147 def to_s "#{@sin}#{SEPARATOR}#{@pin}" end |
#with_pin(new_pin) ⇒ Identifier
Create a new identifier with different PIN component
173 174 175 176 177 |
# File 'lib/sashite/qpi/identifier.rb', line 173 def with_pin(new_pin) return self if @pin == new_pin self.class.new(@sin, new_pin) end |
#with_sin(new_sin) ⇒ Identifier
Create a new identifier with different SIN component
159 160 161 162 163 |
# File 'lib/sashite/qpi/identifier.rb', line 159 def with_sin(new_sin) return self if @sin == new_sin self.class.new(new_sin, @pin) end |