br-utils 0.1.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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +120 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +348 -0
  8. data/Rakefile +6 -0
  9. data/examples/boleto_usage_example.rb +79 -0
  10. data/examples/cep_usage_example.rb +148 -0
  11. data/examples/cnh_usage_example.rb +120 -0
  12. data/examples/cnpj_usage_example.rb +227 -0
  13. data/examples/cpf_usage_example.rb +237 -0
  14. data/examples/currency_usage_example.rb +266 -0
  15. data/examples/date_usage_example.rb +259 -0
  16. data/examples/email_usage_example.rb +321 -0
  17. data/examples/legal_nature_usage_example.rb +437 -0
  18. data/examples/legal_process_usage_example.rb +444 -0
  19. data/examples/license_plate_usage_example.rb +440 -0
  20. data/examples/phone_usage_example.rb +595 -0
  21. data/examples/pis_usage_example.rb +588 -0
  22. data/examples/renavam_usage_example.rb +499 -0
  23. data/examples/voter_id_usage_example.rb +573 -0
  24. data/lib/brazilian-utils/boleto-utils.rb +176 -0
  25. data/lib/brazilian-utils/cep-utils.rb +330 -0
  26. data/lib/brazilian-utils/cnh-utils.rb +88 -0
  27. data/lib/brazilian-utils/cnpj-utils.rb +202 -0
  28. data/lib/brazilian-utils/cpf-utils.rb +192 -0
  29. data/lib/brazilian-utils/currency-utils.rb +226 -0
  30. data/lib/brazilian-utils/data/legal_process_ids.json +38 -0
  31. data/lib/brazilian-utils/date-utils.rb +244 -0
  32. data/lib/brazilian-utils/email-utils.rb +54 -0
  33. data/lib/brazilian-utils/legal-nature-utils.rb +235 -0
  34. data/lib/brazilian-utils/legal-process-utils.rb +240 -0
  35. data/lib/brazilian-utils/license-plate-utils.rb +279 -0
  36. data/lib/brazilian-utils/phone-utils.rb +272 -0
  37. data/lib/brazilian-utils/pis-utils.rb +151 -0
  38. data/lib/brazilian-utils/renavam-utils.rb +113 -0
  39. data/lib/brazilian-utils/voter-id-utils.rb +165 -0
  40. metadata +123 -0
