obfuscator-rb 0.3.2 → 0.8.1

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: 2d62673f5b57f8d9a137fb1026beb43be38357f0f2ed7271f626f42a7649b94e
4
- data.tar.gz: d1c4396db23eb14973609b7bc90743dbaca5dc6c22b3460c4ceabf8e428732e3
3
+ metadata.gz: 7884592e18d5055a2d68e8cb40df90adc40a3408e331add6ee8afa7195c329bd
4
+ data.tar.gz: d24f0d673b56d638b87b6d86e23592b8043fdb765d8c9718e852f5b94e11b255
5
5
  SHA512:
6
- metadata.gz: c9df173ba1df90c84d2a4b4ab3d94e0f4ef89b42fb6233f099d05a0cb725d9253e9f2dcc3022d3baa7972980672f77423cf0a52e823c4d6cb3cdd43165ff7b07
7
- data.tar.gz: 2a04b22644bffdc49e88f6a4c691786d035f432a571ca60b7d7eb15364fb32969258b4b6a7379578702296dccd24c16237b1402b8485a8984ce03936c61e8943
6
+ metadata.gz: 5da8d928def751c287668898aa6b9026eab99a0c2d963ca6d5247a0681809e838e193f86451c5e091d9c21e529fa19c444703bed85cbf2f58525c61985322a0c
7
+ data.tar.gz: 9ea77d3bb0d6aa04a2cdfa0512c3b254bb22b7a61bffeaa15a8b090ed8950de36a77f5ea3b7335421b5901c8af11dbb69440bf66212eaaab9c6091d79d58f8ef
data/.rubocop.yml CHANGED
@@ -33,7 +33,7 @@ Layout/HashAlignment:
33
33
  EnforcedColonStyle: table
34
34
 
35
35
  Metrics/ClassLength:
36
- Max: 140
36
+ Max: 250
37
37
  Exclude:
38
38
  - "test/*"
39
39
 
@@ -44,7 +44,7 @@ Metrics/MethodLength:
44
44
  Max: 60
45
45
 
46
46
  Metrics/AbcSize:
47
- Max: 40
47
+ Max: 45
48
48
  Exclude:
49
49
  - "test/*"
50
50
 
@@ -53,3 +53,15 @@ Metrics/PerceivedComplexity:
53
53
 
54
54
  Metrics/CyclomaticComplexity:
55
55
  Max: 30
56
+
57
+ Minitest/MultipleAssertions:
58
+ Max: 6
59
+
60
+ Performance/ChainArrayAllocation:
61
+ Enabled: true
62
+
63
+ Style/MultilineBlockChain:
64
+ Enabled: true
65
+
66
+ Performance/IoReadlines:
67
+ Enabled: true
data/CHANGELOG.md CHANGED
@@ -4,6 +4,96 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.8.1] - 2024-02-23
8
+
9
+ ### Changed
10
+ - Updated RELEASE_CHECKLIST.md with Git workflow guide
11
+
12
+ ## [0.8.0] - 2024-02-23
13
+
14
+ ### Changed
15
+ - Enhanced top-level documentation with comprehensive NumberObfuscator examples
16
+ - Improved bilingual documentation symmetry in README
17
+ - Updated gem description to better reflect all features
18
+ - Made thread safety warnings more prominent
19
+
20
+ ### Fixed
21
+ - Corrected and standardized examples across both languages in README
22
+ - Fixed missing US format examples in Russian documentation
23
+ - Fixed missing short format examples in English documentation
24
+ - Synchronized error handling examples between languages
25
+
26
+ ## [0.7.0] - 2024-02-22
27
+
28
+ ### Changed
29
+ - Clarified thread safety requirements in documentation
30
+ - Updated gem description to better reflect current capabilities
31
+ - Enhanced documentation with bilingual thread safety guidelines
32
+
33
+ ### Fixed
34
+ - Corrected misleading thread safety claims in documentation
35
+ - Updated examples to show proper instance usage in concurrent scenarios
36
+ - Fixed DateObfuscator examples to match actual implementation
37
+
38
+ ## [0.6.0] - 2024-02-22
39
+
40
+ ### Added
41
+ - Support for IP-like number sequences (e.g., "21.11.234.23")
42
+ - Configurable unsigned mode for NumberObfuscator
43
+ - Improved decimal precision handling in float obfuscation
44
+
45
+ ### Fixed
46
+ - Resolved repeating patterns in seeded number generation
47
+ - Fixed decimal places preservation in float obfuscation
48
+ - Corrected handling of leading zeros with format preservation
49
+ - Fixed sign handling in unsigned mode
50
+
51
+ ### Changed
52
+ - Enhanced number generation algorithm for better randomization
53
+ - Improved format preservation for complex number patterns
54
+ - Optimized RNG state management for consistent results
55
+
56
+ ## [0.5.0] - 2024-02-21
57
+
58
+ ### Added
59
+ - Performance optimizations for NumberObfuscator with large numbers
60
+ - Added stress test mode for extreme number handling
61
+ - Improved test coverage for edge cases in number obfuscation
62
+ - Support for US date formats in DateObfuscator
63
+ - ISO full datetime format support
64
+
65
+ ### Changed
66
+ - Adjusted number magnitude checks in tests for better reliability
67
+ - Made extreme number tests optional via STRESS_TEST environment variable
68
+ - Enhanced DateObfuscator format presets
69
+
70
+ ### Fixed
71
+ - Performance issues with Float::MAX/MIN handling in NumberObfuscator
72
+ - DateObfuscator now properly handles seeds with different input dates
73
+ - Added combined seed calculation to ensure unique but reproducible results for different dates
74
+ - Added test coverage for seed behavior with different inputs
75
+
76
+ ## [0.4.0] - 2024-02-21
77
+
78
+ ### Added
79
+ - NumberObfuscator class for handling numeric content
80
+ - Support for Cyrillic alphabet in mixed content with numbers
81
+ - Format preservation for decimal and thousand separators
82
+ - Leading zeros handling with configuration option
83
+ - UTF-8 encoding support for mixed content
84
+ - Improved number format preservation
85
+
86
+ ### Fixed
87
+ - Float decimal places preservation
88
+ - Character range issues with Cyrillic letters
89
+ - Length preservation in formatted numbers
90
+ - Mixed content handling with Unicode properties
91
+
92
+ ### Changed
93
+ - Enhanced documentation with bilingual number obfuscation examples
94
+ - More robust number format preservation
95
+ - Improved mixed content handling
96
+
7
97
  ## [0.3.2] - 2025-02-07
8
98
  ### Added
9
99
  - Test coverage for the Naturalizer class core functionality
data/README.md CHANGED
@@ -6,15 +6,18 @@
6
6
 
7
7
  ## Русский
8
8
 
