feen 5.0.0.beta2 → 5.0.0.beta3

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.
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Feen
4
+ module Parser
5
+ module PiecesInHand
6
+ # Error messages for validation
7
+ Errors = {
8
+ invalid_type: "Pieces in hand must be a string, got %s",
9
+ empty_string: "Pieces in hand string cannot be empty",
10
+ invalid_format: "Invalid pieces in hand format: %s"
11
+ }.freeze
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Feen
4
+ module Parser
5
+ module PiecesInHand
6
+ # Character used to represent no pieces in hand
7
+ NoPieces = "-"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Feen
4
+ module Parser
5
+ module PiecesInHand
6
+ # Valid pattern for pieces in hand based on BNF:
7
+ # <pieces-in-hand> ::= "-" | <A-part> <B-part> ... <Z-part> <a-part> <b-part> ... <z-part>
8
+ # where each part can be empty or contain repetitions of the same letter
9
+ ValidFormatPattern = /\A(?:-|
10
+ 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*
11
+ 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*
12
+ )\z/x
13
+ end
14
+ end
15
+ end
@@ -2,73 +2,82 @@
2
2
 
3
3
  module Feen
4
4
  module Parser
5
- # Handles parsing of the pieces in hand section of a FEEN string
5
+ # Handles parsing of the pieces in hand section of a FEEN string.
6
+ # Pieces in hand represent pieces available for dropping onto the board.
6
7
  module PiecesInHand
8
+ # Character used to represent no pieces in hand
7
9
  NO_PIECES = "-"
10
+
11
+ # Error messages for validation
8
12
  ERRORS = {
9
- invalid_type: "Pieces in hand must be a string, got %s",
10
- empty_string: "Pieces in hand string cannot be empty",
11
- invalid_chars: "Invalid characters in pieces in hand: %s",
12
- invalid_identifier: "Invalid piece identifier at position %d"
13
+ invalid_type: "Pieces in hand must be a string, got %s",
14
+ empty_string: "Pieces in hand string cannot be empty",
15
+ invalid_format: "Invalid pieces in hand format: %s",
16
+ sorting_error: "Pieces in hand must be in ASCII lexicographic order"
13
17
  }.freeze
14
18
 
15
- # Parses the pieces in hand section of a FEEN string
19
+ # Valid pattern for pieces in hand based on BNF:
20
+ # <pieces-in-hand> ::= "-" | <piece> <pieces-in-hand>
21
+ # <piece> ::= [a-zA-Z]
22
+ VALID_FORMAT_PATTERN = /\A(?:-|[a-zA-Z]+)\z/
23
+
24
+ # Parses the pieces in hand section of a FEEN string.
16
25
  #
17
26
  # @param pieces_in_hand_str [String] FEEN pieces in hand string
18
- # @return [Array<Hash>] Array of pieces in hand
27
+ # @return [Array<String>] Array of single-character piece identifiers in the
28
+ # format specified in the FEEN string (no prefixes or suffixes), sorted in ASCII
29
+ # lexicographic order. Empty array if no pieces are in hand.
19
30
  # @raise [ArgumentError] If the input string is invalid
31
+ #
32
+ # @example Parse no pieces in hand
33
+ # PiecesInHand.parse("-")
34
+ # # => []
35
+ #
36
+ # @example Parse multiple pieces in hand
37
+ # PiecesInHand.parse("BNPPb")
38
+ # # => ["B", "N", "P", "P", "b"]
20
39
  def self.parse(pieces_in_hand_str)
21
- validate_pieces_in_hand_string(pieces_in_hand_str)
40
+ validate_input_type(pieces_in_hand_str)
41
+ validate_format(pieces_in_hand_str)
22
42
 
23
- # Handle the special case of no pieces in hand
24
43
  return [] if pieces_in_hand_str == NO_PIECES
25
44
 
