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