9
- Ruby-гем для обфускации текста. Сохраняет структуру, заменяя содержимое бессмысленными словами, сохраняющими при этом
10
- естественный вид исходного текста. Поддерживает русский и английский языки.
9
+ Ruby-гем для обфускации данных с сохранением их структуры и формата. Поддерживает:
10
+ - Текст на русском и английском языках
11
+ - Числа (включая IP-адреса и смешанный контент)
12
+ - Даты в различных форматах
13
+ - Воспроизводимые результаты через seed
11
14
 
12
15
  ### Установка
13
16
 
14
17
  Добавьте эту строку в Gemfile вашего приложения:
15
18
 
16
19
  ```ruby
17
- gem 'obfuscator-rb', git: 'https://hub.mos.ru/ad/obfuscator.git'
20
+ gem 'obfuscator-rb'
18
21
  ```
19
22
 
20
23
  И выполните:
@@ -29,23 +32,53 @@ $ bundle install
29
32
  $ gem install obfuscator-rb
30
33
  ```
31
34
 
35
+ Установка из репозитория [hub.mos.ru](https://hub.mos.ru/ad/obfuscator):
36
+
37
+ ```bash
38
+ $ gem install obfuscator-rb --source https://hub.mos.ru/ad/obfuscator.git
39
+ ```
40
+
41
+ Код также доступен на [GitHub](https://github.com/amdest/obfuscator) и [Gitverse](https://gitverse.ru/ad-it/obfuscator).
42
+
32
43
  ### Возможности
33
44
 
34
- - Сохраняет структуру текста (пунктуация, пробелы, регистр)
35
- - Сохраняет длину слов (если не включена натурализация)
36
- - Поддерживает несколько режимов обфускации
37
- - Опциональная натурализация текста по некоторым простым правилам
38
- - Необратимая трансформация текста
39
- - Детерминированный вывод при использовании сида
40
- - Полная поддержка UTF-8
41
- - Обеспечена обработка ошибок определённых типов данных
45
+ #### Обфускация чисел
46
+ - Сохраняет формат и структуру чисел (десятичные, тысячные разделители)
47
+ - Обрабатывает IP-подобные последовательности (например, "21.11.234.23")
48
+ - Поддерживает сохранение ведущих нулей (настраиваемо)
49
+ - Предоставляет режим для работы только с положительными числами
50
+ - Сохраняет точность десятичных дробей
51
+ - Обрабатывает смешанный контент с числами и текстом
52
+ - Поддерживает латинский и кириллический алфавиты
53
+
54
+ #### Обфускация дат
55
+ - Поддерживает множество форматов дат через пресеты (ISO, EU, US, русский)
56
+ - Сохраняет структуру и валидность формата
57
+ - Настраиваемые ограничения:
58
+ - Диапазон допустимых лет
59
+ - Сохранение месяца
60
+ - Сохранение дня недели
61
+ - Обрабатывает полный формат ISO с временной зоной
62
+
63
+ #### Общие возможности
64
+ - Детерминированный вывод с опциональным сидом
65
+ - Обфускация с сохранением формата
66
+ - Поддержка кодировки UTF-8
67
+ - Комплексная обработка ошибок
68
+ - Эффективное использование памяти
69
+
70
+ #### Многопоточность
71
+ Отдельные экземпляры обфускаторов не являются потокобезопасными. Для многопоточных операций:
72
+ - Создавайте отдельный экземпляр для каждого потока
73
+ - Не используйте один экземпляр в разных потоках
74
+ - Каждый экземпляр поддерживает свое собственное состояние RNG
42
75
 
43
76
  ### Использование
44
77
 
45
78
  ```ruby
46
79
  require 'obfuscator-rb'
47
80
 
48
- # Базовое использование (режим :direct по умолчанию)
81
+ # Обфускация текста (режим :direct по умолчанию)
49
82
  obfuscator = Obfuscator::Multilang.new
50
83
  text = "Hello, Привет! This is a TEST текст."
51
84
  result = obfuscator.obfuscate(text)
@@ -69,6 +102,51 @@ obfuscator = Obfuscator::Multilang.new(seed: 12345)
69
102
  result = obfuscator.obfuscate(text)
70
103
  # => Одинаковый ввод + одинаковый сид = одинаковый вывод
71
104
  # => "Cumic, Фяцёне! Okac ub h POWO щюзёс."
105
+
106
+ # Обфускация чисел
107
+ obfuscator = Obfuscator::NumberObfuscator.new
108
+ obfuscator.obfuscate(123.45) # => 567.89
109
+ obfuscator.obfuscate("1 234,56") # => "5 678,91"
110
+ obfuscator.obfuscate("192.168.1.1") # => "234.567.8.9"
111
+
112
+ # С настройками
113
+ obfuscator = Obfuscator::NumberObfuscator.new(
114
+ preserve_leading_zeros: false, # Не сохранять ведущие нули
115
+ unsigned: true, # Убрать знаки минус
116
+ seed: 12345 # Для воспроизводимых результатов
117
+ )
118
+ obfuscator.obfuscate("0042") # => "7391"
119
+ obfuscator.obfuscate("-123") # => "456"
120
+
121
+ # Обфускация даты
122
+ obfuscator = Obfuscator::DateObfuscator.new
123
+ obfuscator.obfuscate('2024-03-15') # => "2025-08-23"
124
+
125
+ # Разные форматы дат
126
+ eu = Obfuscator::DateObfuscator.new(format: :eu) # Европейский формат
127
+ eu.obfuscate('15.03.2024') # => "23.08.2025"
128
+
129
+ us = Obfuscator::DateObfuscator.new(format: :us) # Американский формат
130
+ us.obfuscate('03/15/2024') # => "08/23/2025"
131
+
132
+ rus_short = Obfuscator::DateObfuscator.new(format: :rus_short) # Короткий формат
133
+ rus_short.obfuscate('15.03.24') # => "23.08.25"
134
+
135
+ # С ограничениями
136
+ constrained = Obfuscator::DateObfuscator.new(
137
+ format: :rus,
138
+ constraints: {
139
+ min_year: 2020, # Не генерировать даты раньше 2020
140
+ max_year: 2025, # Не генерировать даты позже 2025
141
+ preserve_month: true, # Сохранять месяц
142
+ preserve_weekday: true # Сохранять день недели
143
+ }
144
+ )
145
+ constrained.obfuscate('15.03.2024') # => "14.03.2025" (тот же месяц, тот же день недели)
146
+
147
+ # С сидом для воспроизводимых результатов
148
+ seeded = Obfuscator::DateObfuscator.new(format: :rus, seed: 12345)
149
+ seeded.obfuscate('15.03.2024') # => "21.07.2025" (постоянный результат)
72
150
  ```
73
151
 
74
152
  ### Доступные режимы
@@ -77,30 +155,24 @@ result = obfuscator.obfuscate(text)
77
155
  - `:eng_to_eng` - только английский в английский
78
156
  - `:rus_to_rus` - только русский в русский
79
157
  - `:swapped` - английский в русский и наоборот
80
- - `:mixed` - использует оба алфавита (просто ради прикола)
81
-
82
- ### Обработка входных данных
83
-
84
- Обфускатор обрабатывает разлличные типы данных:
158
+ - `:mixed` - использует оба алфавита
85
159
 
86
- - `nil` возвращает nil
87
- - Числа → возвращаются без изменений
88
- - Объекты с методом `to_s` → обрабатываются нормально
89
- - Объекты без базовых методов Ruby → вызывают `InputError`
90
- - Неверные кодировки → вызывают `EncodingError`
160
+ ### Доступные пресеты для даты
91
161
 
92
- #### Типы ошибок
162
+ - `:eu` => `'%d.%m.%Y'` - европейский формат (31.12.2023)
163
+ - `:eu_short` => `'%d.%m.%y'` - короткий европейский формат (31.12.23)
164
+ - `:rus` => `'%d.%m.%Y'` - русский формат (31.12.2023)
165
+ - `:rus_short` => `'%d.%m.%y'` - короткий русский формат (31.12.23)
166
+ - `:iso` => `'%Y-%m-%d'` - формат ISO (2023-12-31)
167
+ - `:us` => `'%m/%d/%Y'` - американский формат (12/31/2023)
168
+ - `:us_short` => `'%m/%d/%y'` - короткий американский формат (12/31/23)
169
+ - `:iso_full` => `'%Y-%m-%dT%H:%M:%S%z'` - полный формат ISO (2023-12-31T00:00:00+00:00)
93
170
 
94
- - `Obfuscator::Error` - Базовый класс ошибок гема
95
- - `Obfuscator::InputError` - Возникает при неверном типе входных данных
96
- - `Obfuscator::EncodingError` - Возникает при проблемах с кодировкой
97
-
98
- #### Пример использования с обработкой ошибок
171
+ ### Обработка ошибок
99
172
 
100
173
  ```ruby