26
- pieces = []
27
- i = 0
28
-
29
- while i < pieces_in_hand_str.length
30
- # Vérifier que le caractère est une lettre
31
- raise ArgumentError, format(ERRORS[:invalid_identifier], i) unless pieces_in_hand_str[i].match?(/[a-zA-Z]/)
32
-
33
- pieces << { id: pieces_in_hand_str[i] }
34
- i += 1
35
-
36
- end
37
-
38
- # Vérifier que les pièces sont triées par ordre lexicographique
39
- raise ArgumentError, "Pieces in hand must be in ASCII lexicographic order" unless pieces_sorted?(pieces)
45
+ pieces = pieces_in_hand_str.chars
46
+ validate_pieces_order(pieces)
40
47
 
41
48
  pieces
42
49
  end
43
50
 
44
- # Validates the pieces in hand string for syntax
51
+ # Validates that the input is a non-empty string.
45
52
  #
46
- # @param str [String] FEEN pieces in hand string
47
- # @raise [ArgumentError] If the string is invalid
53
+ # @param str [String] Input string to validate
54
+ # @raise [ArgumentError] If input is not a string or is empty
48
55
  # @return [void]
49
- def self.validate_pieces_in_hand_string(str)
50
- raise ArgumentError, format(ERRORS[:invalid_type], str.class) unless str.is_a?(String)
51
-
52
- raise ArgumentError, ERRORS[:empty_string] if str.empty?
53
-
54
- # Check for the special case of no pieces in hand
55
- return if str == NO_PIECES
56
+ private_class_method def self.validate_input_type(str)
57
+ raise ::ArgumentError, format(ERRORS[:invalid_type], str.class) unless str.is_a?(::String)
58
+ raise ::ArgumentError, ERRORS[:empty_string] if str.empty?
59
+ end
56
60
 
57
- # Check for valid characters (only letters)
58
- valid_chars = /\A[a-zA-Z]+\z/
59
- return if str.match?(valid_chars)
61
+ # Validates that the input string matches the expected format.
62
+ #
63
+ # @param str [String] Input string to validate
64
+ # @raise [ArgumentError] If format is invalid
65
+ # @return [void]
66
+ private_class_method def self.validate_format(str)
67
+ return if str.match?(VALID_FORMAT_PATTERN)
60
68
 
61
- invalid_chars = str.scan(/[^a-zA-Z]/).uniq.join(", ")
62
- raise ArgumentError, format(ERRORS[:invalid_chars], invalid_chars)
69
+ raise ::ArgumentError, format(ERRORS[:invalid_format], str)
63
70
  end
64
71
 
65
- # Checks if pieces are sorted in ASCII lexicographic order
72
+ # Validates that pieces are sorted in ASCII lexicographic order.
66
73
  #
67
- # @param pieces [Array<Hash>] Array of piece hashes
68
- # @return [Boolean] True if pieces are sorted
69
- def self.pieces_sorted?(pieces)
70
- piece_ids = pieces.map { |piece| piece[:id] }
71
- piece_ids == piece_ids.sort
74
+ # @param pieces [Array<String>] Array of piece identifiers
75
+ # @raise [ArgumentError] If pieces are not sorted
76
+ # @return [void]
77
+ private_class_method def self.validate_pieces_order(pieces)
78
+ return if pieces == pieces.sort
79
+
80
+ raise ::ArgumentError, ERRORS[:sorting_error]
72
81
  end
73
82
  end
74
83
  end
data/lib/feen/parser.rb CHANGED
@@ -5,48 +5,85 @@ require_relative File.join("parser", "piece_placement")
5
5
  require_relative File.join("parser", "pieces_in_hand")
6
6
 
7
7
  module Feen
8
- # Module responsible for parsing FEEN notation strings into internal data structures
8
+ # Module responsible for parsing FEEN notation strings into internal data structures.
9
+ # FEEN (Format for Encounter & Entertainment Notation) is a compact, canonical, and rule-agnostic
10
+ # textual format for representing static board positions in two-player piece-placement games.
9
11
  module Parser
