djezzzl / database_consistency

The tool to avoid various issues due to inconsistencies and inefficiencies between a database schema and application models.
MIT License
1.02k stars 43 forks source link

MissingAssociationClassChecker fails with `undefined method klass for nil:NilClass` #207

Closed eduardoj closed 1 year ago

eduardoj commented 1 year ago

Recently introduced MissingAssociationClassChecker checker fails for our setup. You can see the setup in this pull request, where the check finally got disabled: https://github.com/openSUSE/open-build-service/pull/14627

Content of the database_consistency_DATE file returned: ``` <===begin===> /usr/lib64/ruby/gems/3.1.0/gems/activerecord-7.0.4.3/lib/active_record/reflection.rb:814:in `source_reflection': undefined method `klass' for nil:NilClass (NoMethodError) through_reflection.klass._reflect_on_association(source_reflection_name) ^^^^^^ from /usr/lib64/ruby/gems/3.1.0/gems/activerecord-7.0.4.3/lib/active_record/reflection.rb:1021:in `derive_class_name' from /usr/lib64/ruby/gems/3.1.0/gems/activerecord-7.0.4.3/lib/active_record/reflection.rb:166:in `class_name' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/checkers/association_checkers/missing_association_class_checker.rb:30:in `report_template' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/checkers/association_checkers/missing_association_class_checker.rb:22:in `rescue in check' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/checkers/association_checkers/missing_association_class_checker.rb:18:in `check' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/checkers/base_checker.rb:25:in `report' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/checkers/base_checker.rb:34:in `report_if_enabled?' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:25:in `block (3 levels) in check' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:23:in `each' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:23:in `flat_map' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:23:in `block (2 levels) in check' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:22:in `each' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:22:in `flat_map' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:22:in `block in check' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:18:in `each' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:18:in `flat_map' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:18:in `check' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/base_processor.rb:30:in `reports' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/base_processor.rb:15:in `block in reports' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/base_processor.rb:14:in `each' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/base_processor.rb:14:in `flat_map' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/base_processor.rb:14:in `reports' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency.rb:104:in `run' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/bin/database_consistency:78:in `' from /usr/bin/database_consistency:25:in `load' from /usr/bin/database_consistency:25:in `
' /usr/lib64/ruby/gems/3.1.0/gems/activerecord-7.0.4.3/lib/active_record/reflection.rb:814:in `source_reflection': undefined method `klass' for nil:NilClass (NoMethodError) through_reflection.klass._reflect_on_association(source_reflection_name) ^^^^^^ from /usr/lib64/ruby/gems/3.1.0/gems/activerecord-7.0.4.3/lib/active_record/reflection.rb:1021:in `derive_class_name' from /usr/lib64/ruby/gems/3.1.0/gems/activerecord-7.0.4.3/lib/active_record/reflection.rb:166:in `class_name' from /usr/lib64/ruby/gems/3.1.0/gems/activerecord-7.0.4.3/lib/active_record/reflection.rb:793:in `klass' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/checkers/association_checkers/missing_association_class_checker.rb:19:in `check' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/checkers/base_checker.rb:25:in `report' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/checkers/base_checker.rb:34:in `report_if_enabled?' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:25:in `block (3 levels) in check' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:23:in `each' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:23:in `flat_map' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:23:in `block (2 levels) in check' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:22:in `each' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:22:in `flat_map' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:22:in `block in check' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:18:in `each' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:18:in `flat_map' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/associations_processor.rb:18:in `check' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/base_processor.rb:30:in `reports' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/base_processor.rb:15:in `block in reports' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/base_processor.rb:14:in `each' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/base_processor.rb:14:in `flat_map' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency/processors/base_processor.rb:14:in `reports' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/lib/database_consistency.rb:104:in `run' from /usr/lib64/ruby/gems/3.1.0/gems/database_consistency-1.7.13/bin/database_consistency:78:in `' from /usr/bin/database_consistency:25:in `load' from /usr/bin/database_consistency:25:in `
' <===end===> ```
djezzzl commented 1 year ago

Hi @eduardoj,

Thank you for reporting this!

Looking at your stack trace, the problem is in your application rather than the checker. I mean checker exposed the issue out load (not in silent mode).

One of your associations is "broken" because it doesn't have through_reflection when the ActiveRecord code expects it to be there. It could be a bug with ActiveRecord itself too.

Please add a simple put mode, the association before checkers/association_checkers/missing_association_class_checker.rb:19 to track down what causes this to fail.

through_reflection.klass._reflect_on_association(source_reflection_name)
                      ^^^^^^
    from /usr/lib64/ruby/gems/3.1.0/gems/activerecord-7.0.4.3/lib/active_record/reflection.rb:1021:in `derive_class_name'
    from /usr/lib64/ruby/gems/3.1.0/gems/activerecord-7.0.4.3/lib/active_record/reflection.rb:166:in `class_name'

P.S. You don't need to turn off the check entirely; you can disable only those that fail.

eduardoj commented 1 year ago

Hi @djezzzl!

That's right. You helped me to find a problem in my application.

After fixing the problem, I had no issues with the checker. I will close this issue.

Sorry if opening this issue, not related to this project created some noise. Thanks for your fast and detailed response!

djezzzl commented 1 year ago

That's right. You helped me to find https://github.com/openSUSE/open-build-service/pull/14645 in my application.

Glad that it helped!

Sorry if opening this issue, not related to this project created some noise. Thanks for your fast and detailed response!

No worries at all! I'm glad I could help. And, BTW, you helped me to understand that the error output could be improved with more context, such as failing checker information.

djezzzl commented 1 year ago

And also, you helped with an idea to write a checker to cover your case (when a through association is broken).

Have a great weekend!