101
-
102
174
  begin
103
- obfuscator.obfuscate(текст)
175
+ obfuscator.obfuscate(text)
104
176
  rescue Obfuscator::InputError => e
105
177
  # Обработка неверного типа входных данных
106
178
  puts "Неверный тип данных: #{e.message}"
@@ -115,15 +187,18 @@ end
115
187
 
116
188
  ## English
117
189
 
118
- A Ruby gem for text obfuscation that preserves text structure while replacing content with meaningless but
119
- natural-looking words. Supports both English and Russian languages.
190
+ A Ruby gem for data obfuscation that preserves structure and format. Supports:
191
+ - Text in both English and Russian
192
+ - Numbers (including IP addresses and mixed content)
193
+ - Dates in various formats
194
+ - Reproducible results via seeding
120
195
 
121
196
  ### Installation
122
197
 
123
198
  Add this line to your application's Gemfile:
124
199
 
125
200
  ```ruby
126
- gem 'obfuscator-rb', git: 'https://hub.mos.ru/ad/obfuscator.git'
201
+ gem 'obfuscator-rb'
127
202
  ```
128
203
 
129
204
  And then execute:
@@ -138,23 +213,53 @@ Or install it yourself as:
138
213
  $ gem install obfuscator-rb
139
214
  ```
140
215
 
216
+ Install from [hub.mos.ru](https://hub.mos.ru/ad/obfuscator):
217
+
218
+ ```bash
219
+ $ gem install obfuscator-rb --source https://hub.mos.ru/ad/obfuscator.git
220
+ ```
221
+
222
+ The code is also available on [GitHub](https://github.com/amdest/obfuscator) and [Gitverse](https://gitverse.ru/ad-it/obfuscator).
223
+
141
224
  ### Features
142
225
 
143
- - Preserves text structure (punctuation, spacing, capitalization)
144
- - Maintains word lengths (unless naturalization is enabled)
145
- - Supports multiple obfuscation modes
146
- - Optional text naturalization according to some basic rules
147
- - Irreversible transformation
148
- - Deterministic output with seeds
149
- - Full UTF-8 support
150
- - Comprehensive error handling with specific error types
226
+ #### Number Obfuscation
227
+ - Preserves number format and structure (decimals, thousand separators)
228
+ - Handles IP-like sequences (e.g., "21.11.234.23")
229
+ - Supports leading zeros preservation (configurable)
230
+ - Provides unsigned mode for positive-only output
231
+ - Maintains consistent decimal precision
232
+ - Processes mixed content with numbers and text
233
+ - Supports both Latin and Cyrillic alphabets
234
+
235
+ #### Date Obfuscation
236
+ - Supports multiple date formats through presets (ISO, EU, US, Russian)
237
+ - Preserves format structure and validity
238
+ - Configurable constraints:
239
+ - Year range limits
240
+ - Month preservation
241
+ - Weekday preservation
242
+ - Handles full ISO datetime format with timezone
243
+
244
+ #### General Features
245
+ - Deterministic output with optional seeding
246
+ - Format-preserving obfuscation
247
+ - UTF-8 encoding support
248
+ - Comprehensive error handling
249
+ - Memory-efficient processing
250
+
251
+ #### Thread Safety
252
+ Individual obfuscator instances are NOT thread-safe. For concurrent operations:
253
+ - Create separate instances per thread
254
+ - Do not share instances across threads
255
+ - Each instance maintains its own RNG state
151
256
 
152
257
  ### Usage
153
258
 
154
259
  ```ruby
155
260
  require 'obfuscator-rb'
156
261
 
157
- # Basic usage (default :direct mode)
262
+ # Text obfuscation (default :direct mode)
158
263
  obfuscator = Obfuscator::Multilang.new
159
264
  text = "Hello, Привет! This is a TEST текст."
160
265
  result = obfuscator.obfuscate(text)
@@ -178,6 +283,51 @@ obfuscator = Obfuscator::Multilang.new(seed: 12345)
178
283
  result = obfuscator.obfuscate(text)
179
284
  # => Same input + same seed = same output
180
285
  # => "Cumic, Фяцёне! Okac ub h POWO щюзёс."
