@@ -281,4 +281,268 @@ class Foo
281
281
RUBY
282
282
end
283
283
end
284
+
285
+ context 'when ActiveSupport inflections are available' do
286
+ let ( :cop_config ) { { 'UseActiveSupportInflections' => true } }
287
+
288
+ before do
289
+ # Stub File.exist? to return true for the default inflector path
290
+ allow ( File ) . to receive ( :exist? ) . with ( './config/initializers/inflections.rb' ) . and_return ( true )
291
+
292
+ allow ( described_class ) . to receive ( :require ) . with ( 'active_support/inflector' ) . and_return ( true )
293
+ allow ( described_class ) . to receive ( :require ) . with ( './config/initializers/inflections.rb' ) . and_return ( true )
294
+ stub_const ( 'ActiveSupport::Inflector' , double ( 'ActiveSupport::Inflector' ) )
295
+ allow ( ActiveSupport ::Inflector ) . to receive ( :underscore ) . with ( 'TestClass' ) . and_return ( 'test_class' )
296
+ end
297
+
298
+ around do |example |
299
+ described_class . reset_activesupport_cache!
300
+ example . run
301
+ described_class . reset_activesupport_cache!
302
+ end
303
+
304
+ it 'uses ActiveSupport inflections for custom acronyms' do
305
+ allow ( ActiveSupport ::Inflector ) . to receive ( :underscore ) . with ( 'PvPClass' ) . and_return ( 'pvp_class' )
306
+
307
+ expect_no_offenses ( <<~RUBY , 'pvp_class_spec.rb' )
308
+ describe PvPClass do; end
309
+ RUBY
310
+ end
311
+
312
+ it 'registers an offense when ActiveSupport inflections suggest different path' do
313
+ allow ( ActiveSupport ::Inflector ) . to receive ( :underscore ) . with ( 'PvPClass' ) . and_return ( 'pvp_class' )
314
+
315
+ expect_offense ( <<~RUBY , 'pv_p_class_spec.rb' )
316
+ describe PvPClass do; end
317
+ ^^^^^^^^^^^^^^^^^ Spec path should end with `pvp_class*_spec.rb`.
318
+ RUBY
319
+ end
320
+
321
+ it 'does not register complex acronyms with method names' do
322
+ allow ( ActiveSupport ::Inflector ) . to receive ( :underscore ) . with ( 'PvPClass' ) . and_return ( 'pvp_class' )
323
+
324
+ expect_no_offenses ( <<~RUBY , 'pvp_class_foo_spec.rb' )
325
+ describe PvPClass, 'foo' do; end
326
+ RUBY
327
+ end
328
+
329
+ it 'does not register nested namespaces with custom acronyms' do
330
+ allow ( ActiveSupport ::Inflector ) . to receive ( :underscore ) . with ( 'API' ) . and_return ( 'api' )
331
+ allow ( ActiveSupport ::Inflector ) . to receive ( :underscore ) . with ( 'HTTPClient' ) . and_return ( 'http_client' )
332
+
333
+ expect_no_offenses ( <<~RUBY , 'api/http_client_spec.rb' )
334
+ describe API::HTTPClient do; end
335
+ RUBY
336
+ end
337
+ end
338
+
339
+ context 'when ActiveSupport inflections are not available' do
340
+ let ( :cop_config ) { { 'UseActiveSupportInflections' => true } }
341
+
342
+ before do
343
+ # Stub File.exist? to return true, but ActiveSupport loading fails
344
+ allow ( File ) . to receive ( :exist? ) . with ( './config/initializers/inflections.rb' ) . and_return ( true )
345
+ allow ( described_class ) . to receive ( :require ) . with ( 'active_support/inflector' ) . and_raise ( LoadError )
346
+ end
347
+
348
+ around do |example |
349
+ described_class . reset_activesupport_cache!
350
+ example . run
351
+ described_class . reset_activesupport_cache!
352
+ end
353
+
354
+ it 'falls back to default inflection behavior' do
355
+ expect_no_offenses ( <<~RUBY , 'pv_p_class_spec.rb' )
356
+ describe PvPClass do; end
357
+ RUBY
358
+ end
359
+
360
+ it 'registers offense when default inflection does not match' do
361
+ expect_offense ( <<~RUBY , 'pvp_class_spec.rb' )
362
+ describe PvPClass do; end
363
+ ^^^^^^^^^^^^^^^^^ Spec path should end with `pv_p_class*_spec.rb`.
364
+ RUBY
365
+ end
366
+ end
367
+
368
+ context 'when ActiveSupport loading raises an error' do
369
+ let ( :cop_config ) { { 'UseActiveSupportInflections' => true } }
370
+
371
+ before do
372
+ # Stub File.exist? to return true, but ActiveSupport loading raises an error
373
+ allow ( File ) . to receive ( :exist? ) . with ( './config/initializers/inflections.rb' ) . and_return ( true )
374
+ allow ( described_class ) . to receive ( :require ) . with ( 'active_support/inflector' ) . and_raise (
375
+ StandardError , 'Something went wrong'
376
+ )
377
+ end
378
+
379
+ around do |example |
380
+ described_class . reset_activesupport_cache!
381
+ example . run
382
+ described_class . reset_activesupport_cache!
383
+ end
384
+
385
+ it 'gracefully falls back to default behavior' do
386
+ expect_no_offenses ( <<~RUBY , 'pv_p_class_spec.rb' )
387
+ describe PvPClass do; end
388
+ RUBY
389
+ end
390
+ end
391
+
392
+ context 'when configured with custom InflectorPath' do
393
+ let ( :cop_config ) do
394
+ {
395
+ 'UseActiveSupportInflections' => true ,
396
+ 'InflectorPath' => './config/custom_inflections.rb'
397
+ }
398
+ end
399
+
400
+ around do |example |
401
+ described_class . reset_activesupport_cache!
402
+ example . run
403
+ described_class . reset_activesupport_cache!
404
+ end
405
+
406
+ context 'when inflector file exists' do
407
+ before do
408
+ # Stub ActiveSupport availability
409
+ allow ( described_class ) . to receive ( :require ) . with ( 'active_support/inflector' ) . and_return ( true )
410
+ stub_const ( 'ActiveSupport::Inflector' ,
411
+ double ( 'ActiveSupport::Inflector' ) )
412
+
413
+ # Stub File.exist? to return true for our custom path
414
+ allow ( File ) . to receive ( :exist? ) . with ( './config/custom_inflections.rb' ) . and_return ( true )
415
+
416
+ # Stub the require call for the inflector file
417
+ allow ( described_class ) . to receive ( :require ) . with ( './config/custom_inflections.rb' ) . and_return ( true )
418
+
419
+ # Mock the inflector behavior with custom acronyms
420
+ allow ( ActiveSupport ::Inflector ) . to receive ( :underscore ) . with ( 'HTTPSClient' ) . and_return ( 'https_client' )
421
+ allow ( ActiveSupport ::Inflector ) . to receive ( :underscore ) . with ( 'XMLParser' ) . and_return ( 'xml_parser' )
422
+ end
423
+
424
+ it 'loads the custom inflector file when it exists' do
425
+ expect ( File ) . to receive ( :exist? ) . with ( './config/custom_inflections.rb' ) . and_return ( true )
426
+ expect ( described_class ) . to receive ( :require ) . with ( './config/custom_inflections.rb' )
427
+
428
+ expect_no_offenses ( <<~RUBY , 'https_client_spec.rb' )
429
+ describe HTTPSClient do; end
430
+ RUBY
431
+ end
432
+
433
+ it 'uses custom inflections from the inflector file' do
434
+ expect_no_offenses ( <<~RUBY , 'https_client_spec.rb' )
435
+ describe HTTPSClient do; end
436
+ RUBY
437
+ end
438
+
439
+ it 'does not register with nested namespaces using custom inflections' do
440
+ allow ( ActiveSupport ::Inflector ) . to receive ( :underscore ) . with ( 'API' ) . and_return ( 'api' )
441
+
442
+ expect_no_offenses ( <<~RUBY , 'api/https_client_spec.rb' )
443
+ describe API::HTTPSClient do; end
444
+ RUBY
445
+ end
446
+
447
+ it 'registers offense when path does not match custom inflections' do
448
+ expect_offense ( <<~RUBY , 'http_s_client_spec.rb' )
449
+ describe HTTPSClient do; end
450
+ ^^^^^^^^^^^^^^^^^^^^ Spec path should end with `https_client*_spec.rb`.
451
+ RUBY
452
+ end
453
+ end
454
+
455
+ context 'when inflector file does not exist' do
456
+ before do
457
+ # Stub File.exist? to return false for our custom path
458
+ allow ( File ) . to receive ( :exist? ) . with ( './config/custom_inflections.rb' ) . and_return ( false )
459
+ end
460
+
461
+ it 'does not try to require the inflector file when it does not exist' do
462
+ expect ( File ) . to receive ( :exist? ) . with ( './config/custom_inflections.rb' ) . and_return ( false )
463
+ expect ( described_class ) . not_to receive ( :require ) . with ( './config/custom_inflections.rb' )
464
+ expect ( described_class ) . not_to receive ( :require ) . with ( 'active_support/inflector' )
465
+
466
+ # Should use default regex-based conversion: HTTPSClient -> https_client
467
+ expect_no_offenses ( <<~RUBY , 'https_client_spec.rb' )
468
+ describe HTTPSClient do; end
469
+ RUBY
470
+ end
471
+
472
+ it 'falls back to default camel_to_snake_case conversion without ActiveSupport' do
473
+ # Should use default regex-based conversion: HTTPSClient -> httpclient -> https_client
474
+ expect_no_offenses ( <<~RUBY , 'https_client_spec.rb' )
475
+ describe HTTPSClient do; end
476
+ RUBY
477
+ end
478
+ end
479
+
480
+ context 'when inflector file loading fails' do
481
+ before do
482
+ # Stub ActiveSupport availability
483
+ allow ( described_class ) . to receive ( :require ) . with ( 'active_support/inflector' ) . and_return ( true )
484
+ stub_const ( 'ActiveSupport::Inflector' ,
485
+ double ( 'ActiveSupport::Inflector' ) )
486
+
487
+ # Stub File.exist? to return true but require to fail
488
+ allow ( File ) . to receive ( :exist? ) . with ( './config/custom_inflections.rb' ) . and_return ( true )
489
+ allow ( described_class ) . to receive ( :require ) . with ( './config/custom_inflections.rb' ) . and_raise (
490
+ LoadError , 'Cannot load file'
491
+ )
492
+
493
+ # Set up fallback behavior
494
+ allow ( ActiveSupport ::Inflector ) . to receive ( :underscore ) . with ( 'HTTPSClient' ) . and_return ( 'https_client' )
495
+ end
496
+
497
+ it 'gracefully handles inflector file loading errors' do
498
+ expect ( File ) . to receive ( :exist? ) . with ( './config/custom_inflections.rb' ) . and_return ( true )
499
+ expect ( described_class ) . to receive ( :require ) . with ( './config/custom_inflections.rb' ) . and_raise ( LoadError )
500
+
501
+ # Should still work with basic ActiveSupport functionality
502
+ expect_no_offenses ( <<~RUBY , 'https_client_spec.rb' )
503
+ describe HTTPSClient do; end
504
+ RUBY
505
+ end
506
+ end
507
+ end
508
+
509
+ context 'when UseActiveSupportInflections is enabled with default inflector path' do
510
+ let ( :cop_config ) { { 'UseActiveSupportInflections' => true } }
511
+
512
+ around do |example |
513
+ described_class . reset_activesupport_cache!
514
+ example . run
515
+ described_class . reset_activesupport_cache!
516
+ end
517
+
518
+ before do
519
+ # Stub ActiveSupport availability
520
+ allow ( described_class ) . to receive ( :require ) . with ( 'active_support/inflector' ) . and_return ( true )
521
+ stub_const ( 'ActiveSupport::Inflector' , double ( 'ActiveSupport::Inflector' ) )
522
+ end
523
+
524
+ it 'uses default inflector path when not configured' do
525
+ allow ( File ) . to receive ( :exist? ) . with ( './config/initializers/inflections.rb' ) . and_return ( true )
526
+ allow ( described_class ) . to receive ( :require ) . with ( './config/initializers/inflections.rb' ) . and_return ( true )
527
+ allow ( ActiveSupport ::Inflector ) . to receive ( :underscore ) . with ( 'HTTPClient' ) . and_return ( 'http_client' )
528
+
529
+ expect ( File ) . to receive ( :exist? ) . with ( './config/initializers/inflections.rb' )
530
+
531
+ expect_no_offenses ( <<~RUBY , 'http_client_spec.rb' )
532
+ describe HTTPClient do; end
533
+ RUBY
534
+ end
535
+
536
+ it 'does not require default inflector file when it does not exist' do
537
+ allow ( File ) . to receive ( :exist? ) . with ( './config/initializers/inflections.rb' ) . and_return ( false )
538
+ allow ( ActiveSupport ::Inflector ) . to receive ( :underscore ) . with ( 'HTTPClient' ) . and_return ( 'http_client' )
539
+
540
+ expect ( File ) . to receive ( :exist? ) . with ( './config/initializers/inflections.rb' ) . and_return ( false )
541
+ expect ( described_class ) . not_to receive ( :require ) . with ( './config/initializers/inflections.rb' )
542
+
543
+ expect_no_offenses ( <<~RUBY , 'http_client_spec.rb' )
544
+ describe HTTPClient do; end
545
+ RUBY
546
+ end
547
+ end
284
548
end
0 commit comments