@@ -0,0 +1,259 @@
1
+ require_relative '../lib/brazilian-utils/date-utils'
2
+ require 'date'
3
+
4
+ puts "=" * 70
5
+ puts "Brazilian Date Utils - Usage Examples"
6
+ puts "=" * 70
7
+ puts
8
+
9
+ # ============================================================================
10
+ # Holiday Checking
11
+ # ============================================================================
12
+ puts "1. CHECKING NATIONAL HOLIDAYS"
13
+ puts "-" * 70
14
+
15
+ # New Year
16
+ new_year = Date.new(2024, 1, 1)
17
+ puts "New Year (Jan 1, 2024): #{BrazilianUtils::DateUtils.is_holiday(new_year)}"
18
+
19
+ # Christmas
20
+ christmas = Date.new(2024, 12, 25)
21
+ puts "Christmas (Dec 25, 2024): #{BrazilianUtils::DateUtils.is_holiday(christmas)}"
22
+
23
+ # Independence Day
24
+ independence = Date.new(2024, 9, 7)
25
+ puts "Independence Day (Sep 7, 2024): #{BrazilianUtils::DateUtils.is_holiday(independence)}"
26
+
27
+ # Labor Day
28
+ labor_day = Date.new(2024, 5, 1)
29
+ puts "Labor Day (May 1, 2024): #{BrazilianUtils::DateUtils.is_holiday(labor_day)}"
30
+
31
+ # Regular day
32
+ regular_day = Date.new(2024, 3, 15)
33
+ puts "Regular day (Mar 15, 2024): #{BrazilianUtils::DateUtils.is_holiday(regular_day)}"
34
+ puts
35
+
36
+ # ============================================================================
37
+ # State Holidays
38
+ # ============================================================================
39
+ puts "2. CHECKING STATE-SPECIFIC HOLIDAYS"
40
+ puts "-" * 70
41
+
42
+ # São Paulo state holiday (July 9)
43
+ sp_holiday = Date.new(2024, 7, 9)
44
+ puts "July 9, 2024 in São Paulo: #{BrazilianUtils::DateUtils.is_holiday(sp_holiday, 'SP')}"
45
+ puts "July 9, 2024 in Rio de Janeiro: #{BrazilianUtils::DateUtils.is_holiday(sp_holiday, 'RJ')}"
46
+ puts "July 9, 2024 in Minas Gerais: #{BrazilianUtils::DateUtils.is_holiday(sp_holiday, 'MG')}"
47
+ puts
48
+
49
+ # Rio de Janeiro state holiday (November 20 - Consciência Negra)
50
+ rj_holiday = Date.new(2024, 11, 20)
51
+ puts "November 20, 2024 in Rio de Janeiro: #{BrazilianUtils::DateUtils.is_holiday(rj_holiday, 'RJ')}"
52
+ puts "November 20, 2024 in São Paulo: #{BrazilianUtils::DateUtils.is_holiday(rj_holiday, 'SP')}"
53
+ puts "November 20, 2024 in Mato Grosso: #{BrazilianUtils::DateUtils.is_holiday(rj_holiday, 'MT')}"
54
+ puts
55
+
56
+ # Bahia state holiday (July 2)
57
+ ba_holiday = Date.new(2024, 7, 2)
58
+ puts "July 2, 2024 in Bahia: #{BrazilianUtils::DateUtils.is_holiday(ba_holiday, 'BA')}"
59
+ puts "July 2, 2024 in São Paulo: #{BrazilianUtils::DateUtils.is_holiday(ba_holiday, 'SP')}"
60
+ puts
61
+
62
+ # ============================================================================
63
+ # Testing Today
64
+ # ============================================================================
65
+ puts "3. CHECKING TODAY'S DATE"
66
+ puts "-" * 70
67
+
68
+ today = Date.today
69
+ puts "Today is: #{today.strftime('%d/%m/%Y')}"
70
+ puts "Is today a national holiday? #{BrazilianUtils::DateUtils.is_holiday(today)}"
71
+ puts "Is today a holiday in SP? #{BrazilianUtils::DateUtils.is_holiday(today, 'SP')}"
72
+ puts "Is today a holiday in RJ? #{BrazilianUtils::DateUtils.is_holiday(today, 'RJ')}"
73
+ puts
74
+
75
+ # ============================================================================
76
+ # Different Date Types
77
+ # ============================================================================
78
+ puts "4. TESTING DIFFERENT DATE TYPES"
79
+ puts "-" * 70
80
+
81
+ date_obj = Date.new(2024, 12, 25)
82
+ datetime_obj = DateTime.new(2024, 12, 25, 10, 30, 0)
83
+ time_obj = Time.new(2024, 12, 25, 10, 30, 0)
84
+
85
+ puts "Date object (Christmas): #{BrazilianUtils::DateUtils.is_holiday(date_obj)}"
86
+ puts "DateTime object (Christmas): #{BrazilianUtils::DateUtils.is_holiday(datetime_obj)}"
87
+ puts "Time object (Christmas): #{BrazilianUtils::DateUtils.is_holiday(time_obj)}"
88
+ puts
89
+
90
+ # ============================================================================
91
+ # Invalid Inputs
92
+ # ============================================================================
93
+ puts "5. TESTING INVALID INPUTS"
94
+ puts "-" * 70
95
+
96
+ puts "String date '2024-01-01': #{BrazilianUtils::DateUtils.is_holiday('2024-01-01').inspect}"
97
+ puts "Invalid UF 'XX': #{BrazilianUtils::DateUtils.is_holiday(Date.new(2024, 1, 1), 'XX').inspect}"
98
+ puts "Invalid UF 'Invalid': #{BrazilianUtils::DateUtils.is_holiday(Date.new(2024, 1, 1), 'Invalid').inspect}"
99
+ puts
100
+
101
+ # ============================================================================
102
+ # Date to Text Conversion
103
+ # ============================================================================
104
+ puts "6. CONVERTING DATES TO TEXT"
105
+ puts "-" * 70
106
+
107
+ # Regular dates
108
+ date1 = '15/03/2024'
109
+ puts "#{date1}: #{BrazilianUtils::DateUtils.convert_date_to_text(date1)}"
110
+
111
+ date2 = '25/12/2023'
112
+ puts "#{date2}: #{BrazilianUtils::DateUtils.convert_date_to_text(date2)}"
113
+
114
+ # First day of month (special case using "Primeiro")
115
+ date3 = '01/01/2024'
116
+ puts "#{date3}: #{BrazilianUtils::DateUtils.convert_date_to_text(date3)}"
117
+
118
+ date4 = '01/05/2024'
119
+ puts "#{date4}: #{BrazilianUtils::DateUtils.convert_date_to_text(date4)}"
120
+
121
+ # Different months
122
+ date5 = '21/04/2024'
123
+ puts "#{date5}: #{BrazilianUtils::DateUtils.convert_date_to_text(date5)}"
124
+
125
+ date6 = '07/09/2024'
126
+ puts "#{date6}: #{BrazilianUtils::DateUtils.convert_date_to_text(date6)}"
127
+ puts
128
+
129
+ # ============================================================================
130
+ # Brazilian National Holidays in Text
131
+ # ============================================================================
132
+ puts "7. BRAZILIAN NATIONAL HOLIDAYS IN TEXT FORMAT"
133
+ puts "-" * 70
134
+
135
+ national_holidays = [
136
+ '01/01/2024', # New Year
137
+ '21/04/2024', # Tiradentes
138
+ '01/05/2024', # Labor Day
139
+ '07/09/2024', # Independence Day
140
+ '12/10/2024', # Nossa Senhora Aparecida
141
+ '02/11/2024', # All Souls Day
142
+ '15/11/2024', # Proclamation of the Republic
143
+ '25/12/2024' # Christmas
144
+ ]
145
+
146
+ national_holidays.each do |holiday|
147
+ text = BrazilianUtils::DateUtils.convert_date_to_text(holiday)
148
+ puts "#{holiday}: #{text}"
149
+ end
150
+ puts
151
+
152
+ # ============================================================================
153
+ # Invalid Date Conversions
154
+ # ============================================================================
155
+ puts "8. TESTING INVALID DATE CONVERSIONS"
156
+ puts "-" * 70
157
+
158
+ invalid_dates = [
159
+ '32/01/2024', # Invalid day
160
+ '29/02/2023', # Not a leap year
161
+ '01-01-2024', # Wrong separator
162
+ '2024/01/01', # Wrong format
163
+ '00/01/2024', # Day zero
164
+ '01/13/2024', # Invalid month
165
+ 'invalid', # Non-date string
166
+ '' # Empty string
167
+ ]
168
+
169
+ invalid_dates.each do |date|
170
+ result = BrazilianUtils::DateUtils.convert_date_to_text(date)
171
+ puts "#{date.inspect}: #{result.inspect}"
172
+ end
173
+ puts
174
+
175
+ # ============================================================================
176
+ # Practical Use Case: Document Date
177
+ # ============================================================================
178
+ puts "9. PRACTICAL USE CASE: DOCUMENT FORMATTING"
179
+ puts "-" * 70
180
+
181
+ document_date = '15/03/2024'
182
+ formatted_date = BrazilianUtils::DateUtils.convert_date_to_text(document_date)
183
+
184
+ puts "Contract Template:"
185
+ puts "-" * 70
186
+ puts "Contrato celebrado em São Paulo, aos #{formatted_date}."
187
+ puts
188
+ puts "Invoice Template:"
189
+ puts "-" * 70
190
+ puts "Nota Fiscal emitida em: #{formatted_date.capitalize}"
191
+ puts
192
+
193
+ # ============================================================================
194
+ # Checking Multiple States
195
+ # ============================================================================
196
+ puts "10. CHECKING HOLIDAYS ACROSS ALL STATES"
197
+ puts "-" * 70
198
+
199
+ test_date = Date.new(2024, 11, 20) # Consciência Negra
200
+ states = %w[SP RJ MG BA RS PR SC PE CE GO DF]
201
+
202
+ puts "Date: November 20, 2024 (Consciência Negra)"
203
+ puts "Holiday Status by State:"
204
+ states.each do |state|
205
+ is_holiday = BrazilianUtils::DateUtils.is_holiday(test_date, state)
206
+ status = is_holiday ? "✓ Holiday" : "✗ Not a holiday"
207
+ puts " #{state}: #{status}"
208
+ end
209
+ puts
210
+
211
+ # ============================================================================
212
+ # Leap Year Handling
213
+ # ============================================================================
214
+ puts "11. LEAP YEAR DATE HANDLING"
215
+ puts "-" * 70
216
+
217
+ leap_date = '29/02/2024'
218
+ non_leap_date = '29/02/2023'
219
+
220
+ puts "#{leap_date}: #{BrazilianUtils::DateUtils.convert_date_to_text(leap_date)}"
221
+ puts "#{non_leap_date}: #{BrazilianUtils::DateUtils.convert_date_to_text(non_leap_date).inspect} (invalid)"
222
+ puts
223
+
224
+ # ============================================================================
225
+ # Combined Example: Check Holiday and Convert to Text
226
+ # ============================================================================
227
+ puts "12. COMBINED EXAMPLE: HOLIDAY CHECK + TEXT CONVERSION"
228
+ puts "-" * 70
229
+
230
+ def format_holiday_info(date_str, uf = nil)
231
+ begin
232
+ day, month, year = date_str.split('/').map(&:to_i)
233
+ date_obj = Date.new(year, month, day)
234
+
235
+ is_holiday = BrazilianUtils::DateUtils.is_holiday(date_obj, uf)
236
+ text = BrazilianUtils::DateUtils.convert_date_to_text(date_str)
237
+
238
+ puts "Data: #{date_str}"
239
+ puts "Por extenso: #{text}"
240
+ if uf
241
+ puts "Feriado em #{uf}? #{is_holiday ? 'Sim' : 'Não'}"
242
+ else
243
+ puts "Feriado nacional? #{is_holiday ? 'Sim' : 'Não'}"
244
+ end
245
+ puts
246
+ rescue => e
247
+ puts "Erro ao processar #{date_str}: #{e.message}"
248
+ puts
249
+ end
250
+ end
251
+
252
+ format_holiday_info('25/12/2024')
253
+ format_holiday_info('09/07/2024', 'SP')
254
+ format_holiday_info('15/03/2024')
255
+ format_holiday_info('01/01/2024', 'RJ')
256
+
257
+ puts "=" * 70
258
+ puts "Examples completed!"
259
+ puts "=" * 70
@@ -0,0 +1,321 @@
1
+ require_relative '../lib/brazilian-utils/email-utils'
2
+
3
+ puts "=" * 70
4
+ puts "Brazilian Email Utils - Usage Examples"
5
+ puts "=" * 70
6
+ puts
7
+
8
+ # ============================================================================
9
+ # Valid Email Addresses
10
+ # ============================================================================
11
+ puts "1. VALID EMAIL ADDRESSES"
12
+ puts "-" * 70
13
+
14
+ valid_emails = [
15
+ 'brutils@brutils.com',
16
+ 'user.name@example.com',
17
+ 'user+tag@example.co.uk',
18
+ 'user_123@test-domain.com',
19
+ 'contact@company.com.br',
20
+ 'john.doe@gmail.com',
21
+ 'jane.smith@outlook.com',
22
+ 'first.middle.last@example.com',
23
+ 'employee@mail.example.com',
24
+ 'user2024@example.com',
25
+ 'User.Name@Example.COM'
26
+ ]
27
+
28
+ valid_emails.each do |email|
29
+ result = BrazilianUtils::EmailUtils.is_valid(email)
30
+ status = result ? "✓ VALID" : "✗ INVALID"
31
+ puts "#{status.ljust(10)} #{email}"
32
+ end
33
+ puts
34
+
35
+ # ============================================================================
36
+ # Invalid Email Addresses
37
+ # ============================================================================
38
+ puts "2. INVALID EMAIL ADDRESSES"
39
+ puts "-" * 70
40
+
41
+ invalid_emails = [
42
+ 'invalid-email@brutils', # No TLD
43
+ '.user@example.com', # Starts with dot
44
+ 'user@', # No domain
45
+ '@example.com', # No local part
46
+ 'user name@example.com', # Contains space
47
+ 'user@example.c', # TLD too short
48
+ 'user@@example.com', # Double @
49
+ 'user..name@example.com', # Consecutive dots
50
+ 'userexample.com', # No @ symbol
51
+ 'user@examplecom', # No dot in domain
52
+ 'user[name]@example.com', # Invalid characters
53
+ '', # Empty string
54
+ ' user@example.com', # Leading space
55
+ 'user@example.com ' # Trailing space
56
+ ]
57
+
58
+ invalid_emails.each do |email|
59
+ result = BrazilianUtils::EmailUtils.is_valid(email)
60
+ status = result ? "✓ VALID" : "✗ INVALID"
61
+ display_email = email.empty? ? '(empty string)' : email.inspect
62
+ puts "#{status.ljust(10)} #{display_email}"
63
+ end
64
+ puts
65
+
66
+ # ============================================================================
67
+ # Using the valid? Alias
68
+ # ============================================================================
69
+ puts "3. USING THE valid? ALIAS"
70
+ puts "-" * 70
71
+
72
+ test_email = 'user@example.com'
73
+ puts "BrazilianUtils::EmailUtils.is_valid('#{test_email}'): #{BrazilianUtils::EmailUtils.is_valid(test_email)}"
74
+ puts "BrazilianUtils::EmailUtils.valid?('#{test_email}'): #{BrazilianUtils::EmailUtils.valid?(test_email)}"
75
+ puts
76
+
77
+ # ============================================================================
78
+ # Non-String Inputs
79
+ # ============================================================================
80
+ puts "4. NON-STRING INPUTS"
81
+ puts "-" * 70
82
+
83
+ non_string_inputs = [
84
+ [nil, 'nil'],
85
+ [123, 'Integer (123)'],
86
+ [12.34, 'Float (12.34)'],
87
+ [true, 'Boolean (true)'],
88
+ [['user@example.com'], 'Array'],
89
+ [{ email: 'user@example.com' }, 'Hash'],
90
+ [:email, 'Symbol']
91
+ ]
92
+
93
+ non_string_inputs.each do |value, description|
94
+ result = BrazilianUtils::EmailUtils.is_valid(value)
95
+ status = result ? "✓ VALID" : "✗ INVALID"
96
+ puts "#{status.ljust(10)} #{description}"
97
+ end
98
+ puts
99
+
100
+ # ============================================================================
101
+ # Practical Use Case: Form Validation
102
+ # ============================================================================
103
+ puts "5. PRACTICAL USE CASE: FORM VALIDATION"
104
+ puts "-" * 70
105
+
106
+ def validate_contact_form(name, email, message)
107
+ errors = []
108
+
109
+ errors << "Name is required" if name.nil? || name.empty?
110
+ errors << "Email is required" if email.nil? || email.empty?
111
+ errors << "Invalid email format" unless BrazilianUtils::EmailUtils.valid?(email)
112
+ errors << "Message is required" if message.nil? || message.empty?
113
+
114
+ if errors.empty?
115
+ { valid: true, message: "Form is valid" }
116
+ else
117
+ { valid: false, errors: errors }
118
+ end
119
+ end
120
+
121
+ # Test cases
122
+ test_forms = [
123
+ { name: 'João Silva', email: 'joao@example.com', message: 'Hello!' },
124
+ { name: 'Maria Santos', email: 'invalid-email', message: 'Hi!' },
125
+ { name: '', email: 'maria@example.com', message: 'Test' },
126
+ { name: 'Pedro Costa', email: 'pedro@company.com.br', message: '' },
127
+ { name: 'Ana Lima', email: '', message: 'Message' }
128
+ ]
129
+
130
+ test_forms.each_with_index do |form, index|
131
+ puts "\nTest #{index + 1}:"
132
+ puts " Name: #{form[:name].inspect}"
133
+ puts " Email: #{form[:email].inspect}"
134
+ puts " Message: #{form[:message].inspect}"
135
+
136
+ result = validate_contact_form(form[:name], form[:email], form[:message])
137
+
138
+ if result[:valid]
139
+ puts " Result: ✓ #{result[:message]}"
140
+ else
141
+ puts " Result: ✗ Errors:"
142
+ result[:errors].each do |error|
143
+ puts " - #{error}"
144
+ end
145
+ end
146
+ end
147
+ puts
148
+
149
+ # ============================================================================
150
+ # Batch Email Validation
151
+ # ============================================================================
152
+ puts "6. BATCH EMAIL VALIDATION"
153
+ puts "-" * 70
154
+
155
+ email_list = [
156
+ 'john.doe@gmail.com',
157
+ 'invalid@domain',
158
+ 'jane+newsletter@example.com',
159
+ '@incomplete.com',
160
+ 'corporate@company.com.br',
161
+ 'user..name@example.com',
162
+ 'contact@subdomain.example.com',
163
+ 'user@example.c',
164
+ 'valid.email_123+tag@test-server.co.uk'
165
+ ]
166
+
167
+ valid_emails = []
168
+ invalid_emails = []
169
+
170
+ email_list.each do |email|
171
+ if BrazilianUtils::EmailUtils.valid?(email)
172
+ valid_emails << email
173
+ else
174
+ invalid_emails << email
175
+ end
176
+ end
177
+
178
+ puts "Total emails: #{email_list.length}"
179
+ puts "Valid emails: #{valid_emails.length}"
180
+ puts "Invalid emails: #{invalid_emails.length}"
181
+ puts
182
+ puts "Valid emails:"
183
+ valid_emails.each { |email| puts " ✓ #{email}" }
184
+ puts
185
+ puts "Invalid emails:"
186
+ invalid_emails.each { |email| puts " ✗ #{email}" }
187
+ puts
188
+
189
+ # ============================================================================
190
+ # Real-World Email Examples
191
+ # ============================================================================
192
+ puts "7. REAL-WORLD EMAIL EXAMPLES"
193
+ puts "-" * 70
194
+
195
+ real_world_examples = {
196
+ 'Gmail' => 'john.doe+spam@gmail.com',
197
+ 'Outlook' => 'jane.smith@outlook.com',
198
+ 'Corporate' => 'employee@company.com',
199
+ 'Brazilian domain' => 'contato@empresa.com.br',
200
+ 'Subdomain' => 'support@help.example.com',
201
+ 'Government' => 'contato@governo.gov.br',
202
+ 'University' => 'aluno@universidade.edu.br',
203
+ 'With numbers' => 'user2024@example.com',
204
+ 'With underscores' => 'first_last@example.com',
205
+ 'Multiple dots' => 'first.middle.last@example.com'
206
+ }
207
+
208
+ real_world_examples.each do |category, email|
209
+ result = BrazilianUtils::EmailUtils.valid?(email)
210
+ status = result ? "✓" : "✗"
211
+ puts "#{status} #{category.ljust(18)}: #{email}"
212
+ end
213
+ puts
214
+
215
+ # ============================================================================
216
+ # Email Validation with Feedback
217
+ # ============================================================================
218
+ puts "8. EMAIL VALIDATION WITH DETAILED FEEDBACK"
219
+ puts "-" * 70
220
+
221
+ def validate_email_with_feedback(email)
222
+ unless email.is_a?(String)
223
+ return { valid: false, message: "Email must be a string, got #{email.class}" }
224
+ end
225
+
226
+ if email.empty?
227
+ return { valid: false, message: "Email cannot be empty" }
228
+ end
229
+
230
+ unless email.include?('@')
231
+ return { valid: false, message: "Email must contain @ symbol" }
232
+ end
233
+
234
+ if email.start_with?('.')
235
+ return { valid: false, message: "Email cannot start with a dot" }
236
+ end
237
+
238
+ unless email.match?(/\.[a-zA-Z]{2,}$/)
239
+ return { valid: false, message: "Email must have a valid TLD (at least 2 letters)" }
240
+ end
241
+
242
+ if BrazilianUtils::EmailUtils.valid?(email)
243
+ { valid: true, message: "Email is valid" }
244
+ else
245
+ { valid: false, message: "Email format is invalid" }
246
+ end
247
+ end
248
+
249
+ test_emails_feedback = [
250
+ 'valid@example.com',
251
+ 'invalid',
252
+ '@domain.com',
253
+ '.user@example.com',
254
+ 'user@domain',
255
+ nil,
256
+ ''
257
+ ]
258
+
259
+ test_emails_feedback.each do |email|
260
+ result = validate_email_with_feedback(email)
261
+ status = result[:valid] ? "✓ VALID" : "✗ INVALID"
262
+ display = email.nil? ? 'nil' : (email.empty? ? '(empty)' : email)
263
+ puts "#{status.ljust(10)} #{display.ljust(25)} => #{result[:message]}"
264
+ end
265
+ puts
266
+
267
+ # ============================================================================
268
+ # Statistics
269
+ # ============================================================================
270
+ puts "9. EMAIL VALIDATION STATISTICS"
271
+ puts "-" * 70
272
+
273
+ all_test_emails = [
274
+ 'user@example.com', 'invalid@', '@example.com', 'user.name@example.com',
275
+ 'user+tag@example.co.uk', 'invalid email@test.com', 'user@domain',
276
+ 'test..user@example.com', 'user@test-domain.com', '.user@example.com',
277
+ 'user@example.c', 'john.doe@gmail.com', 'user@subdomain.example.com',
278
+ 'contact@company.com.br', 'user@example.com '
279
+ ]
280
+
281
+ valid_count = all_test_emails.count { |email| BrazilianUtils::EmailUtils.valid?(email) }
282
+ invalid_count = all_test_emails.count { |email| !BrazilianUtils::EmailUtils.valid?(email) }
283
+
284
+ puts "Total emails tested: #{all_test_emails.length}"
285
+ puts "Valid: #{valid_count} (#{(valid_count * 100.0 / all_test_emails.length).round(1)}%)"
286
+ puts "Invalid: #{invalid_count} (#{(invalid_count * 100.0 / all_test_emails.length).round(1)}%)"
287
+ puts
288
+
289
+ # ============================================================================
290
+ # Common Mistakes
291
+ # ============================================================================
292
+ puts "10. COMMON EMAIL MISTAKES"
293
+ puts "-" * 70
294
+
295
+ common_mistakes = {
296
+ 'Missing @' => 'userexample.com',
297
+ 'Missing domain' => 'user@',
298
+ 'Missing local part' => '@example.com',
299
+ 'Space in email' => 'user name@example.com',
300
+ 'No TLD' => 'user@domain',
301
+ 'TLD too short' => 'user@example.c',
302
+ 'Starts with dot' => '.user@example.com',
303
+ 'Consecutive dots' => 'user..name@example.com',
304
+ 'Double @' => 'user@@example.com',
305
+ 'Special chars' => 'user!name@example.com',
306
+ 'Trailing space' => 'user@example.com ',
307
+ 'Leading space' => ' user@example.com'
308
+ }
309
+
310
+ puts "Mistake Email Valid?"
311
+ puts "-" * 70
312
+ common_mistakes.each do |mistake, email|
313
+ result = BrazilianUtils::EmailUtils.valid?(email)
314
+ status = result ? "Yes" : "No"
315
+ puts "#{mistake.ljust(26)} #{email.inspect.ljust(30)} #{status}"
316
+ end
317
+
318
+ puts
319
+ puts "=" * 70
320
+ puts "Examples completed!"
321
+ puts "=" * 70