286
+
287
+ # Number obfuscation
288
+ obfuscator = Obfuscator::NumberObfuscator.new
289
+ obfuscator.obfuscate(123.45) # => 567.89
290
+ obfuscator.obfuscate("1,234.56") # => "5,678.91"
291
+ obfuscator.obfuscate("192.168.1.1") # => "234.567.8.9"
292
+
293
+ # With configuration
294
+ obfuscator = Obfuscator::NumberObfuscator.new(
295
+ preserve_leading_zeros: false, # Don't keep leading zeros
296
+ unsigned: true, # Remove minus signs
297
+ seed: 12345 # For reproducible results
298
+ )
299
+ obfuscator.obfuscate("0042") # => "7391"
300
+ obfuscator.obfuscate("-123") # => "456"
301
+
302
+ # Date obfuscation
303
+ obfuscator = Obfuscator::DateObfuscator.new
304
+ obfuscator.obfuscate('2024-03-15') # => "2025-08-23"
305
+
306
+ # Different date formats
307
+ eu = Obfuscator::DateObfuscator.new(format: :eu) # European format
308
+ eu.obfuscate('15.03.2024') # => "23.08.2025"
309
+
310
+ us = Obfuscator::DateObfuscator.new(format: :us) # US format
311
+ us.obfuscate('03/15/2024') # => "08/23/2025"
312
+
313
+ rus_short = Obfuscator::DateObfuscator.new(format: :rus_short) # Short format
314
+ rus_short.obfuscate('15.03.24') # => "23.08.25"
315
+
316
+ # With constraints
317
+ constrained = Obfuscator::DateObfuscator.new(
318
+ format: :iso,
319
+ constraints: {
320
+ min_year: 2020, # Don't generate dates before 2020
321
+ max_year: 2025, # Don't generate dates after 2025
322
+ preserve_month: true, # Keep the same month
323
+ preserve_weekday: true # Keep the same day of week
324
+ }
325
+ )
326
+ constrained.obfuscate('2024-03-15') # => "2025-03-14" (same month, same weekday)
327
+
328
+ # With seed for reproducible results
329
+ seeded = Obfuscator::DateObfuscator.new(seed: 12345)
330
+ seeded.obfuscate('2024-03-15') # => "2025-07-21" (consistent output)
181
331
  ```
182
332
 
183
333
  ### Available Modes
@@ -186,39 +336,33 @@ result = obfuscator.obfuscate(text)
186
336
  - `:eng_to_eng` - English to English only
187
337
  - `:rus_to_rus` - Russian to Russian only
188
338
  - `:swapped` - English to Russian and vice versa
189
- - `:mixed` - uses both alphabets (just for fun)
190
-
191
- ### Input Handling
339
+ - `:mixed` - uses both alphabets
192
340
 
193
- The obfuscator handles various input types:
341
+ ### Available Date Presets
194
342
 
195
- - `nil` returns nil
196
- - Numbers returns unchanged
197
- - Objects responding to `to_s` processes normally
198
- - Objects without basic Ruby methods raises `InputError`
199
- - Invalid encodings raises `EncodingError`
343
+ - `:eu` => `'%d.%m.%Y'` - European format (31.12.2023)
344
+ - `:eu_short` => `'%d.%m.%y'` - Short European format (31.12.23)
345
+ - `:rus` => `'%d.%m.%Y'` - Russian format (31.12.2023)
346
+ - `:rus_short` => `'%d.%m.%y'` - Short Russian format (31.12.23)
347
+ - `:iso` => `'%Y-%m-%d'` - ISO format (2023-12-31)
348
+ - `:us` => `'%m/%d/%Y'` - US format (12/31/2023)
349
+ - `:us_short` => `'%m/%d/%y'` - Short US format (12/31/23)
350
+ - `:iso_full` => `'%Y-%m-%dT%H:%M:%S%z'` - Full ISO format (2023-12-31T00:00:00+00:00)
200
351
 
201
- #### Error Types
202
-
203
- - `Obfuscator::Error` - Base error class for the gem
204
- - `Obfuscator::InputError` - Raised for invalid input types
205
- - `Obfuscator::EncodingError` - Raised for encoding-related issues
206
-
207
- #### Example Usage with Error Handling
352
+ ### Error Handling
208
353
 
209
354
  ```ruby
210
-
211
355
  begin
212
356
  obfuscator.obfuscate(text)
213
357
  rescue Obfuscator::InputError => e
214
358
  # Handle invalid input types
215
- puts "Invalid input: #{e.message}"
359
+ puts "Invalid input type: #{e.message}"
216
360
  rescue Obfuscator::EncodingError => e
217
361
  # Handle encoding issues
218
362
  puts "Encoding error: #{e.message}"
219
363
  rescue Obfuscator::Error => e
220
364
  # Handle other obfuscation errors
221
- puts "Obfuscation failed: #{e.message}"
365
+ puts "Obfuscation error: #{e.message}"
222
366
  end