12
+ # Regular expression pattern for matching a valid FEEN string structure
13
+ # Captures exactly three non-space components separated by single spaces
14
+ FEEN_PATTERN = /\A([^\s]+)\s([^\s]+)\s([^\s]+)\z/
15
+
16
+ # Error message for invalid FEEN format
17
+ INVALID_FORMAT_ERROR = "Invalid FEEN format: expected exactly 3 fields separated by single spaces"
18
+
10
19
  # Parses a complete FEEN string into a structured representation
11
20
  #
12
21
  # @param feen_string [String] Complete FEEN notation string
13
- # @return [Hash] Hash containing the parsed position data
14
- # @raise [ArgumentError] If the FEEN string is invalid
22
+ # @return [Hash] Hash containing the parsed position data with the following keys:
23
+ # - :piece_placement [Array] - Hierarchical array structure representing the board
24
+ # - :pieces_in_hand [Array<String>] - Pieces available for dropping onto the board
25
+ # - :games_turn [Array<String>] - A two-element array with [active_variant, inactive_variant]
26
+ # @raise [ArgumentError] If the FEEN string is invalid or any component cannot be parsed
27
+ #
28
+ # @example Parsing a standard chess initial position
29
+ # feen = "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR - CHESS/chess"
30
+ # result = Feen::Parser.parse(feen)
31
+ # # => {
32
+ # # piece_placement: [
33
+ # # ["r", "n", "b", "q", "k=", "b", "n", "r"],
34
+ # # ["p", "p", "p", "p", "p", "p", "p", "p"],
35
+ # # ["", "", "", "", "", "", "", ""],
36
+ # # ["", "", "", "", "", "", "", ""],
37
+ # # ["", "", "", "", "", "", "", ""],
38
+ # # ["", "", "", "", "", "", "", ""],
39
+ # # ["P", "P", "P", "P", "P", "P", "P", "P"],
40
+ # # ["R", "N", "B", "Q", "K=", "B", "N", "R"]
41
+ # # ],
42
+ # # pieces_in_hand: [],
43
+ # # games_turn: ["CHESS", "chess"]
44
+ # # }
45
+ #
46
+ # @example Parsing a shogi position (from a Tempo Loss Bishop Exchange opening) with pieces in hand
47
+ # feen = "lnsgk2nl/1r4gs1/p1pppp1pp/1p4p2/7P1/2P6/PP1PPPP1P/1SG4R1/LN2KGSNL Bb SHOGI/shogi"
48
+ # result = Feen::Parser.parse(feen)
49
+ # # => {
50
+ # # piece_placement: [
51
+ # # ["l", "n", "s", "g", "k", "", "", "n", "l"],
52
+ # # ["", "r", "", "", "", "", "g", "s", ""],
53
+ # # ["p", "", "p", "p", "p", "p", "", "p", "p"],
54
+ # # ["", "p", "", "", "", "", "p", "", ""],
55
+ # # ["", "", "", "", "", "", "", "P", ""],
56
+ # # ["", "", "P", "", "", "", "", "", ""],
57
+ # # ["P", "P", "", "P", "P", "P", "P", "", "P"],
58
+ # # ["", "S", "G", "", "", "", "", "R", ""],
59
+ # # ["L", "N", "", "", "K", "G", "S", "N", "L"]
60
+ # # ],
61
+ # # pieces_in_hand: ["B", "b"],
62
+ # # games_turn: ["SHOGI", "shogi"]
63
+ # # }
15
64
  def self.parse(feen_string)
16
- validate_feen_string(feen_string)
65
+ feen_string = String(feen_string)
66
+
67
+ # Match the FEEN string against the expected pattern
68
+ match = FEEN_PATTERN.match(feen_string)
17
69
 
18
- # Split the FEEN string into its three fields
19
- fields = feen_string.strip.split(/\s+/)
70
+ # Raise an error if the format doesn't match the expected pattern
71
+ raise ::ArgumentError, INVALID_FORMAT_ERROR unless match
20
72
 
