philiprehberger-string_kit 0.1.5 → 0.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: a101ff6299427781a2e36187c45ea562262a55e42ccbd16603914bd88d98e8db
4
- data.tar.gz: 191f68c44b3f23ee978204ca0826470ee5e7301c4e50742c1748e3b561940ccd
3
+ metadata.gz: 107a691dbf6789259b13f7186ea60b5cd1b026bfefc695e41dfe47a8244350e2
4
+ data.tar.gz: 1c211206ef60437262aee4bbabaf6f730460d99cd2f31306794ca60fdf51c9e9
5
5
  SHA512:
6
- metadata.gz: 25e22e494fc0b416cec981127cdd30b211a85ea18330d69fabec6487b95b8cc376013cfb56268cf2cc756a23c28ad3fc6d423dd421ff9b05ac382f27f2838ca0
7
- data.tar.gz: 225abafbcd6f1542197d745ba6cfb155007002449f8632c778b58d28eeb2bb627d38d08b22c1cdbe6c888fabc04fb6f3e67807325bbb7116953e0bfa8db6b05b
6
+ metadata.gz: 7997a306ea22f20b323d3c06ad8e6f2142905765f2b4b5a2c5312417f6300a7287a994469ff4dbf8071078a24b95ad4447326c4d552c98e02a3e34850b4b8cac
7
+ data.tar.gz: c0be14da1d59836b63530ff4cdc6ecab888791cb5c746719f5aa7d8e403b0e500bb3a00495016bbfddcbb0a34b61dd86271e7fb0be36816331158226c170b3c3
data/CHANGELOG.md CHANGED
@@ -7,6 +7,21 @@ and this gem adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.0] - 2026-04-03
11
+
12
+ ### Added
13
+ - Add `slug` method for URL-safe slug generation with custom separator support
14
+ - Add `pad` method for left, right, and center string padding
15
+ - Add `transliterate` method to replace accented/diacritical characters with ASCII equivalents
16
+ - Add `dot_case` method for dot.case conversion
17
+ - Add `path_case` method for path/case conversion
18
+ - Add `reverse_case` method to swap upper and lower case characters
19
+
20
+ ## [0.1.6] - 2026-03-31
21
+
22
+ ### Added
23
+ - Add GitHub issue templates, dependabot config, and PR template
24
+
10
25
  ## [0.1.5] - 2026-03-31
11
26
 
12
27
  ### Changed
data/README.md CHANGED
@@ -35,6 +35,26 @@ Philiprehberger::StringKit.camel_case('hello world') # => "helloWorld"
35
35
  Philiprehberger::StringKit.pascal_case('hello world') # => "HelloWorld"
36
36
  Philiprehberger::StringKit.snake_case('Hello World') # => "hello_world"
37
37
  Philiprehberger::StringKit.constant_case('hello world') # => "HELLO_WORLD"