223
367
  ```
224
368
 
@@ -0,0 +1,192 @@
1
+ # Release Checklist
2
+
3
+ ## Initial Setup
4
+
5
+ ### RubyGems Configuration
6
+
7
+ #### Option 1: Credentials File (Recommended)
8
+ 1. Create/edit `~/.gem/credentials`:
9
+ ```yaml
10
+ ---
11
+ :rubygems_api_key: your_api_key_here
12
+ ```
13
+ 2. Set proper permissions:
14
+ ```bash
15
+ chmod 600 ~/.gem/credentials
16
+ ```
17
+
18
+ #### Option 2: Environment Variable
19
+ Set `RUBYGEMS_API_KEY` environment variable:
20
+ ```bash
21
+ export RUBYGEMS_API_KEY=your_api_key_here
22
+ ```
23
+
24
+ ### First-time Push with MFA
25
+ 1. Get your MFA code ready
26
+ 2. Push the gem:
27
+ ```bash
28
+ gem push pkg/obfuscator-rb-X.Y.Z.gem --otp YOUR_MFA_CODE
29
+ ```
30
+
31
+ ### Common RubyGems CLI commands
32
+ ```bash
33
+ # Build gem with the gemspec file
34
+ gem build obfuscator-rb.gemspec # output: obfuscator-rb-X.Y.Z.gem
35
+
36
+ # Build gem with the Rakefile
37
+ rake build # output: pkg/obfuscator-rb-X.Y.Z.gem
38
+
39
+ # Push a new version of the gem
40
+ gem push pkg/obfuscator-rb-X.Y.Z.gem --otp YOUR_MFA_CODE
41
+
42
+ # Yank a version of the gem
43
+ gem yank obfuscator-rb -v X.Y.Z --otp YOUR_MFA_CODE
44
+
45
+ # List owned gems
46
+ gem list -r obfuscator-rb
47
+
48
+ # Check gem details
49
+ gem info obfuscator-rb
50
+
51
+ # Install a specific version of the gem locally
52
+ gem install ./obfuscator-rb-X.Y.Z.gem
53
+ ```
54
+
55
+ ## Pre-release Checks
56
+
57
+ ### 1. Code Quality
58
+ - [ ] Run full test suite:
59
+ ```bash
60
+ bundle exec rake test
61
+ ```
62
+ - [ ] Run Rubocop:
63
+ ```bash
64
+ bundle exec rubocop
65
+ ```
66
+
67
+ ### 2. Version Update
68
+ - [ ] Update version in `lib/obfuscator/version.rb`
69
+ - [ ] Update CHANGELOG.md
70
+ - Add new version section
71
+ - Document all changes under appropriate categories
72
+ - Add release date
73
+
74
+ ### 3. Local Build & Test
75
+ - [ ] Build gem using Rake (preferred):
76
+ ```bash
77
+ bundle exec rake build
78
+ # Gem will be in pkg/obfuscator-rb-X.Y.Z.gem
79
+ ```
80
+ OR build directly:
81
+ ```bash
82
+ gem build obfuscator-rb.gemspec
83
+ ```
84
+ - [ ] Install and test locally:
85
+ ```bash
86
+ gem install pkg/obfuscator-rb-X.Y.Z.gem
87
+ # OR if built directly:
88
+ # gem install ./obfuscator-rb-X.Y.Z.gem
89
+ ```
90
+ - [ ] Verify in IRB:
91
+ ```ruby
92
+ require 'obfuscator-rb'
93
+ # Test basic functionality
94
+ ```
95
+
96
+ ### 4. Documentation Check
97
+ - [ ] Review gemspec
98
+ - Version number
99
+ - Dependencies
100
+ - Metadata
101
+ - [ ] Check README is up to date
102
+ - [ ] Verify YARD documentation if changed
103
+
104
+ ## Release Process
105
+
106
+ ### 5. Version Control
107
+ - [ ] Commit changes:
108
+ ```bash
109
+ git add .
110
+ git commit -m "feat: release X.Y.Z"
111
+ ```
112
+ - [ ] Create version tag:
113
+ ```bash
114
+ git tag -a vX.Y.Z -m "Release X.Y.Z"
115
+ ```
116
+ - [ ] Push changes and tags:
117
+ ```bash
118
+ git push origin master
119
+ git push origin vX.Y.Z
120
+ ```
121
+
122
+ ### 6. Publish
123
+ - [ ] Push to RubyGems:
124
+ ```bash
125
+ gem push pkg/obfuscator-rb-X.Y.Z.gem --otp YOUR_MFA_CODE
126
+ ```
127
+ - [ ] Verify gem page on RubyGems.org
128
+
129
+ ## Post-release
130
+ - [ ] Clean up local gem files:
131
+ ```bash
132
+ rm -rf pkg/
133
+ # OR if built directly:
134
+ # rm obfuscator-rb-*.gem
135
+ ```
136
+ - [ ] Announce release if significant
137
+ - [ ] Update any related documentation or wiki pages
138
+
139
+ # Git Workflow Guide
140
+
141
+ ## Branch Structure
142
+ - `master`: Release branch, contains only tagged releases
143
+ - `dev`: Main development branch (local only)
144
+ - Feature branches: Created from and merged back to `dev`
145
+
146
+ ## Regular Development Flow
147
+ 1. Start work from dev:
148
+ ```bash
149
+ git checkout dev
150
+ git pull origin master # sync with latest release if needed
151
+ git checkout -b feat/my-feature
152
+ ```
153
+
154
+ 2. Make changes and commit:
155
+ ```bash
156
+ git add .
157
+ git commit -m "feat: description"
158
+ ```
159
+
160
+ 3. Merge back to dev:
161
+ ```bash
162
+ git checkout dev
163
+ git merge feat/my-feature
164
+ git branch -d feat/my-feature
165
+ ```
166
+
167
+ ## Release Process
168
+ 1. Prepare release in dev:
169
+ ```bash
170
+ git checkout dev
171
+ # Update version and CHANGELOG
172
+ git commit -m "chore: prepare release X.Y.Z"
173
+ ```
174
+
175
+ 2. Create release:
176
+ ```bash
177
+ git checkout master
178
+ git merge --squash dev # squash all dev commits
179
+ git commit -m "release: version X.Y.Z"
180
+ git tag -a vX.Y.Z -m "version X.Y.Z"
181
+ ```
182
+
183
+ 3. Push release:
184
+ ```bash
185
+ git push origin master --tags
186
+ ```
187
+
188
+ 4. Update dev:
189
+ ```bash
190
+ git checkout dev
191
+ git merge master # keep dev in sync with latest release
192
+ ```
@@ -38,6 +38,9 @@ module Obfuscator
38
38
  # - :rus => '%d.%m.%Y' # 31.12.2023
39
39
  # - :rus_short => '%d.%m.%y' # 31.12.23
40
40
  # - :iso => '%Y-%m-%d' # 2023-12-31
41
+ # - :us => '%m/%d/%Y' # 12/31/2023
42
+ # - :us_short => '%m/%d/%y' # 12/31/23
43
+ # - :iso_full => '%Y-%m-%dT%H:%M:%S%z' # 2023-12-31T00:00:00+00:00
41
44
  #
42
45
  # @param format [Symbol, String] Preset format name or custom format string (default: :iso)
43
46
  # @param seed [Integer, nil] Optional seed for reproducible results
@@ -56,7 +59,10 @@ module Obfuscator
56
59
  eu_short: '%d.%m.%y', # 31.12.23
57
60
  rus: '%d.%m.%Y', # 31.12.2023
58
61
  rus_short: '%d.%m.%y', # 31.12.23
59
- iso: '%Y-%m-%d' # 2023-12-31
62
+ iso: '%Y-%m-%d', # 2023-12-31
63
+ us: '%m/%d/%Y', # 12/31/2023
64
+ us_short: '%m/%d/%y', # 12/31/23
65
+ iso_full: '%Y-%m-%dT%H:%M:%S%z' # 2023-12-31T00:00:00+00:00
60
66
  }.freeze
61
67
 
62
68
  def initialize(format: :iso, seed: nil, constraints: {})
@@ -67,13 +73,17 @@ module Obfuscator
67
73
  end
68
74
 
69
75
  def obfuscate(date_string)
70
- # Reset RNG state before each obfuscation if seed was provided
71
- setup_rng(@seed) if @seed
72
-
73
- return date_string if date_string.nil? || date_string.empty?
76
+ return date_string if !date_string.is_a?(Date) && (date_string.nil? || date_string.empty?)
74
77
 
75
78
  begin
76
- date = ::Date.strptime(date_string, @format)
79
+ date = date_string.is_a?(Date) ? date_string : ::Date.strptime(date_string, @format)
80
+
81
+ # Create a unique seed based on both the original seed and input date
82
+ if @seed
83
+ combined_seed = [@seed, date.to_time.to_i].hash
84
+ setup_rng(combined_seed)
85
+ end
86
+
77
87
  obfuscated_date = generate_date(date)
78
88
  obfuscated_date.strftime(@format)
79
89
  rescue ArgumentError => e
@@ -32,6 +32,9 @@ module Obfuscator
32
32
  end
33
33
 
34
34
  def random_sample(array)
35
+ raise ArgumentError, "Expected Array, got #{array.class}" unless array.is_a?(Array)
36
+ raise ArgumentError, 'Array cannot be empty' if array.empty?
37
+
35
38
  array.sample(random: @rng)
36
39
  end
37
40
 
@@ -40,6 +43,9 @@ module Obfuscator
40
43
  end
41
44
 
42
45
  def random_range(range)
46
+ raise ArgumentError, "Expected Range, got #{range.class}" unless range.is_a?(Range)
47
+ raise ArgumentError, 'Range cannot be infinite' if range.end.nil?
48
+
43
49
  @rng.rand(range)
44
50
  end
45
51
  end
@@ -0,0 +1,323 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Obfuscator
4
+ # Class for obfuscating numbers while preserving their format and structure.
5
+ # Handles pure numbers (integers, floats), formatted strings, and mixed content
6
+ # including multilingual text (Latin and Cyrillic alphabets).
7
+ #
8
+ # @example Basic usage with numbers
9
+ # obfuscator = NumberObfuscator.new
10
+ # obfuscator.obfuscate(123.45) # => 567.89
11
+ # obfuscator.obfuscate(-42) # => -73
12
+ #
13
+ # @example With string numbers and leading zeros
14
+ # obfuscator.obfuscate("0042") # => "0073"
15
+ # obfuscator.obfuscate("00123.40") # => "00567.80"
16
+ #
17
+ # @example With formatted numbers
18
+ # obfuscator.obfuscate("1 234,56") # => "5 678,91"
19
+ # obfuscator.obfuscate("1_000_000") # => "5_678_912"
20
+ #
21
+ # @example With mixed content (Latin and Cyrillic)
22
+ # obfuscator.obfuscate("ABC-42XY") # => "DEF-73ZW"
23
+ # obfuscator.obfuscate("АБВ-42ЩЮ") # => "ГДЕ-73ЖЗ"
24
+ # obfuscator.obfuscate("21.11.234.23") # => "65.87.891.45"
25
+ #
26
+ # @example With options
27
+ # # Don't preserve leading zeros
28
+ # obfuscator = NumberObfuscator.new(preserve_leading_zeros: false)
29
+ # obfuscator.obfuscate("0042") # => "7391"
30
+ #
31
+ # # Remove signs from negative numbers
32
+ # obfuscator = NumberObfuscator.new(unsigned: true)
33
+ # obfuscator.obfuscate("-123") # => "456"
34
+ #
35
+ # # Use seed for reproducible results
36
+ # obfuscator = NumberObfuscator.new(seed: 12345)
37
+ # obfuscator.obfuscate("123.45") # => Same result for same seed
38
+ #
39
+ # Features:
40
+ # - Preserves number format (decimal separators, thousand separators)
41
+ # - Maintains leading zeros (optional)
42
+ # - Handles mixed content with letters and numbers
43
+ # - Supports both Latin and Cyrillic alphabets
44
+ # - Provides reproducible results with seeds
45
+ # - UTF-8 encoding support
46
+ #
47
+ # @param preserve_leading_zeros [Boolean] Whether to keep leading zeros in string numbers (default: true)
48
+ # @param unsigned [Boolean] Whether to remove signs from negative numbers (default: false)
49
+ # @param seed [Integer, nil] Optional seed for reproducible results
50
+ #
51
+ # @raise [Error] If number obfuscation fails
52
+ # @raise [InputError] If input is neither Numeric nor String
53
+ #
54
+ # @note This method is optimized for single-use. For bulk operations,
55
+ # consider creating a single instance and reusing it.
56
+ #
57
+ # @note This class is not thread-safe. For concurrent usage,
58
+ # create separate instances per thread.
59
+ #
60
+ # @note Requires Ruby 3.0+ for pattern matching features
61
+ class NumberObfuscator
62
+ include Internal::RNG
63
+
64
+ FormatError = Class.new(Error)
65
+ RangeError = Class.new(Error)
66
+
67
+ # Consider memoizing character sets
68
+ UPPERCASE_CYRILLIC = ('А'..'Я').to_a - ['Ё']
69
+ LOWERCASE_CYRILLIC = ('а'..'я').to_a - ['ё']
70
+
71
+ def initialize(preserve_leading_zeros: true, unsigned: false, seed: nil)
72
+ @preserve_leading_zeros = preserve_leading_zeros
73
+ @unsigned = unsigned
74
+ @seed = seed # Store seed for reuse
75
+ setup_rng(seed)
76
+ end
77
+
78
+ def obfuscate(input)
79
+ return input if input.nil? || (input.is_a?(String) && input.empty?)
80
+
81
+ # Create a unique seed based on both the original seed and input
82
+ if @seed
83
+ combined_seed = [@seed, input.to_s].hash
84
+ setup_rng(combined_seed)
85
+ end
86
+
87
+ # Handle unsigned option at the entry point
88
+ input = if @unsigned && input.is_a?(String)
89
+ input.gsub(/^-/, '')
90
+ elsif @unsigned && input.is_a?(Numeric)
91
+ input.abs
92
+ else
93
+ input
94
+ end
95
+
96
+ case input
97
+ when Numeric
98
+ obfuscate_numeric(input)
99
+ when String
100
+ obfuscate_string(input)
101
+ else
102
+ raise InputError, "Input must be Numeric or String, got: #{input.class}"
103
+ end
104
+ rescue InputError
105
+ raise
106
+ rescue StandardError => e
107
+ raise Error, "Number obfuscation error: #{e.message}"
108
+ end
109
+
110
+ private
111
+
112
+ def preserve_format(original, new_number)
113
+ return new_number if original.nil?
114
+ return original if original.to_s.match?(/^-?0+\.?0*$/)
115
+
116
+ original_str = original.to_s
117
+ is_negative = original_str.start_with?('-') || new_number.negative?
118
+ new_str = new_number.abs.to_s
119
+
120
+ # Split into parts for IP-like numbers
121
+ if original_str.count('.') > 1
122
+ parts = original_str.gsub(/^-/, '').split('.')
123
+ new_parts = parts.map do |part|
124
+ part_len = part.length
125
+ new_part = new_number.abs.to_s[-part_len..]
126
+ new_part.rjust(part_len, '0')
127
+ end
128
+ new_str = new_parts.join('.')
129
+ else
130
+ # Handle decimal places
131
+ if original_str.include?('.') || original_str.include?(',')
132
+ separator = original_str.include?(',') ? ',' : '.'
133
+ decimal_places = original_str.gsub(/^-/, '').split(/[.,]/).last&.length || 0
134
+ new_str = format("%.#{decimal_places}f", new_number.abs).tr('.', separator)
135
+ end
136
+
137
+ # Handle leading zeros
138
+ if @preserve_leading_zeros
139
+ leading_zeros = original_str.gsub(/^-/, '').match(/^0+/)&.[](0)
140
+ new_str = "#{leading_zeros}#{new_str}" if leading_zeros
141
+ end
142
+
143
+ # Ensure exact length match
144
+ new_str = new_str.rjust(original_str.gsub(/^-/, '').length, '0')
145
+ end
146
+
147
+ # Add sign only if input was negative
148
+ is_negative ? "-#{new_str}" : new_str
149
+ end
150
+
151
+ def obfuscate_string(text)
152
+ return text if text.empty?
153
+
154
+ # Split into numbers and separators while preserving positions
155
+ parts = text.scan(/(\d+(?:\.\d+)?)|([^\d]+)/)
156
+ parts.flatten!
157
+ parts.compact!
158
+
159
+ # Generate base seed for the entire string
160
+ base_seed = if @seed
161
+ "#{@seed}:#{text}".hash
162
+ else
163
+ Random.new_seed
164
+ end
165
+
166
+ result = ''
167
+ numbers_count = parts.count { |p| p.match?(/\d/) }
168
+ number_index = 0
169
+
170
+ parts.each do |part|
171
+ result += if part.match?(/\d/)
172
+ position_seed = "#{base_seed}:#{number_index}:#{numbers_count}".hash
173
+ local_rng = Random.new(position_seed)
174
+
175
+ number_result = with_temporary_rng(local_rng) do
176
+ original_str = @unsigned ? part.gsub(/^-/, '') : part
177
+ original = if original_str.include?('.')
178
+ original_str.to_f
179
+ else
180
+ original_str.to_i
181
+ end
182
+
183
+ new_number = generate_similar_number(original)
184
+ preserve_format(original_str, new_number)
185
+ end
186
+
187
+ number_index += 1
188
+ number_result
189
+ else
190
+ part
191
+ end
192
+ end
193
+
194
+ result
195
+ end
196
+
197
+ def obfuscate_mixed_string(text)
198
+ # Split into tokens preserving all separators and non-numeric parts
199
+ # \p{L} matches any kind of letter from any language
200
+ # \p{N} matches any kind of numeric character in any script
201
+ tokens = text.split(/(\d+(?:\.\d+)?|\s+|[[:punct:]]|[\p{L}]+)/)
202
+
203
+ tokens.map do |token|
204
+ case token
205
+ when /^\d+(?:\.\d+)?$/ # Pure number
206
+ new_number = generate_similar_number(token.to_f)
207
+ preserve_format(token, new_number)
208
+ when /\p{L}+/ # Letters (any script)
209
+ obfuscate_letters(token)
210
+ else # Spaces and punctuation
211
+ token
212
+ end
213
+ end.join
214
+ end
215
+
216
+ def obfuscate_letters(text)
217
+ # Ensure UTF-8 encoding
218
+ text = text.encode('UTF-8') unless text.encoding == Encoding::UTF_8
219
+
220
+ text.chars.map do |char|
221
+ case char
222
+ when /[A-Z]/
223
+ (((char.ord - 'A'.ord + random_integer(1, 25)) % 26) + 'A'.ord).chr
224
+ when /[a-z]/
225
+ (((char.ord - 'a'.ord + random_integer(1, 25)) % 26) + 'a'.ord).chr
226
+ when /[А-Я]/
227
+ if char == 'Ё'
228
+ 'Е'
229
+ else
230
+ random_sample(UPPERCASE_CYRILLIC)
231
+ end
232
+ when /[а-я]/
233
+ if char == 'ё'
234
+ 'е'
235
+ else
236
+ random_sample(LOWERCASE_CYRILLIC)
237
+ end
238
+ else
239
+ char
240
+ end
241
+ end.join.force_encoding('UTF-8')
242
+ end
243
+
244
+ def obfuscate_numeric(number)
245
+ return number if number.zero?
246
+
247
+ case number
248
+ when Integer
249
+ # For integers, maintain the same number of digits
250
+ digits = number.abs.to_s.length
251
+ base = 10**(digits - 1)
252
+ new_number = ((random_probability * 9) + 1) * base
253
+ new_number = new_number.to_i
254
+
255
+ # Handle sign based on unsigned option
256
+ if @unsigned
257
+ new_number.abs
258
+ else
259
+ number.negative? ? -new_number.abs : new_number.abs
260
+ end
261
+
262
+ when Float, BigDecimal
263
+ # For floating point, maintain similar magnitude and precision
264
+ str_number = number.to_s
265
+ decimal_places = str_number.split('.')[1]&.length || 0
266
+ new_number = generate_similar_number(number)
267
+
268
+ # Ensure consistent decimal places
269
+ new_number = new_number.round(decimal_places) if decimal_places.positive?
270
+
271
+ # Handle sign based on unsigned option
272
+ if @unsigned
273
+ new_number.abs
274
+ else
275
+ number.negative? ? -new_number.abs : new_number.abs
276
+ end
277
+
278
+ else
279
+ raise InputError, "Unsupported numeric type: #{number.class}"
280
+ end
281
+ end
282
+
283
+ def generate_similar_number(number)
284
+ return 0 if number.zero?
285
+
286
+ # Work with absolute value
287
+ abs_num = number.abs
288
+ str_num = abs_num.to_s
289
+ is_float = number.is_a?(Float) || str_num.include?('.')
290
+ num_digits = str_num.gsub(/[^\d]/, '').length
291
+
292
+ # Generate base number with correct digits
293
+ base = 10**(num_digits - 1)
294
+ max = (10**num_digits) - 1
295
+ new_number = base + (random_probability * (max - base)).to_i
296
+
297
+ # Handle float conversion and decimal places
298
+ if is_float
299
+ decimal_places = str_num.split('.')[1]&.length || 0
300
+ new_number = (new_number.to_f / (10**decimal_places)).round(decimal_places)
301
+ end
302
+
303
+ # Handle sign based on unsigned option
304
+ if @unsigned
305
+ new_number.abs
306
+ else
307
+ number.negative? ? -new_number.abs : new_number.abs
308
+ end
309
+ end
310
+
311
+ def random_integer(min, max)
312
+ (random_probability * (max - min + 1)).to_i + min
313
+ end
314
+
315
+ def with_temporary_rng(temp_rng)
316
+ original_rng = @rng
317
+ @rng = temp_rng
318
+ yield
319
+ ensure
320
+ @rng = original_rng
321
+ end
322
+ end
323
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Obfuscator
4
- VERSION = '0.3.2'
4
+ VERSION = '0.8.1'
5
5
  end
data/lib/obfuscator-rb.rb CHANGED
@@ -1,21 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Obfuscator is a text obfuscation library that preserves text structure while replacing content
4
- # with meaningless but natural-looking words. It supports both English and Russian languages.
4
+ # with meaningless but natural-looking words. It supports both English and Russian languages,
5
+ # as well as numbers and dates with format preservation.
5
6
  #
6
- # The gem provides two main obfuscators:
7
+ # The gem provides three main obfuscators:
7
8
  # - {Multilang} for text obfuscation with multiple language support
9
+ # - {NumberObfuscator} for number and mixed content obfuscation
8
10
  # - {DateObfuscator} for date obfuscation with format preservation
9
11
  #
12
+ # THREAD SAFETY:
13
+ # Individual obfuscator instances are NOT thread-safe. For concurrent operations:
14
+ # - Create separate instances per thread
15
+ # - Do not share instances across threads
16
+ # - Each instance maintains its own RNG state
17
+ #
10
18
  # @example Basic text obfuscation
11
19
  # require 'obfuscator-rb'
12
20
  #
13
21
  # obfuscator = Obfuscator::Multilang.new
14
22
  # obfuscator.obfuscate("Hello, World!") # => "Kites, Mefal!"
15
23
  #
16
- # @example Date obfuscation
17
- # date_obf = Obfuscator::DateObfuscator.new
18
- # date_obf.obfuscate("2023-12-31") # => "2025-07-15"
24
+ # @example Number obfuscation with format preservation
25
+ # num_obf = Obfuscator::NumberObfuscator.new
26
+ # num_obf.obfuscate(123.45) # => 567.89
27
+ # num_obf.obfuscate("1,234.56") # => "5,678.91"
28
+ # num_obf.obfuscate("192.168.1.1") # => "234.567.8.9"
29
+ # num_obf.obfuscate("ABC-42XY") # => "DEF-73ZW"
30
+ #
31
+ # @example Date obfuscation with constraints
32
+ # date_obf = Obfuscator::DateObfuscator.new(
33
+ # format: :iso,
34
+ # constraints: {
35
+ # min_year: 2020,
36
+ # max_year: 2025,
37
+ # preserve_month: true
38
+ # }
39
+ # )
40
+ # date_obf.obfuscate("2023-12-31") # => "2025-12-15"
41
+ #
42
+ # @example Error handling
43
+ # begin
44
+ # obfuscator.obfuscate(input)
45
+ # rescue Obfuscator::InputError => e
46
+ # # Handle invalid input types
47
+ # rescue Obfuscator::EncodingError => e
48
+ # # Handle encoding issues
49
+ # rescue Obfuscator::Error => e
50
+ # # Handle other obfuscation errors
51
+ # end
19
52
  #
20
53
  # Error handling is provided through specific error classes:
21
54
  # - {Error} Base error class for the gem
@@ -23,6 +56,7 @@
23
56
  # - {EncodingError} Raised for encoding-related issues
24
57
  #
25
58
  # @see Multilang For text obfuscation functionality
59
+ # @see NumberObfuscator For number and mixed content obfuscation
26
60
  # @see DateObfuscator For date obfuscation functionality
27
61
  # @see Internal::RNG For random number generation utilities
28
62
 
@@ -30,6 +64,23 @@ module Obfuscator
30
64
  class Error < StandardError; end
31
65
  class EncodingError < Error; end
32
66
  class InputError < Error; end
67
+
68
+ # Base module for the Obfuscator gem.
69
+ #
70
+ # THREAD SAFETY:
71
+ # Individual obfuscator instances are NOT thread-safe. For concurrent operations:
72
+ # - Create separate instances per thread
73
+ # - Do not share instances across threads
74
+ # - Each instance maintains its own RNG state
75
+ #
76
+ # @example Thread-safe usage
77
+ # threads = 4.times.map do
78
+ # Thread.new do
79
+ # # Create a new instance for each thread
80
+ # obfuscator = Obfuscator::NumberObfuscator.new(seed: 12345)
81
+ # obfuscator.obfuscate("123.45")
82
+ # end
83
+ # end
33
84
  end
34
85
 
35
86
  require_relative 'obfuscator/version'
@@ -38,6 +89,7 @@ require_relative 'obfuscator/internal/rng'
38
89
  require_relative 'obfuscator/naturalizer'
39
90
  require_relative 'obfuscator/multilang'
40
91
  require_relative 'obfuscator/date_obfuscator'
92
+ require_relative 'obfuscator/number_obfuscator'
41
93
 
42
94
  # Usage example:
43
95
  if __FILE__ == $PROGRAM_NAME
metadata CHANGED
@@ -1,23 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: obfuscator-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aleksandr Dryzhuk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-02-19 00:00:00.000000000 Z
11
+ date: 2025-02-23 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
- A Ruby gem for text obfuscation that preserves text structure while replacing content
15
- with meaningless but natural-looking words. Supports both English and Russian languages,
16
- with various obfuscation modes and optional text naturalization.
14
+ A Ruby library for data obfuscation that:
15
+ - Preserves original data format and structure as much as possible
16
+ - Supports numbers (including IP-like sequences), dates, and text
17
+ - Maintains text structure while replacing content with meaningless but natural-looking words in English and Russian
18
+ - Maintains data type consistency and decimal precision
19
+ - Offers seeded randomization for reproducible results
20
+ - Handles various number formats (leading zeros, separators)
21
+ - Provides configurable options (unsigned mode, format preservation)
17
22
 
18
- Гем для обфускации текста, сохраняющий его структуру и естественный вид, но заменяющий при этом содержимое
19
- бессмысленными словами. Поддерживает английский и русский языки, различные режимы обфускации и опциональную
20
- натурализацию текста.
23
+ Note: Individual obfuscator instances are not thread-safe.
24
+ For concurrent operations, create separate instances per thread.
21
25
  email:
22
26
  - dev@ad-it.pro
23
27
  executables: []
@@ -28,6 +32,7 @@ files:
28
32
  - CHANGELOG.md
29
33
  - LICENSE.txt
30
34
  - README.md
35
+ - RELEASE_CHECKLIST.md
31
36
  - Rakefile
32
37
  - lib/obfuscator-rb.rb
33
38
  - lib/obfuscator/constants.rb
@@ -35,6 +40,7 @@ files:
35
40
  - lib/obfuscator/internal/rng.rb
36
41
  - lib/obfuscator/multilang.rb
37
42
  - lib/obfuscator/naturalizer.rb
43
+ - lib/obfuscator/number_obfuscator.rb
38
44
  - lib/obfuscator/version.rb
39
45
  homepage: https://hub.mos.ru/ad/obfuscator
40
46
  licenses:
@@ -62,6 +68,5 @@ requirements: []
62
68
  rubygems_version: 3.5.22
63
69
  signing_key:
64
70
  specification_version: 4
65
- summary: Text obfuscator that preserves structure while working with both English
66
- and Russian languages
71
+ summary: A robust data obfuscator for numbers, dates, and text with format preservation
67
72
  test_files: []