Usando HasCustomFields en un plugin

Tengo un modelo en un plugin. El modelo es Pfaffmananger::Server y la tabla es pfaffmanager_servers. Por razones propias, quiero tener campos personalizados para este modelo (espero que haya casos en los que quiera extender los datos del servidor que probablemente solo se usarán en unas pocas instancias y no quiero tener un montón de campos mayormente sin usar en el modelo).

Aquí está la migración que crea la tabla:

class CreatePfaffmanagerServerCustomField < ActiveRecord::Migration[6.0]
  def change
    create_table :pfaffmanager_server_custom_fields do |t|
      t.integer :server_id, null: false
      t.string :name, limit: 256, null: false
      t.text :value
      t.timestamps null: false
    end
    add_index :pfaffmanager_server_custom_fields, [:server_id, :name]
  end
end

Si uso pfaffmanager_server_id en lugar de server_id, la migración falla porque no puede encontrar server_id, pero cuando uso pfaffmanager_server_id como nombre del campo, al intentar guardar un servidor después de crear un campo personalizado, obtengo:

  Pfaffmanager::ServerCustomField Load (0.4ms)  SELECT "pfaffmanager_server_custom_fields".* FROM "pfaffmanager_server_custom_fields" WHERE "pfaffmanager_server_custom_fields"."server_id" = 1
   (0.2ms)  ROLLBACK
PG::UndefinedColumn: ERROR:  column "pfaffmanager_server_id" of relation "pfaffmanager_server_custom_fields" does not exist
LINE 1: INSERT INTO pfaffmanager_server_custom_fields (pfaffmanager_...
    

Así que si la migración tiene el campo pfaffmanager_server_id, la migración falla, y si el nombre del campo es server_id, el guardado falla.

Estoy revisando save_custom_fields en concerns/has_custom_fields.rb, pero no termino de entender si hay alguna forma de sobrescribirlo.

¿Hay algo que pueda hacer aparte de refactorizar todo para usar server en lugar de pfaffmanager_server?

¿También actualizaste la llamada add_index para que apunte a pfaffmanager_server_id?

¡Muchas gracias, David!

Sí. Eso podría ser mi problema. Supongo que ese es el mensaje de error que debería haber incluido. Y puedo hacer que la migración funcione con cualquiera de los dos campos (aunque con pfaffmanager_server_id como nombre del campo, el nombre del índice supera los 63 caracteres, así que tengo que usar un nombre diferente para el índice).

Déjame intentarlo de nuevo.

server_id como nombre del campo

class CreatePfaffmanagerServerCustomField < ActiveRecord::Migration[6.0]
  def change
    create_table :pfaffmanager_server_custom_fields do |t|
      t.integer :server_id, null: false
      t.string :name, limit: 256, null: false
      t.text :value
      t.timestamps null: false
    end
    add_index :pfaffmanager_server_custom_fields, [:server_id, :name]
  end
end

Falló al guardar con:

 Pfaffmanager::ServerCustomField Load (3.8ms)  SELECT "pfaffmanager_server_custom_fields".* FROM "pfaffmanager_server_custom_fields" WHERE "pfaffmanager_server_custom_fields"."server_id" = 1
   (1.4ms)  ROLLBACK
PG::UndefinedColumn: ERROR:  column "pfaffmanager_server_id" of relation "pfaffmanager_server_custom_fields" does not exist
LINE 1: INSERT INTO pfaffmanager_server_custom_fields (pfaffmanager_...
                                                       ^

Así que está buscando pfaffmanager_server_id. Ahora, usemos pfaffmanager_server_id.

pfaffmanager_server_id como nombre del campo

Aquí está la migración:

class CreatePfaffmanagerServerCustomField < ActiveRecord::Migration[6.0]
  def change
    create_table :pfaffmanager_server_custom_fields do |t|
      t.integer :pfaffmanager_server_id, null: false
      t.string :name, limit: 256, null: false
      t.text :value
      t.timestamps null: false
    end
    add_index :pfaffmanager_server_custom_fields,
      [:pfaffmanager_server_id, :name],
      name: 'index_pfaffmanager_server_custom_fields_on_server_id_and_name'

  end
end

Esto es lo que ocurre cuando intento s.custom_fields (lo anterior pudo devolver nil para esto, pero no falló hasta que intenté guardar un campo personalizado). Registro el tipo de campo personalizado aquí:

 pry(main)> s.custom_fields
   (1.1ms)  SELECT "pfaffmanager_server_custom_fields"."name", "pfaffmanager_server_custom_fields"."value" FROM "pfaffmanager_server_custom_fields" WHERE "pfaffmanager_server_custom_fields"."server_id" = 1 ORDER BY id asc
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR:  column pfaffmanager_server_custom_fields.server_id does not exist
LINE 1: ...e" FROM "pfaffmanager_server_custom_fields" WHERE "pfaffmana...
                                                             ^

from /home/pfaffman/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/rack-mini-profiler-2.3.0/lib/patches/db/pg.rb:69:in `exec_params'
Caused by PG::UndefinedColumn: ERROR:  column pfaffmanager_server_custom_fields.server_id does not exist
LINE 1: ...e" FROM "pfaffmanager_server_custom_fields" WHERE "pfaffmana...
                                                             ^

Ahora está buscando server_id.

Parece que custom_fields lo quiere de una manera y save_custom_fields de otra.

¿No le importa cómo se llame el índice, verdad?

No creo que sí.

Creo que la segunda aproximación es probablemente la que deberíamos buscar (usar pfaffman_server_id como nombre de columna).

Me pregunto si sobrescribir este método podría ayudar:

Puedes verificar el valor actual en la consola así:

Server.new.custom_fields_fk

Si eso es server_id, entonces sugiero sobrescribir ese método en tu modelo Server:

class Server < ...
  def custom_fields_fk
    "pfaffman_server_id"
  end
end

O tal vez sea esta parte :thinking:

¡Muchas gracias! Entonces, Server.new.custom_fields_fk es pfaffmanager_server_id si uso pfaffmanager_serfver_id. Así que quizás debería usar server_id y luego sobrescribirlo como sugeriste arriba.

Parece que refresh_custom_fields_from_db está intentando usar los nombres incorrectos. No termino de entender qué hace _custom_fields.order....

Voy a ver qué pasa si uso server_id y si puedo luego sobrescribir custom_fields_fk.

¡Jaja! ¡Lo lograste! Muchas gracias. Cambié la referencia a server_id y luego hice esto en mi modelo server.rb:

    def custom_fields_fk
      @custom_fields_fk ||= "server_id"
    end

Mientras esto solo sobrescriba el comportamiento para este modelo y no rompa user_custom_field ni los demás, creo que estaré listo para volver a golpearme la cabeza en Best way to enforce permissions--controller or constraint?. Y luego podré agregar rutas para hacer cosas como llenar mis nuevos campos personalizados con… algo.

No puedo agradecerte lo suficiente. Probablemente me ahorraste un día entero. ¡Te debo una cerveza:beer:!

¡Genial! :tada: Si puedes pensar en una manera de hacerlo más genérico, definitivamente estaremos encantados de recibir un PR.

Si se me ocurre algo, te lo haré saber o enviaré una PR, pero tengo que pensar que un plugin que quiera usar campos personalizados está bastante en el límite.

OM_fk_G.

custom_fields_fk me pareció tan estúpido que supuse que era algo que pretendía eliminar y le había puesto un nombre estúpido para saber que era seguro eliminarlo más tarde. Era más tarde, así que lo eliminé. Y luego mis especificaciones fallaron.

Incluso había un comentario con un enlace a este tema en el código.