21
- raise ArgumentError, "Invalid FEEN format: expected 3 fields, got #{fields.size}" unless fields.size == 3
73
+ # Capture the three distinct parts
74
+ piece_placement_string, pieces_in_hand_string, games_turn_string = match.captures
22
75
 
23
76
  # Parse each field using the appropriate submodule
24
- piece_placement = PiecePlacement.parse(fields[0])
25
- games_turn = GamesTurn.parse(fields[1])
26
- pieces_in_hand = PiecesInHand.parse(fields[2])
77
+ piece_placement = PiecePlacement.parse(piece_placement_string)
78
+ pieces_in_hand = PiecesInHand.parse(pieces_in_hand_string)
79
+ games_turn = GamesTurn.parse(games_turn_string)
27
80
 
28
- # Return a structured representation of the position
81
+ # Create a structured representation of the position
29
82
  {
30
- piece_placement: piece_placement,
31
- games_turn: games_turn,
32
- pieces_in_hand: pieces_in_hand
83
+ piece_placement:,
84
+ pieces_in_hand:,
85
+ games_turn:
33
86
  }
34
87
  end
35
-
36
- # Validates the FEEN string for basic format
37
- #
38
- # @param feen_string [String] FEEN string to validate
39
- # @raise [ArgumentError] If the FEEN string is fundamentally invalid
40
- # @return [void]
41
- def self.validate_feen_string(feen_string)
42
- raise ArgumentError, "FEEN must be a string, got #{feen_string.class}" unless feen_string.is_a?(String)
43
-
44
- raise ArgumentError, "FEEN string cannot be empty" if feen_string.empty?
45
-
46
- # Check for at least two spaces (three fields)
47
- return unless feen_string.count(" ") < 2
48
-
49
- raise ArgumentError, "Invalid FEEN format: must contain at least two spaces separating three fields"
50
- end
51
88
  end
52
89
  end
data/lib/feen.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative File.join("feen", "converter")
4
3
  require_relative File.join("feen", "dumper")
5
4
  require_relative File.join("feen", "parser")
6
5
 
@@ -9,102 +8,69 @@ require_relative File.join("feen", "parser")
9
8
  #
10
9
  # @see https://sashite.dev/documents/feen/1.0.0/
11
10
  module Feen
12
- # Dumps position params into a FEEN string.
11
+ # Dumps position components into a FEEN string.
13
12
  #
14
- # @param position [Hash] Hash containing the position data
15
- # @option position [Array] :piece_placement Board position data
16
- # @option position [Hash] :games_turn Games and turn data
17
- # @option position [Array<Hash>] :pieces_in_hand Pieces in hand data
13
+ # @see Feen::Dumper.dump for the detailed parameters documentation
18
14
  # @return [String] FEEN notation string
19
- # @raise [ArgumentError] If the position data is invalid
15
+ # @raise [ArgumentError] If any parameter is invalid
20
16
  # @example
