sashite-pin 4.0.0 → 4.2.0

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: 9d3f359a3d215523ac369b662e9a6d1a290acd6076db4f34829d0eb579ac8e2a
4
- data.tar.gz: a25dc1c9f2f47ed6121f82a839ee28d070c5a5c8bb11bd10abeb2e589d4dae30
3
+ metadata.gz: 654a422f7d1d03a9b2e474e6f29644c7d8636cc6a7da60100fc49efa87ccda02
4
+ data.tar.gz: c9548c761f14c4dc76acc1376fda12fa64339bbe7d6b2567b49f205113c89b59
5
5
  SHA512:
6
- metadata.gz: 79a1949901c57b7057498f1157a68cf71169a034f2645d6f69c9cb2d4a1272a422d54b794930013af7b0042277951dc32bffaa80a49abc88e7fe8ed5161a8644
7
- data.tar.gz: e475211bb7e79ac75df02bfcf58023354e602479e40b76ae453942ae5f3fe38a1af310f477db8ccbf57c070bd1e0d0dab2422c2eab9013e896b5f90f79d4b007
6
+ metadata.gz: fc265f0049d911133c3de8910ed15911a207fcec239aa97d8c4a9766242f2f5ca53d80802e36c9aafeac525eb79a01ef4f3625738f04622db97b210d8094296c
7
+ data.tar.gz: 5a5254e21d89dfec797ce72c62d1a65689c9db5460a91c13edba6360655483b7dd1c0a024c04ee35e5e35971c30e08518b540086112334da0a662d2ea1f3b432
data/LICENSE CHANGED
@@ -186,7 +186,7 @@
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright 2025 Cyril Kato
189
+ Copyright 2025-2026 Cyril Kato
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
data/README.md CHANGED
@@ -11,6 +11,18 @@
11
11
 
