SchemaPlus / schema_associations

ActiveRecord extension that automatically (DRY) creates associations based on the schema
Other
46 stars 8 forks source link

Can't determine table_name automatically for models derived from abstract classes #12

Open dmeranda opened 8 years ago

dmeranda commented 8 years ago

If you have a model class that is derived from an abstract class then the automatic determination of the table name does not appear to work. Consider that we have a very minimal table called "colors", with no foreign keys, then with the model classes:

# app/models/color_base.rb
class ColorBase < ActiveRecord::Base
  self.abstract_class = true
end

# app/models/color.rb
class Color < ColorBase
end

Without schema_associations you can enter:

$ rails console
irb> Color.table_name
=>  "colors"

But with schema_associations listed in the Gemfile; using the default configurations, you get an error:

$ rails console
irb> Color.table_name
  ColorBase Reverse Foreign Keys (1.4ms)          SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
          FROM information_schema.key_column_usage
         WHERE table_schema = SCHEMA()
           AND referenced_table_schema = table_schema
         ORDER BY constraint_name, ordinal_position;

  ColorBase Foreign Keys (0.8ms)  SHOW CREATE TABLE ``
Mysql2::Error: Incorrect table name '': SHOW CREATE TABLE ``
ActiveRecord::StatementInvalid: Mysql2::Error: Incorrect table name '': SHOW CREATE TABLE ``
    from /.../activerecord-4.2.4/lib/active_record/connection_adapters/abstract_mysql_adapter.rb:305:in `query'
    from /.../activerecord-4.2.4/lib/active_record/connection_adapters/abstract_mysql_adapter.rb:305:in `block in execute'
    from /.../activerecord-4.2.4/lib/active_record/connection_adapters/abstract_adapter.rb:473:in `block in log'
    from /.../activesupport-4.2.4/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
    from /.../activerecord-4.2.4/lib/active_record/connection_adapters/abstract_adapter.rb:467:in `log'
    from /.../activerecord-4.2.4/lib/active_record/connection_adapters/abstract_mysql_adapter.rb:305:in `execute'
    from /.../activerecord-4.2.4/lib/active_record/connection_adapters/mysql2_adapter.rb:231:in `execute'
    from /.../activerecord-4.2.4/lib/active_record/connection_adapters/mysql2_adapter.rb:235:in `exec_query'
    from /.../schema_plus_core-0.6.0/lib/schema_plus/core/active_record/connection_adapters/mysql2_adapter.rb:51:in `block in exec_query'
...

Interestingly, if you immediately retry the same thing it works:

irb> Color.table_name
  Color Reverse Foreign Keys (1.7ms)          SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
          FROM information_schema.key_column_usage
         WHERE table_schema = SCHEMA()
           AND referenced_table_schema = table_schema
         ORDER BY constraint_name, ordinal_position;

  Color Foreign Keys (0.7ms)  SHOW CREATE TABLE `colors`
=> "colors"

There are no problems if do-nothing abstract base class is not in the mix. The only schema_plus gems that are loaded are:

schema_monkey (2.1.3)
schema_plus_core (0.6.0)
schema_plus_foreign_keys (0.1.2)
schema_associations (1.2.3)

If I had to guess it seems like an initialization ordering thing, but I don't know the code well enough.

dmeranda commented 8 years ago

Just poking around in the code I added a one-line patch which appears to work. But as I really don't know how all the code works I have no idea if this is the correct thing to do.

--- lib/schema_associations/active_record/associations.rb-1.2.3 2015-11-10 05:01:07.607501051 -0500
+++ lib/schema_associations/active_record/associations.rb   2015-11-14 01:35:01.260257705 -0500
@@ -80,6 +80,7 @@
       def _load_schema_associations_associations #:nodoc:
         return if @schema_associations_associations_loaded
         @schema_associations_associations_loaded = true
+        return if abstract_class?
         return unless schema_associations_config.auto_create?

         reverse_foreign_keys.each do | foreign_key |
ScottSmix commented 8 years ago

It would be nice to have a clean way to have the functionality of "schema_associations" in a base model class when schema associations are turned off globally and have it inherited. For example:

# app/models/schema_plus_base.rb
class SchemaPlusBase < ActiveRecord::Base
  self.abstract_class = true
  schema_associations
end

# app/models/color.rb
class Color < SchemaPlusBase
end

In the example above, schema_associations does not work in the "Color" class.

ronen commented 8 years ago

@ScottSmix i'd be happy to accept a PR. No time to dive into it myself though.