21
- # position = {
22
- # piece_placement: [[{id: 'r'}, {id: 'n'}, {id: 'b'}, {id: 'q'}, {id: 'k'}, {id: 'b'}, {id: 'n'}, {id: 'r'}],
23
- # [{id: 'p'}, {id: 'p'}, {id: 'p'}, {id: 'p'}, {id: 'p'}, {id: 'p'}, {id: 'p'}, {id: 'p'}],
24
- # [nil, nil, nil, nil, nil, nil, nil, nil],
25
- # [nil, nil, nil, nil, nil, nil, nil, nil],
26
- # [nil, nil, nil, nil, nil, nil, nil, nil],
27
- # [nil, nil, nil, nil, nil, nil, nil, nil],
28
- # [{id: 'P'}, {id: 'P'}, {id: 'P'}, {id: 'P'}, {id: 'P'}, {id: 'P'}, {id: 'P'}, {id: 'P'}],
29
- # [{id: 'R'}, {id: 'N'}, {id: 'B'}, {id: 'Q'}, {id: 'K'}, {id: 'B'}, {id: 'N'}, {id: 'R'}]],
30
- # games_turn: {
31
- # active_player: 'CHESS',
32
- # inactive_player: 'chess',
33
- # uppercase_game: 'CHESS',
34
- # lowercase_game: 'chess'
35
- # },
36
- # pieces_in_hand: []
37
- # }
38
- # Feen.dump(position) # => "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR CHESS/chess -"
39
- def self.dump(position)
40
- Dumper.dump(position)
17
+ # piece_placement = [
18
+ # ["r", "n", "b", "q", "k=", "b", "n", "r"],
19
+ # ["p", "p", "p", "p", "p", "p", "p", "p"],
20
+ # ["", "", "", "", "", "", "", ""],
21
+ # ["", "", "", "", "", "", "", ""],
22
+ # ["", "", "", "", "", "", "", ""],
23
+ # ["", "", "", "", "", "", "", ""],
24
+ # ["P", "P", "P", "P", "P", "P", "P", "P"],
25
+ # ["R", "N", "B", "Q", "K=", "B", "N", "R"]
26
+ # ]
27
+ # Feen.dump(
28
+ # piece_placement: piece_placement,
29
+ # pieces_in_hand: [],
30
+ # games_turn: ["CHESS", "chess"]
31
+ # )
32
+ # # => "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR - CHESS/chess"
33
+ def self.dump(...)
34
+ Dumper.dump(...)
41
35
  end
42
36
 
43
- # Parses a FEEN string into position params.
37
+ # Parses a FEEN string into position components.
44
38
  #
45
- # @param feen_string [String] FEEN notation string
39
+ # @see Feen::Parser.parse for the detailed parameters and return value documentation
46
40
  # @return [Hash] Hash containing the parsed position data
47
41
  # @raise [ArgumentError] If the FEEN string is invalid
48
42
  # @example
49
- # feen_string = "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR CHESS/chess -"
43
+ # feen_string = "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR - CHESS/chess"
50
44
  # Feen.parse(feen_string)
51
45
  # # => {
52
- # # piece_placement: [[{id: 'r'}, {id: 'n'}, {id: 'b'}, {id: 'q'}, {id: 'k', suffix: '='}, {id: 'b'}, {id: 'n'}, {id: 'r'}],
53
- # # [{id: 'p'}, {id: 'p'}, {id: 'p'}, {id: 'p'}, {id: 'p'}, {id: 'p'}, {id: 'p'}, {id: 'p'}],
54
- # # [nil, nil, nil, nil, nil, nil, nil, nil],
55
- # # [nil, nil, nil, nil, nil, nil, nil, nil],
56
- # # [nil, nil, nil, nil, nil, nil, nil, nil],
57
- # # [nil, nil, nil, nil, nil, nil, nil, nil],
58
- # # [{id: 'P'}, {id: 'P'}, {id: 'P'}, {id: 'P'}, {id: 'P'}, {id: 'P'}, {id: 'P'}, {id: 'P'}],
59
- # # [{id: 'R'}, {id: 'N'}, {id: 'B'}, {id: 'Q'}, {id: 'K', suffix: '='}, {id: 'B'}, {id: 'N'}, {id: 'R'}]],
60
- # # games_turn: {
61
- # # active_player: 'CHESS',
62
- # # inactive_player: 'chess',
63
- # # uppercase_game: 'CHESS',
64
- # # lowercase_game: 'chess',
65
- # # active_player_casing: :uppercase
66
- # # },
67
- # # pieces_in_hand: []
46
+ # # piece_placement: [
47
+ # # ["r", "n", "b", "q", "k=", "b", "n", "r"],
48
+ # # ["p", "p", "p", "p", "p", "p", "p", "p"],
49
+ # # ["", "", "", "", "", "", "", ""],
50
+ # # ["", "", "", "", "", "", "", ""],
51
+ # # ["", "", "", "", "", "", "", ""],
52
+ # # ["", "", "", "", "", "", "", ""],
53
+ # # ["P", "P", "P", "P", "P", "P", "P", "P"],
54
+ # # ["R", "N", "B", "Q", "K=", "B", "N", "R"]
55
+ # # ],
56
+ # # pieces_in_hand: [],
57
+ # # games_turn: ["CHESS", "chess"]
68
58
  # # }