12
12
  This library implements the [PIN Specification v1.0.0](https://sashite.dev/specs/pin/1.0.0/).
13
13
 
14
+ PIN is a compact, ASCII-only token format encoding a **Piece Identity**: the tuple (**Piece Name**, **Piece Side**, **Piece State**, **Terminal Status**). Case encodes side, an optional `+`/`-` prefix encodes state, and an optional `^` suffix marks terminal pieces.
15
+
16
+ ### Implementation Constraints
17
+
18
+ | Constraint | Value | Rationale |
19
+ |------------|-------|-----------|
20
+ | Token length | 1–3 characters | `[+-]?[A-Za-z]\^?` per spec |
21
+ | Character space | 312 tokens | 26 abbreviations × 2 sides × 3 states × 2 terminal |
22
+ | Instance pool | 312 objects | All identifiers are pre-instantiated and frozen |
23
+
24
+ The closed domain of 312 possible values enables a flyweight architecture with zero allocation on the hot path.
25
+
14
26
  ## Installation
15
27
 
16
28
  ```ruby
@@ -35,7 +47,7 @@ require "sashite/pin"
35
47
 
36
48
  # Standard parsing (raises on error)
37
49
  pin = Sashite::Pin.parse("K")
38
- pin.type # => :K
50
+ pin.abbr # => :K
39
51
  pin.side # => :first
40
52
  pin.state # => :normal
41
53
  pin.terminal? # => false
@@ -53,57 +65,73 @@ pin = Sashite::Pin.parse("+K^")
53
65
  pin.state # => :enhanced
54
66
  pin.terminal? # => true
55
67
 
68
+ # Returns a cached instance — no allocation
69
+ Sashite::Pin.parse("+K^").equal?(Sashite::Pin.parse("+K^")) # => true
70
+
56
71
  # Invalid input raises ArgumentError
57
72
  Sashite::Pin.parse("invalid") # => raises ArgumentError
58
73
  ```
59
74
 
75
+ ### Safe Parsing (String → Identifier | nil)
76
+
77
+ Parse without raising exceptions. Returns `nil` on invalid input.
78
+
79
+ ```ruby
80
+ # Valid input returns an Identifier
81
+ Sashite::Pin.safe_parse("K") # => #<Sashite::Pin::Identifier K>
82
+ Sashite::Pin.safe_parse("+R^") # => #<Sashite::Pin::Identifier +R^>
83
+
84
+ # Invalid input returns nil — no exception allocated
85
+ Sashite::Pin.safe_parse("") # => nil
86
+ Sashite::Pin.safe_parse("invalid") # => nil
87
+ Sashite::Pin.safe_parse(nil) # => nil
88
+ ```
89
+
90
+ ### Fetching by Components (Symbol, Symbol, ... → Identifier)
91
+
92
+ Retrieve a cached identifier directly by components, bypassing string parsing entirely.
93
+
94
+ ```ruby
95
+ # Direct lookup — no string parsing, no allocation
96
+ Sashite::Pin.fetch(:K, :first) # => #<Sashite::Pin::Identifier K>
97
+ Sashite::Pin.fetch(:R, :second, :enhanced) # => #<Sashite::Pin::Identifier +r>
98
+ Sashite::Pin.fetch(:K, :first, :normal, terminal: true) # => #<Sashite::Pin::Identifier K^>
99
+
100
+ # Same cached instance as parse
101
+ Sashite::Pin.fetch(:K, :first).equal?(Sashite::Pin.parse("K")) # => true
102
+
103
+ # Invalid components raise ArgumentError
104
+ Sashite::Pin.fetch(:KK, :first) # => raises ArgumentError
105
+ Sashite::Pin.fetch(:K, :third) # => raises ArgumentError
106
+ ```
107
+
60
108
  ### Formatting (Identifier → String)
61
109
 
62
110
  Convert an `Identifier` back to a PIN string.
63
111
 
64
112
  ```ruby
65
- # From Identifier object
66
- pin = Sashite::Pin::Identifier.new(:K, :first)
67
- pin.to_s # => "K"
68
-
69
- # With attributes
70
- pin = Sashite::Pin::Identifier.new(:R, :second, :enhanced)
71
- pin.to_s # => "+r"
113
+ pin = Sashite::Pin.parse("+K^")
114
+ pin.to_s # => "+K^"
72
115
 
73
- pin = Sashite::Pin::Identifier.new(:K, :first, :normal, terminal: true)
74
- pin.to_s # => "K^"
116
+ pin = Sashite::Pin.parse("r")
117
+ pin.to_s # => "r"
75
118
  ```
76
119
 
77
120
  ### Validation
78
121
 
79
122
  ```ruby
80
- # Boolean check
123
+ # Boolean check (never raises)
124
+ # Uses an exception-free code path internally for performance.
81
125
  Sashite::Pin.valid?("K") # => true
82
126
  Sashite::Pin.valid?("+R") # => true
83
127
  Sashite::Pin.valid?("K^") # => true
84
128
  Sashite::Pin.valid?("invalid") # => false
85
- ```
86
-
87
- ### Accessing Identifier Data
88
-
89
- ```ruby
90
- pin = Sashite::Pin.parse("+K^")
91
-
92
- # Get attributes
93
- pin.type # => :K
94
- pin.side # => :first
95
- pin.state # => :enhanced
96
- pin.terminal? # => true
97
-
98
- # Get string components
99
- pin.letter # => "K"
100
- pin.prefix # => "+"
101
- pin.suffix # => "^"
129
+ Sashite::Pin.valid?(nil) # => false
102
130
  ```
103
131
 
104
132
  ### Transformations
105
133
 
106
- All transformations return new immutable instances.
134
+ All transformations return cached instances from the flyweight pool — no new object is ever allocated.
107
135
 
108
136
  ```ruby
109
137
  pin = Sashite::Pin.parse("K")
@@ -117,14 +145,17 @@ pin.normalize.to_s # => "K"
117
145
  pin.flip.to_s # => "k"
118
146
 
119
147
  # Terminal transformations
120
- pin.mark_terminal.to_s # => "K^"
121
- pin.unmark_terminal.to_s # => "K"
148
+ pin.terminal.to_s # => "K^"
149
+ pin.non_terminal.to_s # => "K"
122
150
 
123
151
  # Attribute changes
124
- pin.with_type(:Q).to_s # => "Q"
125
- pin.with_side(:second).to_s # => "k"
152
+ pin.with_abbr(:Q).to_s # => "Q"
153
+ pin.with_side(:second).to_s # => "k"
126
154
  pin.with_state(:enhanced).to_s # => "+K"
127
155
  pin.with_terminal(true).to_s # => "K^"
156
+
157
+ # Transformations return cached instances
158
+ pin.enhance.equal?(Sashite::Pin.parse("+K")) # => true
128
159
  ```
129
160
 
130
161
  ### Queries
@@ -146,7 +177,7 @@ pin.terminal? # => true
146
177
 
147
178
  # Comparison queries
148
179
  other = Sashite::Pin.parse("k")
149
- pin.same_type?(other) # => true
180
+ pin.same_abbr?(other) # => true
150
181
  pin.same_side?(other) # => false
151
182
  pin.same_state?(other) # => false
152
183
  pin.same_terminal?(other) # => false
@@ -154,27 +185,60 @@ pin.same_terminal?(other) # => false
154
185
 
155
186
  ## API Reference
156
187
 
157
- ### Types
188
+ ### Module Methods
189
+
190
+ ```ruby
191
+ # Parses a PIN string into a cached Identifier.
192
+ # Returns a pre-instantiated, frozen instance.
193
+ # Raises ArgumentError if the string is not valid.
194
+ #
195
+ # @param string [String] PIN string
196
+ # @return [Identifier]
197
+ # @raise [ArgumentError] if invalid
198
+ def Sashite::Pin.parse(string)
199
+
200
+ # Parses a PIN string without raising.
201
+ # Returns a cached Identifier on success, nil on failure.
202
+ # Never allocates exception objects or captures backtraces.
203
+ #
204
+ # @param string [String] PIN string
205
+ # @return [Identifier, nil]
206
+ def Sashite::Pin.safe_parse(string)
207
+
208
+ # Retrieves a cached Identifier by components.
209
+ # Bypasses string parsing entirely — direct hash lookup.
210
+ # Raises ArgumentError if components are invalid.
211
+ #
212
+ # @param abbr [Symbol] Piece abbreviation (:A through :Z)
213
+ # @param side [Symbol] Piece side (:first or :second)
214
+ # @param state [Symbol] Piece state (:normal, :enhanced, or :diminished)
215
+ # @param terminal [Boolean] Terminal status
216
+ # @return [Identifier]
217
+ # @raise [ArgumentError] if invalid
218
+ def Sashite::Pin.fetch(abbr, side, state = :normal, terminal: false)
219
+
220
+ # Reports whether string is a valid PIN.
221
+ # Never raises; returns false for any invalid input.
222
+ # Uses an exception-free code path internally for performance.
223
+ #
224
+ # @param string [String] PIN string
225
+ # @return [Boolean]
226
+ def Sashite::Pin.valid?(string)
227
+ ```
228
+
229
+ ### Identifier
158
230
 
159
231
  ```ruby
160
232
  # Identifier represents a parsed PIN with all attributes.
233
+ # All instances are frozen and pre-instantiated — never construct directly,
234
+ # use Sashite::Pin.parse, .safe_parse, or .fetch instead.
161
235
  class Sashite::Pin::Identifier
162
- # Creates an Identifier from attributes.
163
- # Raises ArgumentError if attributes are invalid.
236
+ # Returns the piece name abbreviation (always uppercase symbol).
164
237
  #
165
- # @param type [Symbol] Piece type (:A to :Z)
166
- # @param side [Symbol] Player side (:first or :second)
167
- # @param state [Symbol] Piece state (:normal, :enhanced, or :diminished)
168
- # @param terminal [Boolean] Terminal status
169
- # @return [Identifier]
170
- def initialize(type, side, state = :normal, terminal: false)
171
-
172
- # Returns the piece type (always uppercase symbol).
173
- #
174
- # @return [Symbol]
175
- def type
238
+ # @return [Symbol] :A through :Z
239
+ def abbr
176
240
 
177
- # Returns the player side.
241
+ # Returns the piece side.
178
242
  #
179
243
  # @return [Symbol] :first or :second
180
244
  def side
@@ -196,41 +260,12 @@ class Sashite::Pin::Identifier
196
260
  end
197
261
  ```
198
262
 
199
- ### Constants
200
-
201
- ```ruby
202
- Sashite::Pin::Constants::VALID_TYPES # => [:A, :B, ..., :Z]
203
- Sashite::Pin::Constants::VALID_SIDES # => [:first, :second]
204
- Sashite::Pin::Constants::VALID_STATES # => [:normal, :enhanced, :diminished]
205
- Sashite::Pin::Constants::MAX_STRING_LENGTH # => 3
206
- ```
207
-
208
- ### Parsing
209
-
210
- ```ruby
211
- # Parses a PIN string into an Identifier.
212
- # Raises ArgumentError if the string is not valid.
213
- #
214
- # @param string [String] PIN string
215
- # @return [Identifier]
216
- # @raise [ArgumentError] if invalid
217
- def Sashite::Pin.parse(string)
218
- ```
219
-
220
- ### Validation
221
-
222
- ```ruby
223
- # Reports whether string is a valid PIN.
224
- #
225
- # @param string [String] PIN string
226
- # @return [Boolean]
227
- def Sashite::Pin.valid?(string)
228
- ```
229
-
230
263
  ### Transformations
231
264
 
265
+ All transformations return cached `Sashite::Pin::Identifier` instances from the flyweight pool:
266
+
232
267
  ```ruby
233
- # State transformations (return new Identifier)
268
+ # State transformations
234
269
  def enhance # => Identifier with :enhanced state
235
270
  def diminish # => Identifier with :diminished state
236
271
  def normalize # => Identifier with :normal state
@@ -239,19 +274,50 @@ def normalize # => Identifier with :normal state
239
274
  def flip # => Identifier with opposite side
240
275
 
241
276
  # Terminal transformations
242
- def mark_terminal # => Identifier with terminal: true
243
- def unmark_terminal # => Identifier with terminal: false
277
+ def terminal # => Identifier with terminal: true
278
+ def non_terminal # => Identifier with terminal: false
244
279
 
245
280
  # Attribute changes
246
- def with_type(new_type) # => Identifier with different type
247
- def with_side(new_side) # => Identifier with different side
248
- def with_state(new_state) # => Identifier with different state
281
+ def with_abbr(new_abbr) # => Identifier with different abbreviation
282
+ def with_side(new_side) # => Identifier with different side
283
+ def with_state(new_state) # => Identifier with different state
249
284
  def with_terminal(new_terminal) # => Identifier with specified terminal status
250
285
  ```
251
286
 
287
+ ### Queries
288
+
289
+ ```ruby
290
+ # State queries
291
+ def normal? # => Boolean
292
+ def enhanced? # => Boolean
293
+ def diminished? # => Boolean
294
+
295
+ # Side queries
296
+ def first_player? # => Boolean
297
+ def second_player? # => Boolean
298
+
299
+ # Terminal query
300
+ def terminal? # => Boolean
301
+
302
+ # Comparison queries
303
+ def same_abbr?(other) # => Boolean
304
+ def same_side?(other) # => Boolean
305
+ def same_state?(other) # => Boolean
306
+ def same_terminal?(other) # => Boolean
307
+ ```
308
+
309
+ ### Constants
310
+
311
+ ```ruby
312
+ Sashite::Pin::Constants::VALID_ABBRS # => [:A, :B, ..., :Z]
313
+ Sashite::Pin::Constants::VALID_SIDES # => [:first, :second]
314
+ Sashite::Pin::Constants::VALID_STATES # => [:normal, :enhanced, :diminished]
315
+ Sashite::Pin::Constants::MAX_STRING_LENGTH # => 3
316
+ ```
317
+
252
318
  ### Errors
253
319
 
254
- All parsing and validation errors raise `ArgumentError` with descriptive messages:
320
+ All errors raise `ArgumentError` with descriptive messages:
255
321
 
256
322
  | Message | Cause |
257
323
  |---------|-------|
@@ -263,13 +329,30 @@ All parsing and validation errors raise `ArgumentError` with descriptive message
263
329
 
264
330
  ## Design Principles
265
331
 
266
- - **Bounded values**: Explicit validation of types, sides, states
267
- - **Object-oriented**: `Identifier` class enables methods and encapsulation
332
+ - **Spec conformance**: Strict adherence to PIN v1.0.0
333
+ - **Flyweight identifiers**: All 312 possible instances are pre-built and frozen; parsing, fetching, and transformations return cached objects with zero allocation
334
+ - **Performance-oriented internals**: Exception-free validation path; exceptions only at the public API boundary
268
335
  - **Ruby idioms**: `valid?` predicate, `to_s` conversion, `ArgumentError` for invalid input
269
- - **Immutable identifiers**: Frozen instances prevent mutation
270
- - **Transformation methods**: Return new instances for state changes
336
+ - **Immutable identifiers**: All instances are frozen after creation
271
337
  - **No dependencies**: Pure Ruby standard library only
272
338
 
339
+ ### Performance Architecture
340
+
341
+ PIN has a closed domain of exactly 312 valid tokens (26 letters × 2 cases × 3 states × 2 terminal). The implementation exploits this constraint through three complementary strategies.
342
+
343
+ **Flyweight instance pool** — All 312 `Identifier` objects are pre-instantiated and frozen at load time. `parse`, `safe_parse`, `fetch`, and all transformation methods return these cached instances via hash lookup. No `Identifier` is ever allocated after the module loads. This makes PIN essentially free to call from EPIN, FEEN, or any other hot loop — every call is a hash lookup returning a pre-existing frozen object.
344
+
345
+ **Dual-path parsing** — Parsing is split into two layers to avoid using exceptions for control flow:
346
+
347
+ - **Validation layer** — `safe_parse` performs all validation and returns the cached `Identifier` on success, or `nil` on failure, without raising, without allocating exception objects, and without capturing backtraces.
348
+ - **Public API layer** — `parse` calls `safe_parse` internally. On failure, it raises `ArgumentError` exactly once, at the boundary. `valid?` calls `safe_parse` and returns a boolean directly, never raising.
349
+
350
+ **Zero-allocation transformations** — Every transformation method (`flip`, `enhance`, `diminish`, `terminal`, `with_abbr`, etc.) computes the target component values and performs a direct lookup into the instance pool. The result is always a cached object — transformations never allocate. Chaining like `pin.enhance.flip.terminal` performs three hash lookups and zero allocations.
351
+
352
+ **Direct component lookup** — `fetch` bypasses string parsing entirely. Given components `(:K, :first, :enhanced, terminal: true)`, it performs a single hash lookup into the instance pool. This is the fastest path for callers that already have structured data (e.g., EPIN's internal construction from parsed components).
353
+
354
+ This architecture ensures that PIN never becomes a bottleneck when called from higher-level parsers like EPIN or FEEN, where it may be invoked hundreds of times per position.
355
+
273
356
  ## Related Specifications
274
357
 
275
358
  - [Game Protocol](https://sashite.dev/game-protocol/) — Conceptual foundation
@@ -2,12 +2,17 @@
2
2
 
3
3
  module Sashite
4
4
  module Pin
5
- # Constants for PIN (Piece Identifier Notation).
5
+ # Performance-oriented constants for PIN (Piece Identifier Notation).
6
6
  #
7
- # This module defines the valid values for PIN attributes.
7
+ # All lookups are O(1) frozen Hashes. Byte-level constants eliminate
8
+ # repeated method calls in the parser hot path.
8
9
  module Constants
9
- # Valid piece types (uppercase symbols A-Z).
10
- VALID_TYPES = %i[A B C D E F G H I J K L M N O P Q R S T U V W X Y Z].freeze
10
+ # ====================================================================
11
+ # Domain values
12
+ # ====================================================================
13
+
14
+ # Valid piece name abbreviations (uppercase symbols :A..:Z).
15
+ VALID_ABBRS = %i[A B C D E F G H I J K L M N O P Q R S T U V W X Y Z].freeze
11
16
 
12
17
  # Valid player sides.
13
18
  VALID_SIDES = %i[first second].freeze
@@ -15,21 +20,54 @@ module Sashite
15
20
  # Valid piece states.
16
21
  VALID_STATES = %i[normal enhanced diminished].freeze
17
22
 
18
- # Maximum length of a valid PIN string.
19
- # Corresponds to "+K^" (state modifier + letter + terminal marker).
20
- MAX_STRING_LENGTH = 3
23
+ # ====================================================================
24
+ # O(1) validation lookups (frozen Hashes faster than Array#include?)
25
+ # ====================================================================
26
+
27
+ # { :A => true, :B => true, … :Z => true }
28
+ ABBR_SET = VALID_ABBRS.each_with_object({}) { |a, h| h[a] = true }.freeze
29
+
30
+ # { :first => true, :second => true }
31
+ SIDE_SET = VALID_SIDES.each_with_object({}) { |s, h| h[s] = true }.freeze
21
32
 
22
- # State modifier for enhanced state.
23
- ENHANCED_PREFIX = "+"
33
+ # { :normal => true, :enhanced => true, :diminished => true }
34
+ STATE_SET = VALID_STATES.each_with_object({}) { |s, h| h[s] = true }.freeze
24
35
 
25
- # State modifier for diminished state.
36
+ # ====================================================================
37
+ # String fragments (frozen, reused by Identifier#to_s)
38
+ # ====================================================================
39
+
40
+ ENHANCED_PREFIX = "+"
26
41
  DIMINISHED_PREFIX = "-"
42
+ TERMINAL_SUFFIX = "^"
43
+ EMPTY_STRING = ""
44
+
45
+ # ====================================================================
46
+ # Byte constants (used by Parser for zero-allocation character checks)
47
+ # ====================================================================
48
+
49
+ # State modifier bytes
50
+ BYTE_PLUS = 0x2B # '+'
51
+ BYTE_MINUS = 0x2D # '-'
52
+
53
+ # Terminal marker byte
54
+ BYTE_CARET = 0x5E # '^'
55
+
56
+ # ASCII letter ranges
57
+ BYTE_UPPER_A = 0x41 # 'A'
58
+ BYTE_UPPER_Z = 0x5A # 'Z'
59
+ BYTE_LOWER_A = 0x61 # 'a'
60
+ BYTE_LOWER_Z = 0x7A # 'z'
61
+
62
+ # Case conversion offset (lowercase - uppercase = 32)
63
+ CASE_OFFSET = 0x20
27
64
 
28
- # Empty string (no modifier).
29
- EMPTY_STRING = ""
65
+ # ====================================================================
66
+ # Token length
67
+ # ====================================================================
30
68
 
31
- # Terminal marker suffix.
32
- TERMINAL_SUFFIX = "^"
69
+ # Maximum byte length of a valid PIN string: "+K^"
70
+ MAX_BYTE_LENGTH = 3
33
71
  end
34
72
  end
35
73
  end
@@ -5,21 +5,18 @@ module Sashite
5
5
  module Errors
6
6
  class Argument < ::ArgumentError
7
7
  # Centralized error messages for PIN parsing and validation.
8
- #
9
- # @example
10
- # raise Errors::Argument, Messages::EMPTY_INPUT
11
8
  module Messages
12
9
  # Parsing errors
13
- EMPTY_INPUT = "empty input"
14
- INPUT_TOO_LONG = "input exceeds 3 characters"
10
+ EMPTY_INPUT = "empty input"
11
+ INPUT_TOO_LONG = "input exceeds 3 characters"
15
12
  MUST_CONTAIN_ONE_LETTER = "must contain exactly one letter"
16
- INVALID_STATE_MODIFIER = "invalid state modifier"
13
+ INVALID_STATE_MODIFIER = "invalid state modifier"
17
14
  INVALID_TERMINAL_MARKER = "invalid terminal marker"
18
15
 
19
- # Validation errors (constructor)
20
- INVALID_TYPE = "type must be a symbol from :A to :Z"
21
- INVALID_SIDE = "side must be :first or :second"
22
- INVALID_STATE = "state must be :normal, :enhanced, or :diminished"
16
+ # Validation errors (constructor / fetch)
17
+ INVALID_ABBR = "abbr must be a symbol from :A to :Z"
18
+ INVALID_SIDE = "side must be :first or :second"
19
+ INVALID_STATE = "state must be :normal, :enhanced, or :diminished"
23
20
  INVALID_TERMINAL = "terminal must be true or false"
24
21
  end
25
22
  end
@@ -6,9 +6,6 @@ module Sashite
6
6
  module Pin
7
7
  module Errors
8
8
  # Error raised when PIN parsing or validation fails.
9
- #
10
- # @example
11
- # raise Argument, Argument::Messages::EMPTY_INPUT
12
9
  class Argument < ::ArgumentError
13
10
  end
14
11
  end