sashite-sin 2.1.0 → 3.0.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 +4 -4
- data/LICENSE +201 -0
- data/README.md +143 -261
- data/lib/sashite/sin/constants.rb +38 -0
- data/lib/sashite/sin/errors/argument/messages.rb +45 -0
- data/lib/sashite/sin/errors/argument.rb +21 -0
- data/lib/sashite/sin/errors.rb +19 -0
- data/lib/sashite/sin/identifier.rb +155 -311
- data/lib/sashite/sin/parser.rb +137 -0
- data/lib/sashite/sin.rb +48 -166
- metadata +14 -12
- data/LICENSE.md +0 -22
data/README.md
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# sin.rb
|
|
2
2
|
|
|
3
3
|
[](https://github.com/sashite/sin.rb/tags)
|
|
4
4
|
[](https://rubydoc.info/github/sashite/sin.rb/main)
|
|
5
|
-
](https://github.com/sashite/sin.rb/raw/main/LICENSE
|
|
5
|
+
[](https://github.com/sashite/sin.rb/actions)
|
|
6
|
+
[](https://github.com/sashite/sin.rb/raw/main/LICENSE)
|
|
7
7
|
|
|
8
|
-
> **SIN** (Style Identifier Notation) implementation for
|
|
8
|
+
> **SIN** (Style Identifier Notation) implementation for Ruby.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Overview
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
This gem implements the [SIN Specification v1.0.0](https://sashite.dev/specs/sin/1.0.0/) exactly, providing a rule-agnostic notation system for style identification in board games.
|
|
12
|
+
This library implements the [SIN Specification v1.0.0](https://sashite.dev/specs/sin/1.0.0/).
|
|
15
13
|
|
|
16
14
|
## Installation
|
|
17
15
|
|
|
@@ -28,321 +26,205 @@ gem install sashite-sin
|
|
|
28
26
|
|
|
29
27
|
## Usage
|
|
30
28
|
|
|
31
|
-
###
|
|
29
|
+
### Parsing (String → Identifier)
|
|
30
|
+
|
|
31
|
+
Convert a SIN string into an `Identifier` object.
|
|
32
32
|
|
|
33
33
|
```ruby
|
|
34
34
|
require "sashite/sin"
|
|
35
35
|
|
|
36
|
-
#
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
identifier.side # => :first
|
|
41
|
-
identifier.letter # => "C" (combined representation)
|
|
42
|
-
|
|
43
|
-
# Create identifiers directly
|
|
44
|
-
identifier = Sashite::Sin.identifier(:C, :first) # Family=:C, Side=:first
|
|
45
|
-
identifier = Sashite::Sin::Identifier.new(:C, :second) # Family=:C, Side=:second
|
|
46
|
-
|
|
47
|
-
# Validate SIN strings
|
|
48
|
-
Sashite::Sin.valid?("C") # => true
|
|
49
|
-
Sashite::Sin.valid?("c") # => true
|
|
50
|
-
Sashite::Sin.valid?("1") # => false (not a letter)
|
|
51
|
-
Sashite::Sin.valid?("CC") # => false (not single character)
|
|
52
|
-
```
|
|
36
|
+
# Standard parsing (raises on error)
|
|
37
|
+
sin = Sashite::Sin.parse("C")
|
|
38
|
+
sin.style # => :C
|
|
39
|
+
sin.side # => :first
|
|
53
40
|
|
|
54
|
-
|
|
41
|
+
# Lowercase indicates second player
|
|
42
|
+
sin = Sashite::Sin.parse("c")
|
|
43
|
+
sin.style # => :C
|
|
44
|
+
sin.side # => :second
|
|
55
45
|
|
|
56
|
-
|
|
57
|
-
#
|
|
58
|
-
|
|
46
|
+
# Invalid input raises ArgumentError
|
|
47
|
+
Sashite::Sin.parse("") # => raises ArgumentError
|
|
48
|
+
Sashite::Sin.parse("CC") # => raises ArgumentError
|
|
49
|
+
```
|
|
59
50
|
|
|
60
|
-
|
|
61
|
-
flipped = identifier.flip # Family=:C, Side=:second
|
|
62
|
-
flipped.to_s # => "c"
|
|
51
|
+
### Formatting (Identifier → String)
|
|
63
52
|
|
|
64
|
-
|
|
65
|
-
changed = identifier.with_family(:S) # Family=:S, Side=:first
|
|
66
|
-
changed.to_s # => "S"
|
|
53
|
+
Convert an `Identifier` back to a SIN string.
|
|
67
54
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
55
|
+
```ruby
|
|
56
|
+
# From Identifier object
|
|
57
|
+
sin = Sashite::Sin::Identifier.new(:C, :first)
|
|
58
|
+
sin.to_s # => "C"
|
|
71
59
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
result.to_s # => "m"
|
|
60
|
+
sin = Sashite::Sin::Identifier.new(:C, :second)
|
|
61
|
+
sin.to_s # => "c"
|
|
75
62
|
```
|
|
76
63
|
|
|
77
|
-
###
|
|
64
|
+
### Validation
|
|
78
65
|
|
|
79
66
|
```ruby
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
#
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
opposite.first_player? # => false
|
|
87
|
-
opposite.second_player? # => true
|
|
88
|
-
|
|
89
|
-
# Family and side comparison
|
|
90
|
-
chess1 = Sashite::Sin.parse("C")
|
|
91
|
-
chess2 = Sashite::Sin.parse("c")
|
|
92
|
-
shogi = Sashite::Sin.parse("S")
|
|
93
|
-
|
|
94
|
-
chess1.same_family?(chess2) # => true (both Chess family)
|
|
95
|
-
chess1.same_side?(shogi) # => true (both first player)
|
|
96
|
-
chess1.same_family?(shogi) # => false (different families)
|
|
67
|
+
# Boolean check
|
|
68
|
+
Sashite::Sin.valid?("C") # => true
|
|
69
|
+
Sashite::Sin.valid?("c") # => true
|
|
70
|
+
Sashite::Sin.valid?("") # => false
|
|
71
|
+
Sashite::Sin.valid?("CC") # => false
|
|
72
|
+
Sashite::Sin.valid?("1") # => false
|
|
97
73
|
```
|
|
98
74
|
|
|
99
|
-
### Identifier
|
|
75
|
+
### Accessing Identifier Data
|
|
100
76
|
|
|
101
77
|
```ruby
|
|
102
|
-
|
|
103
|
-
identifiers = %w[C c S s M m].map { |sin| Sashite::Sin.parse(sin) }
|
|
104
|
-
|
|
105
|
-
# Filter by player
|
|
106
|
-
first_player_identifiers = identifiers.select(&:first_player?)
|
|
107
|
-
first_player_identifiers.map(&:to_s) # => ["C", "S", "M"]
|
|
78
|
+
sin = Sashite::Sin.parse("C")
|
|
108
79
|
|
|
109
|
-
#
|
|
110
|
-
|
|
111
|
-
|
|
80
|
+
# Get attributes
|
|
81
|
+
sin.style # => :C
|
|
82
|
+
sin.side # => :first
|
|
112
83
|
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
chess_identifiers.map(&:to_s) # => ["C", "c"]
|
|
84
|
+
# Get string component
|
|
85
|
+
sin.letter # => "C"
|
|
116
86
|
```
|
|
117
87
|
|
|
118
|
-
|
|
88
|
+
### Transformations
|
|
119
89
|
|
|
120
|
-
|
|
121
|
-
```
|
|
122
|
-
<style-letter>
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### Grammar (BNF)
|
|
126
|
-
```bnf
|
|
127
|
-
<sin> ::= <uppercase-letter> | <lowercase-letter>
|
|
90
|
+
All transformations return new immutable `Identifier` objects.
|
|
128
91
|
|
|
129
|
-
<uppercase-letter> ::= "A" | "B" | "C" | ... | "Z"
|
|
130
|
-
<lowercase-letter> ::= "a" | "b" | "c" | ... | "z"
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### Regular Expression
|
|
134
92
|
```ruby
|
|
135
|
-
|
|
136
|
-
```
|
|
93
|
+
sin = Sashite::Sin.parse("C")
|
|
137
94
|
|
|
138
|
-
|
|
95
|
+
# Side transformation
|
|
96
|
+
sin.flip.to_s # => "c"
|
|
139
97
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
| **Family** | Style family symbol | `:C`, `:S`, `:X` |
|
|
145
|
-
| **Side** | Player assignment | `:first`, `:second` |
|
|
146
|
-
| **Letter** | Combined representation | `"C"`, `"c"`, `"S"`, `"s"` |
|
|
147
|
-
|
|
148
|
-
#### Dual-Purpose Encoding
|
|
149
|
-
|
|
150
|
-
The **Letter** combines two distinct semantic components:
|
|
151
|
-
- **Style Family**: The underlying family symbol (:A-:Z), representing the game tradition or rule system
|
|
152
|
-
- **Player Assignment**: The side (:first or :second), encoded as case in the letter representation
|
|
153
|
-
|
|
154
|
-
**Examples**:
|
|
155
|
-
- Family `:C` + Side `:first` → Letter `"C"` (Chess, First player)
|
|
156
|
-
- Family `:C` + Side `:second` → Letter `"c"` (Chess, Second player)
|
|
157
|
-
- Family `:S` + Side `:first` → Letter `"S"` (Shōgi, First player)
|
|
158
|
-
- Family `:S` + Side `:second` → Letter `"s"` (Shōgi, Second player)
|
|
159
|
-
|
|
160
|
-
## Traditional Game Style Examples
|
|
98
|
+
# Attribute changes
|
|
99
|
+
sin.with_style(:S).to_s # => "S"
|
|
100
|
+
sin.with_side(:second).to_s # => "c"
|
|
101
|
+
```
|
|
161
102
|
|
|
162
|
-
|
|
103
|
+
### Queries
|
|
163
104
|
|
|
164
105
|
```ruby
|
|
165
|
-
|
|
166
|
-
chess_white = Sashite::Sin.parse("C") # First player (White pieces)
|
|
167
|
-
chess_black = Sashite::Sin.parse("c") # Second player (Black pieces)
|
|
168
|
-
|
|
169
|
-
# Shōgi (9×9 board)
|
|
170
|
-
shogi_sente = Sashite::Sin.parse("S") # First player (Sente 先手)
|
|
171
|
-
shogi_gote = Sashite::Sin.parse("s") # Second player (Gote 後手)
|
|
106
|
+
sin = Sashite::Sin.parse("C")
|
|
172
107
|
|
|
173
|
-
#
|
|
174
|
-
|
|
175
|
-
|
|
108
|
+
# Side queries
|
|
109
|
+
sin.first_player? # => true
|
|
110
|
+
sin.second_player? # => false
|
|
176
111
|
|
|
177
|
-
#
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
# Janggi (9×10 board)
|
|
182
|
-
janggi_cho = Sashite::Sin.parse("J") # First player (Cho 초)
|
|
183
|
-
janggi_han = Sashite::Sin.parse("j") # Second player (Han 한)
|
|
112
|
+
# Comparison queries
|
|
113
|
+
other = Sashite::Sin.parse("c")
|
|
114
|
+
sin.same_style?(other) # => true
|
|
115
|
+
sin.same_side?(other) # => false
|
|
184
116
|
```
|
|
185
117
|
|
|
186
|
-
##
|
|
118
|
+
## API Reference
|
|
119
|
+
|
|
120
|
+
### Types
|
|
187
121
|
|
|
188
122
|
```ruby
|
|
189
|
-
#
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
#
|
|
194
|
-
|
|
195
|
-
[
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
123
|
+
# Identifier represents a parsed SIN identifier with style and side.
|
|
124
|
+
class Sashite::Sin::Identifier
|
|
125
|
+
# Creates an Identifier from style and side.
|
|
126
|
+
# Raises ArgumentError if attributes are invalid.
|
|
127
|
+
#
|
|
128
|
+
# @param style [Symbol] Style abbreviation (:A through :Z)
|
|
129
|
+
# @param side [Symbol] Player side (:first or :second)
|
|
130
|
+
# @return [Identifier]
|
|
131
|
+
def initialize(style, side)
|
|
132
|
+
|
|
133
|
+
# Returns the style as an uppercase symbol.
|
|
134
|
+
#
|
|
135
|
+
# @return [Symbol]
|
|
136
|
+
def style
|
|
137
|
+
|
|
138
|
+
# Returns the player side.
|
|
139
|
+
#
|
|
140
|
+
# @return [Symbol] :first or :second
|
|
141
|
+
def side
|
|
142
|
+
|
|
143
|
+
# Returns the SIN string representation.
|
|
144
|
+
#
|
|
145
|
+
# @return [String]
|
|
146
|
+
def to_s
|
|
199
147
|
end
|
|
200
|
-
|
|
201
|
-
identifiers = create_hybrid_match
|
|
202
|
-
identifiers[0].same_side?(identifiers[1]) # => false (different players)
|
|
203
|
-
identifiers[0].same_family?(identifiers[1]) # => false (different families)
|
|
204
148
|
```
|
|
205
149
|
|
|
206
|
-
## System Constraints
|
|
207
|
-
|
|
208
|
-
### Character Limitation
|
|
209
|
-
SIN provides **26 possible identifiers** per player using ASCII letters (A-Z, a-z).
|
|
210
|
-
|
|
211
|
-
### Player Limitation
|
|
212
|
-
SIN supports exactly **two players** through case distinction:
|
|
213
|
-
- **First player**: Uppercase letters (A-Z) → `:first`
|
|
214
|
-
- **Second player**: Lowercase letters (a-z) → `:second`
|
|
215
|
-
|
|
216
|
-
### Context Dependency
|
|
217
|
-
The specific SIN assignment for a style may vary between different game contexts based on collision avoidance, historical precedence, and community conventions.
|
|
218
|
-
|
|
219
|
-
## API Reference
|
|
220
|
-
|
|
221
|
-
### Main Module Methods
|
|
222
|
-
|
|
223
|
-
- `Sashite::Sin.valid?(sin_string)` - Check if string is valid SIN notation
|
|
224
|
-
- `Sashite::Sin.parse(sin_string)` - Parse SIN string into Identifier object
|
|
225
|
-
- `Sashite::Sin.identifier(family, side)` - Create identifier instance directly
|
|
226
|
-
|
|
227
|
-
### Identifier Class
|
|
228
|
-
|
|
229
|
-
#### Creation and Parsing
|
|
230
|
-
- `Sashite::Sin::Identifier.new(family, side)` - Create identifier instance
|
|
231
|
-
- `Sashite::Sin::Identifier.parse(sin_string)` - Parse SIN string
|
|
232
|
-
|
|
233
|
-
#### Attribute Access
|
|
234
|
-
- `#family` - Get style family (symbol :A through :Z)
|
|
235
|
-
- `#side` - Get player side (:first or :second)
|
|
236
|
-
- `#letter` - Get combined letter representation (string)
|
|
237
|
-
- `#to_s` - Convert to SIN string representation
|
|
238
|
-
|
|
239
|
-
#### Player Queries
|
|
240
|
-
- `#first_player?` - Check if first player identifier
|
|
241
|
-
- `#second_player?` - Check if second player identifier
|
|
242
|
-
|
|
243
|
-
#### Transformations (immutable - return new instances)
|
|
244
|
-
- `#flip` - Switch player assignment
|
|
245
|
-
- `#with_family(new_family)` - Create identifier with different family
|
|
246
|
-
- `#with_side(new_side)` - Create identifier with different side
|
|
247
|
-
|
|
248
|
-
#### Comparison Methods
|
|
249
|
-
- `#same_family?(other)` - Check if same style family
|
|
250
|
-
- `#same_side?(other)` - Check if same player side
|
|
251
|
-
- `#==(other)` - Full equality comparison
|
|
252
|
-
- `#same_letter?(other)` - Alias for `same_family?` (deprecated)
|
|
253
|
-
|
|
254
150
|
### Constants
|
|
255
151
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
- `Sashite::Sin::Identifier::SIN_PATTERN` - Regular expression for SIN validation
|
|
261
|
-
|
|
262
|
-
## Advanced Usage
|
|
152
|
+
```ruby
|
|
153
|
+
Sashite::Sin::Identifier::VALID_STYLES # => [:A, :B, ..., :Z]
|
|
154
|
+
Sashite::Sin::Identifier::VALID_SIDES # => [:first, :second]
|
|
155
|
+
```
|
|
263
156
|
|
|
264
|
-
###
|
|
157
|
+
### Parsing
|
|
265
158
|
|
|
266
159
|
```ruby
|
|
267
|
-
#
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
chess_white = Sashite::Sin.identifier(:C, :first)
|
|
275
|
-
shogi_white = chess_white.with_family(:S) # Change family, keep side
|
|
276
|
-
chess_black = chess_white.with_side(:second) # Change side, keep family
|
|
160
|
+
# Parses a SIN string into an Identifier.
|
|
161
|
+
# Raises ArgumentError if the string is not valid.
|
|
162
|
+
#
|
|
163
|
+
# @param string [String] SIN string
|
|
164
|
+
# @return [Identifier]
|
|
165
|
+
# @raise [ArgumentError] if invalid
|
|
166
|
+
def Sashite::Sin.parse(string)
|
|
277
167
|
```
|
|
278
168
|
|
|
279
|
-
###
|
|
169
|
+
### Validation
|
|
280
170
|
|
|
281
171
|
```ruby
|
|
282
|
-
#
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
# Original identifier is never modified
|
|
288
|
-
original.to_s # => "C" (unchanged)
|
|
289
|
-
flipped.to_s # => "c"
|
|
290
|
-
changed_family.to_s # => "S"
|
|
291
|
-
|
|
292
|
-
# Transformations can be chained
|
|
293
|
-
result = original.flip.with_family(:M).flip
|
|
294
|
-
result.to_s # => "M"
|
|
172
|
+
# Reports whether string is a valid SIN identifier.
|
|
173
|
+
#
|
|
174
|
+
# @param string [String] SIN string
|
|
175
|
+
# @return [Boolean]
|
|
176
|
+
def Sashite::Sin.valid?(string)
|
|
295
177
|
```
|
|
296
178
|
|
|
297
|
-
|
|
179
|
+
### Transformations
|
|
298
180
|
|
|
299
|
-
|
|
181
|
+
All transformations return new `Sashite::Sin::Identifier` objects:
|
|
300
182
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
- **Flexible collision resolution**: Systematic approaches for identifier conflicts
|
|
305
|
-
- **Semantic clarity**: Distinct concepts for Family, Side, and Letter
|
|
306
|
-
- **SNN coordination**: Works harmoniously with formal style naming
|
|
307
|
-
- **Context-aware**: Adapts to avoid conflicts within specific game scenarios
|
|
308
|
-
- **Canonical representation**: Each style-player combination has exactly one SIN identifier
|
|
309
|
-
- **Immutable**: All identifier instances are frozen and transformations return new objects
|
|
310
|
-
- **Functional**: Pure functions with no side effects
|
|
183
|
+
```ruby
|
|
184
|
+
# Side transformation
|
|
185
|
+
def flip # => Identifier
|
|
311
186
|
|
|
312
|
-
|
|
187
|
+
# Attribute changes
|
|
188
|
+
def with_style(style) # => Identifier
|
|
189
|
+
def with_side(side) # => Identifier
|
|
190
|
+
```
|
|
313
191
|
|
|
314
|
-
|
|
315
|
-
- [SIN Examples](https://sashite.dev/specs/sin/1.0.0/examples/) - Practical implementation examples
|
|
316
|
-
- [Style Name Notation (SNN)](https://sashite.dev/specs/snn/) - Formal naming for game styles
|
|
317
|
-
- [Sashité Protocol](https://sashite.dev/protocol/) - Conceptual foundation for abstract strategy board games
|
|
192
|
+
### Queries
|
|
318
193
|
|
|
319
|
-
|
|
194
|
+
```ruby
|
|
195
|
+
# Side queries
|
|
196
|
+
def first_player? # => Boolean
|
|
197
|
+
def second_player? # => Boolean
|
|
320
198
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
199
|
+
# Comparison queries
|
|
200
|
+
def same_style?(other) # => Boolean
|
|
201
|
+
def same_side?(other) # => Boolean
|
|
202
|
+
```
|
|
324
203
|
|
|
325
|
-
|
|
204
|
+
### Errors
|
|
326
205
|
|
|
327
|
-
|
|
328
|
-
# Clone the repository
|
|
329
|
-
git clone https://github.com/sashite/sin.rb.git
|
|
330
|
-
cd sin.rb
|
|
206
|
+
All parsing and validation errors raise `ArgumentError` with descriptive messages:
|
|
331
207
|
|
|
332
|
-
|
|
333
|
-
|
|
208
|
+
| Message | Cause |
|
|
209
|
+
|---------|-------|
|
|
210
|
+
| `"empty input"` | String length is 0 |
|
|
211
|
+
| `"input exceeds 1 character"` | String too long |
|
|
212
|
+
| `"must be a letter"` | Character is not A-Z or a-z |
|
|
334
213
|
|
|
335
|
-
|
|
336
|
-
ruby test.rb
|
|
214
|
+
## Design Principles
|
|
337
215
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
216
|
+
- **Bounded values**: Explicit validation of styles and sides
|
|
217
|
+
- **Object-oriented**: `Identifier` class enables methods and encapsulation
|
|
218
|
+
- **Ruby idioms**: `valid?` predicate, `to_s` conversion, `ArgumentError` for invalid input
|
|
219
|
+
- **Immutable identifiers**: All transformations return new objects
|
|
220
|
+
- **No dependencies**: Pure Ruby standard library only
|
|
341
221
|
|
|
342
|
-
##
|
|
222
|
+
## Related Specifications
|
|
343
223
|
|
|
344
|
-
|
|
224
|
+
- [Game Protocol](https://sashite.dev/game-protocol/) — Conceptual foundation
|
|
225
|
+
- [SIN Specification](https://sashite.dev/specs/sin/1.0.0/) — Official specification
|
|
226
|
+
- [SIN Examples](https://sashite.dev/specs/sin/1.0.0/examples/) — Usage examples
|
|
345
227
|
|
|
346
|
-
##
|
|
228
|
+
## License
|
|
347
229
|
|
|
348
|
-
|
|
230
|
+
Available as open source under the [Apache License 2.0](https://opensource.org/licenses/Apache-2.0).
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sashite
|
|
4
|
+
module Sin
|
|
5
|
+
# Constants for the SIN (Style Identifier Notation) specification.
|
|
6
|
+
#
|
|
7
|
+
# Defines valid values for styles and sides, as well as formatting constants.
|
|
8
|
+
#
|
|
9
|
+
# @example Accessing valid styles
|
|
10
|
+
# Constants::VALID_STYLES # => [:A, :B, ..., :Z]
|
|
11
|
+
#
|
|
12
|
+
# @example Accessing valid sides
|
|
13
|
+
# Constants::VALID_SIDES # => [:first, :second]
|
|
14
|
+
#
|
|
15
|
+
# @see https://sashite.dev/specs/sin/1.0.0/
|
|
16
|
+
module Constants
|
|
17
|
+
# Valid style symbols (A-Z as uppercase symbols).
|
|
18
|
+
#
|
|
19
|
+
# @return [Array<Symbol>] Array of 26 valid style symbols
|
|
20
|
+
VALID_STYLES = %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
|
|
21
|
+
|
|
22
|
+
# Valid side symbols.
|
|
23
|
+
#
|
|
24
|
+
# @return [Array<Symbol>] Array of valid side symbols
|
|
25
|
+
VALID_SIDES = %i[first second].freeze
|
|
26
|
+
|
|
27
|
+
# Maximum length of a valid SIN string.
|
|
28
|
+
#
|
|
29
|
+
# @return [Integer] Maximum string length (1)
|
|
30
|
+
MAX_STRING_LENGTH = 1
|
|
31
|
+
|
|
32
|
+
# Empty string constant for internal use.
|
|
33
|
+
#
|
|
34
|
+
# @return [String] Empty string
|
|
35
|
+
EMPTY_STRING = ""
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sashite
|
|
4
|
+
module Sin
|
|
5
|
+
module Errors
|
|
6
|
+
class Argument < ::ArgumentError
|
|
7
|
+
# Error messages for SIN parsing and validation.
|
|
8
|
+
#
|
|
9
|
+
# Provides centralized, immutable error message constants for consistent
|
|
10
|
+
# error reporting across the library.
|
|
11
|
+
#
|
|
12
|
+
# @example Using an error message
|
|
13
|
+
# raise ArgumentError, Messages::EMPTY_INPUT
|
|
14
|
+
#
|
|
15
|
+
# @see https://sashite.dev/specs/sin/1.0.0/
|
|
16
|
+
module Messages
|
|
17
|
+
# Error message for empty input string.
|
|
18
|
+
#
|
|
19
|
+
# @return [String] Error message
|
|
20
|
+
EMPTY_INPUT = "empty input"
|
|
21
|
+
|
|
22
|
+
# Error message for input exceeding maximum length.
|
|
23
|
+
#
|
|
24
|
+
# @return [String] Error message
|
|
25
|
+
INPUT_TOO_LONG = "input exceeds 1 character"
|
|
26
|
+
|
|
27
|
+
# Error message for invalid character (not a letter).
|
|
28
|
+
#
|
|
29
|
+
# @return [String] Error message
|
|
30
|
+
MUST_BE_LETTER = "must be a letter"
|
|
31
|
+
|
|
32
|
+
# Error message for invalid style value.
|
|
33
|
+
#
|
|
34
|
+
# @return [String] Error message
|
|
35
|
+
INVALID_STYLE = "invalid style"
|
|
36
|
+
|
|
37
|
+
# Error message for invalid side value.
|
|
38
|
+
#
|
|
39
|
+
# @return [String] Error message
|
|
40
|
+
INVALID_SIDE = "invalid side"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "argument/messages"
|
|
4
|
+
|
|
5
|
+
module Sashite
|
|
6
|
+
module Sin
|
|
7
|
+
module Errors
|
|
8
|
+
# Namespace for ArgumentError-related constants and messages.
|
|
9
|
+
#
|
|
10
|
+
# Provides structured access to error messages used when raising
|
|
11
|
+
# ArgumentError exceptions throughout the library.
|
|
12
|
+
#
|
|
13
|
+
# @example Raising an error with a message
|
|
14
|
+
# raise ArgumentError, Argument::Messages::EMPTY_INPUT
|
|
15
|
+
#
|
|
16
|
+
# @see Argument::Messages
|
|
17
|
+
class Argument < ::ArgumentError
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "errors/argument"
|
|
4
|
+
|
|
5
|
+
module Sashite
|
|
6
|
+
module Sin
|
|
7
|
+
# Namespace for error-related constants and messages.
|
|
8
|
+
#
|
|
9
|
+
# Provides structured access to error messages used throughout the library.
|
|
10
|
+
#
|
|
11
|
+
# @example Accessing error messages
|
|
12
|
+
# Errors::Argument::Messages::EMPTY_INPUT # => "empty input"
|
|
13
|
+
#
|
|
14
|
+
# @see Errors::Argument
|
|
15
|
+
# @see Errors::Argument::Messages
|
|
16
|
+
module Errors
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|