69
- def self.parse(feen_string)
70
- Parser.parse(feen_string)
59
+ def self.parse(...)
60
+ Parser.parse(...)
71
61
  end
72
62
 
73
63
  # Validates if the given string is a valid FEEN string
74
64
  #
75
- # @param feen_string [String] FEEN string to validate
65
+ # @see Feen.parse for parameter details
76
66
  # @return [Boolean] True if the string is a valid FEEN string, false otherwise
77
67
  # @example
78
- # Feen.valid?("rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR CHESS/chess -") # => true
68
+ # Feen.valid?("rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR - CHESS/chess") # => true
79
69
  # Feen.valid?("invalid feen string") # => false
80
- def self.valid?(feen_string)
81
- parse(feen_string)
70
+ def self.valid?(...)
71
+ parse(...)
82
72
  true
83
73
  rescue ::ArgumentError
84
74
  false
85
75
  end
86
-
87
- # Converts a FEN string to a FEEN string for chess positions
88
- #
89
- # @param fen_string [String] Standard FEN notation string for chess
90
- # @return [String] Equivalent FEEN notation string
91
- # @raise [ArgumentError] If the FEN string is invalid
92
- # @example
93
- # Feen.from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
94
- # # => "rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR CHESS/chess -"
95
- def self.from_fen(fen_string)
96
- Converter.from_fen(fen_string)
97
- end
98
-
99
- # Converts a FEEN string to a FEN string for chess positions
100
- #
101
- # @param feen_string [String] FEEN notation string
102
- # @return [String] Equivalent FEN notation string
103
- # @raise [ArgumentError] If the FEEN string is invalid
104
- # @example
105
- # Feen.to_fen("rnbqk=bnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK=BNR CHESS/chess -")
106
- # # => "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
107
- def self.to_fen(feen_string)
108
- Converter.to_fen(feen_string)
109
- end
110
76
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: feen
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0.beta2
4
+ version: 5.0.0.beta3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyril Kato
@@ -18,18 +18,21 @@ files:
18
18
  - LICENSE.md
19
19
  - README.md
20
20
  - lib/feen.rb
21
- - lib/feen/converter.rb
22
- - lib/feen/converter/from_fen.rb
23
- - lib/feen/converter/to_fen.rb
24
21
  - lib/feen/dumper.rb
25
22
  - lib/feen/dumper/games_turn.rb
26
23
  - lib/feen/dumper/piece_placement.rb
27
24
  - lib/feen/dumper/pieces_in_hand.rb
25
+ - lib/feen/dumper/pieces_in_hand/errors.rb
26
+ - lib/feen/dumper/pieces_in_hand/no_pieces.rb
28
27
  - lib/feen/parser.rb
29
28
  - lib/feen/parser/games_turn.rb
29
+ - lib/feen/parser/games_turn/errors.rb
30
+ - lib/feen/parser/games_turn/valid_games_turn_pattern.rb
30
31
  - lib/feen/parser/piece_placement.rb
31
32
  - lib/feen/parser/pieces_in_hand.rb
32
- - lib/feen/sanitizer.rb
33
+ - lib/feen/parser/pieces_in_hand/errors.rb
34
+ - lib/feen/parser/pieces_in_hand/no_pieces.rb
35
+ - lib/feen/parser/pieces_in_hand/valid_format_pattern.rb
33
36
  homepage: https://github.com/sashite/feen.rb
34
37
  licenses:
35
38
  - MIT
@@ -42,7 +45,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
42
45
  requirements:
43
46
  - - ">="
44
47
  - !ruby/object:Gem::Version
45
- version: 3.4.0
48
+ version: 3.2.0
46
49
  required_rubygems_version: !ruby/object:Gem::Requirement
47
50
  requirements:
48
51
  - - ">="