38
+ Philiprehberger::StringKit.dot_case('SomeString') # => "some.string"
39
+ Philiprehberger::StringKit.path_case('SomeString') # => "some/string"
40
+ Philiprehberger::StringKit.reverse_case('Hello') # => "hELLO"
41
+ ```
42
+
43
+ ### Slug Generation and Transliteration
44
+
45
+ ```ruby
46
+ Philiprehberger::StringKit.slug('Hello World!') # => "hello-world"
47
+ Philiprehberger::StringKit.slug('Hello World!', separator: '_') # => "hello_world"
48
+ Philiprehberger::StringKit.transliterate('cr\u00E8me br\u00FBl\u00E9e') # => "creme brulee"
49
+ ```
50
+
51
+ ### String Padding
52
+
53
+ ```ruby
54
+ Philiprehberger::StringKit.pad('hi', 5) # => "hi "
55
+ Philiprehberger::StringKit.pad('hi', 5, side: :left) # => " hi"
56
+ Philiprehberger::StringKit.pad('hi', 6, side: :both) # => " hi "
57
+ Philiprehberger::StringKit.pad('hi', 5, char: '*') # => "hi***"
38
58
  ```
39
59
 
40
60
  ### Text Processing
@@ -65,6 +85,12 @@ Philiprehberger::StringKit.dedent(" hello\n world") # => "hello\nworl
65
85
  | `StringKit.pascal_case(str)` | Convert string to PascalCase |
66
86
  | `StringKit.snake_case(str)` | Convert string to snake_case |
67
87
  | `StringKit.constant_case(str)` | Convert string to CONSTANT_CASE |
88
+ | `StringKit.dot_case(str)` | Convert string to dot.case |
89
+ | `StringKit.path_case(str)` | Convert string to path/case |
90
+ | `StringKit.reverse_case(str)` | Swap upper and lower case characters |
91
+ | `StringKit.slug(str, separator:)` | Generate URL-safe slug |
92
+ | `StringKit.pad(str, length, char:, side:)` | Pad string to target length |
93
+ | `StringKit.transliterate(str)` | Replace accented characters with ASCII equivalents |
68
94
  | `StringKit.strip_html(str)` | Remove HTML tags from string |
69
95
  | `StringKit.normalize_whitespace(str)` | Collapse whitespace to single spaces |
70
96
  | `StringKit.word_count(str)` | Count words in string |
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module StringKit
5
- VERSION = '0.1.5'
5
+ VERSION = '0.2.0'
6
6
  end
7
7
  end
@@ -8,6 +8,53 @@ module Philiprehberger
8
8
 
9
9
  WORD_BOUNDARY = /[^a-zA-Z0-9\u00C0-\u024F]+/
10
10
 
11
+ TRANSLITERATION_MAP = {
12
+ "\u00C0" => 'A', "\u00C1" => 'A', "\u00C2" => 'A', "\u00C3" => 'A', "\u00C4" => 'A', "\u00C5" => 'A',
13
+ "\u00E0" => 'a', "\u00E1" => 'a', "\u00E2" => 'a', "\u00E3" => 'a', "\u00E4" => 'a', "\u00E5" => 'a',
14
+ "\u00C6" => 'AE', "\u00E6" => 'ae',
15
+ "\u00C7" => 'C', "\u00E7" => 'c',
16
+ "\u00C8" => 'E', "\u00C9" => 'E', "\u00CA" => 'E', "\u00CB" => 'E',
17
+ "\u00E8" => 'e', "\u00E9" => 'e', "\u00EA" => 'e', "\u00EB" => 'e',
18
+ "\u00CC" => 'I', "\u00CD" => 'I', "\u00CE" => 'I', "\u00CF" => 'I',
19
+ "\u00EC" => 'i', "\u00ED" => 'i', "\u00EE" => 'i', "\u00EF" => 'i',
20
+ "\u00D0" => 'D', "\u00F0" => 'd',
21
+ "\u00D1" => 'N', "\u00F1" => 'n',
22
+ "\u00D2" => 'O', "\u00D3" => 'O', "\u00D4" => 'O', "\u00D5" => 'O', "\u00D6" => 'O', "\u00D8" => 'O',
23
+ "\u00F2" => 'o', "\u00F3" => 'o', "\u00F4" => 'o', "\u00F5" => 'o', "\u00F6" => 'o', "\u00F8" => 'o',
24
+ "\u00D9" => 'U', "\u00DA" => 'U', "\u00DB" => 'U', "\u00DC" => 'U',
25
+ "\u00F9" => 'u', "\u00FA" => 'u', "\u00FB" => 'u', "\u00FC" => 'u',
26
+ "\u00DD" => 'Y', "\u00FD" => 'y', "\u00FF" => 'y',
27
+ "\u00DE" => 'Th', "\u00FE" => 'th',
28
+ "\u00DF" => 'ss',
29
+ "\u0100" => 'A', "\u0101" => 'a', "\u0102" => 'A', "\u0103" => 'a', "\u0104" => 'A', "\u0105" => 'a',
30
+ "\u0106" => 'C', "\u0107" => 'c', "\u0108" => 'C', "\u0109" => 'c', "\u010A" => 'C', "\u010B" => 'c',
31
+ "\u010C" => 'C', "\u010D" => 'c',
32
+ "\u010E" => 'D', "\u010F" => 'd', "\u0110" => 'D', "\u0111" => 'd',
33
+ "\u0112" => 'E', "\u0113" => 'e', "\u0114" => 'E', "\u0115" => 'e', "\u0116" => 'E', "\u0117" => 'e',
34
+ "\u0118" => 'E', "\u0119" => 'e', "\u011A" => 'E', "\u011B" => 'e',
35
+ "\u011C" => 'G', "\u011D" => 'g', "\u011E" => 'G', "\u011F" => 'g', "\u0120" => 'G', "\u0121" => 'g',
36
+ "\u0122" => 'G', "\u0123" => 'g',
37
+ "\u0124" => 'H', "\u0125" => 'h', "\u0126" => 'H', "\u0127" => 'h',
38
+ "\u0128" => 'I', "\u0129" => 'i', "\u012A" => 'I', "\u012B" => 'i', "\u012C" => 'I', "\u012D" => 'i',
39
+ "\u012E" => 'I', "\u012F" => 'i', "\u0130" => 'I', "\u0131" => 'i',
40
+ "\u0134" => 'J', "\u0135" => 'j',
41
+ "\u0136" => 'K', "\u0137" => 'k',
42
+ "\u0139" => 'L', "\u013A" => 'l', "\u013B" => 'L', "\u013C" => 'l', "\u013D" => 'L', "\u013E" => 'l',
43
+ "\u0141" => 'L', "\u0142" => 'l',
44
+ "\u0143" => 'N', "\u0144" => 'n', "\u0145" => 'N', "\u0146" => 'n', "\u0147" => 'N', "\u0148" => 'n',
45
+ "\u014C" => 'O', "\u014D" => 'o', "\u014E" => 'O', "\u014F" => 'o', "\u0150" => 'O', "\u0151" => 'o',
46
+ "\u0152" => 'OE', "\u0153" => 'oe',
47
+ "\u0154" => 'R', "\u0155" => 'r', "\u0156" => 'R', "\u0157" => 'r', "\u0158" => 'R', "\u0159" => 'r',
48
+ "\u015A" => 'S', "\u015B" => 's', "\u015C" => 'S', "\u015D" => 's', "\u015E" => 'S', "\u015F" => 's',
49
+ "\u0160" => 'S', "\u0161" => 's',
50
+ "\u0162" => 'T', "\u0163" => 't', "\u0164" => 'T', "\u0165" => 't', "\u0166" => 'T', "\u0167" => 't',
51
+ "\u0168" => 'U', "\u0169" => 'u', "\u016A" => 'U', "\u016B" => 'u', "\u016C" => 'U', "\u016D" => 'u',
52
+ "\u016E" => 'U', "\u016F" => 'u', "\u0170" => 'U', "\u0171" => 'u', "\u0172" => 'U', "\u0173" => 'u',
53
+ "\u0174" => 'W', "\u0175" => 'w',
54
+ "\u0176" => 'Y', "\u0177" => 'y', "\u0178" => 'Y',
55
+ "\u0179" => 'Z', "\u017A" => 'z', "\u017B" => 'Z', "\u017C" => 'z', "\u017D" => 'Z', "\u017E" => 'z'
56
+ }.freeze
57
+
11
58
  # Convert string to Title Case
12
59
  #
13
60
  # @param str [String]
@@ -157,6 +204,80 @@ module Philiprehberger
157
204
  lines.map { |line| line.length > min_indent ? line[min_indent..] : line.lstrip }.join
158
205
  end
159
206
 
207
+ # Generate a URL-safe slug
208
+ #
209
+ # @param str [String]
210
+ # @param separator [String] separator character (default: '-')
211
+ # @return [String]
212
+ def self.slug(str, separator: '-')
213
+ validate!(str)
214
+ result = transliterate(str)
215
+ result = result.downcase
216
+ result = result.gsub(/[^a-z0-9\s-]/, '')
217
+ result = result.strip.gsub(/[\s-]+/, separator)
218
+ result.gsub(/#{Regexp.escape(separator)}+/, separator)
219
+ end
220
+
221
+ # Pad string to target length
222
+ #
223
+ # @param str [String]
224
+ # @param length [Integer] target length
225
+ # @param char [String] padding character (default: ' ')
226
+ # @param side [Symbol] :left, :right, or :both (default: :right)
227
+ # @return [String]
228
+ def self.pad(str, length, char: ' ', side: :right)
229
+ validate!(str)
230
+ return str if str.length >= length
231
+
232
+ diff = length - str.length
233
+ case side
234
+ when :left
235
+ (char * diff) + str
236
+ when :both
237
+ left_pad = diff / 2
238
+ right_pad = diff - left_pad
239
+ (char * left_pad) + str + (char * right_pad)
240
+ else
241
+ str + (char * diff)
242
+ end
243
+ end
244
+
245
+ # Replace accented/diacritical characters with ASCII equivalents
246
+ #
247
+ # @param str [String]
248
+ # @return [String]
249
+ def self.transliterate(str)
250
+ validate!(str)
251
+ str.gsub(/[^\x00-\x7F]/) { |char| TRANSLITERATION_MAP.fetch(char, char) }
252
+ end
253
+
254
+ # Convert string to dot.case
255
+ #
256
+ # @param str [String]
257
+ # @return [String]
258
+ def self.dot_case(str)
259
+ validate!(str)
260
+ separate_words(str).join('.')
261
+ end
262
+
263
+ # Convert string to path/case
264
+ #
265
+ # @param str [String]
266
+ # @return [String]
267
+ def self.path_case(str)
268
+ validate!(str)
269
+ separate_words(str).join('/')
270
+ end
271
+
272
+ # Swap upper and lower case characters
273
+ #
274
+ # @param str [String]
275
+ # @return [String]
276
+ def self.reverse_case(str)
277
+ validate!(str)
278
+ str.swapcase
279
+ end
280
+
160
281
  class << self
161
282
  private
162
283
 
metadata CHANGED
@@ -1,17 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: philiprehberger-string_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Rehberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-31 00:00:00.000000000 Z
11
+ date: 2026-04-04 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: String case conversion, HTML stripping, whitespace normalization, word
14
- counting, reading time estimation, excerpt extraction, indentation, and more.
13
+ description: String case conversion, slug generation, transliteration, padding, HTML
14
+ stripping, whitespace normalization, word counting, reading time estimation, excerpt
15
+ extraction, indentation, and more.
15
16
  email:
16
17
  - me@philiprehberger.com
17
18
  executables: []
@@ -23,11 +24,11 @@ files:
23
24
  - README.md
24
25
  - lib/philiprehberger/string_kit.rb
25
26
  - lib/philiprehberger/string_kit/version.rb
26
- homepage: https://github.com/philiprehberger/rb-string-kit
27
+ homepage: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-string_kit
27
28
  licenses:
28
29
  - MIT
29
30
  metadata:
30
- homepage_uri: https://github.com/philiprehberger/rb-string-kit
31
+ homepage_uri: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-string_kit
31
32
  source_code_uri: https://github.com/philiprehberger/rb-string-kit
32
33
  changelog_uri: https://github.com/philiprehberger/rb-string-kit/blob/main/CHANGELOG.md
33
34
  bug_tracker_uri: https://github.com/philiprehberger/rb-string-kit/issues