Usando HasCustomFields em um plugin

Tenho um modelo em um plugin. O modelo é Pfaffmananger::Server e a tabela é pfaffmanager_servers. Por motivos específicos, quero ter campos personalizados para este modelo (espero que haja casos em que eu precise estender os dados do servidor de formas que provavelmente serão usadas apenas em algumas instâncias, e não quero uma infinidade de campos subutilizados no modelo).

Aqui está a migração que cria a tabela:

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

Se eu usar pfaffmanager_server_id em vez de server_id, a migração falha porque não consegue encontrar server_id. No entanto, quando uso pfaffmanager_server_id como nome do campo, ao tentar salvar um servidor após criar um campo personalizado, recebo o seguinte erro:

  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_...
    

Ou seja, se a migração tiver o campo pfaffmanager_server_id, ela falha; se o nome do campo for server_id, a operação de salvar falha.

Estou analisando o método save_custom_fields em concerns/has_custom_fields.rb, mas não consigo identificar se há alguma maneira de sobrescrevê-lo.

Existe algo que eu possa fazer além de refatorar tudo para usar server em vez de pfaffmanager_server?

Você também atualizou a chamada add_index para apontar para pfaffmanager_server_id?

Muito obrigado, David!

Sim. Isso pode ser o meu problema. Acho que essa é a mensagem de erro que eu deveria ter incluído. E consigo fazer a migração funcionar com qualquer um dos campos (embora, com pfaffmanager_server_id como nome do campo, o nome do índice fique > 63 caracteres, então preciso usar um nome diferente para o índice.

Vou tentar novamente.

server_id como nome do 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

Falha ao salvar com:

 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_...
                                                       ^

Então, ele está procurando por pfaffmanager_server_id. Agora, vamos usar pfaffmanager_server_id.

pfaffmanager_server_id como nome do campo

Aqui está a migração:

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

Eis o que acontece quando tento s.custom_fields (o código acima conseguia retornar nil para isso, mas só falhou quando tentei salvar um campo personalizado). Eu chamei register_custom_field_type aqui:

 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...
                                                             ^

Agora ele está procurando por server_id.

Então, parece que custom_fields espera de um jeito e save_custom_fields espera do outro.

Não importa qual seja o nome do índice, certo?

Não acho que importa, não.

Acho que a segunda abordagem é provavelmente a que devemos seguir (usando pfaffman_server_id como nome da coluna).

Será que sobrescrever esse método pode ajudar:

Você pode verificar o valor atual no console assim:

Server.new.custom_fields_fk

Se for server_id, sugiro sobrescrever esse método no seu modelo Server:

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

Ou talvez seja esta parte :thinking:

Muito obrigado! Então, Server.new.custom_fields_fk é pfaffmanager_server_id se eu usar pfaffmanager_serfver_id. Talvez eu devesse usar server_id e depois sobrescrever como você sugeriu acima.

Parece que refresh_custom_fields_from_db está tentando usar os nomes errados. Não entendo muito bem o que _custom_fields.order... está fazendo.

Vou ver o que acontece se eu usar server_id e se consigo sobrescrever custom_fields_fk depois.

Ha! Você conseguiu! Muito obrigado. Eu mudei para server_id e depois fiz isso no meu modelo server.rb:

    def custom_fields_fk
      @custom_fields_fk ||= "server_id"
    end

Desde que isso vá sobrescrever apenas para este modelo e não quebre user_custom_field e afins, acho que estou pronto para começar a me dar uma surra novamente em Best way to enforce permissions--controller or constraint?. E então posso adicionar rotas para fazer coisas como preencher meus novos campos personalizados com… alguma coisa.

Não consigo agradecer o suficiente. Você provavelmente me economizou um dia inteiro. Devo uma :beer: para você!

Ótimo! :tada: Se você conseguir pensar em uma maneira de torná-lo mais genérico, certamente será bem-vindo um PR.

Se me ocorrer algo, eu aviso você ou submeto um PR, mas tenho que pensar que um plugin que queira usar campos personalizados está bem na borda.

OM_fk_G.

custom_fields_fk parecia tão estúpido que presumi que era algo que eu pretendia excluir e o nomeei de forma estúpida para saber que era seguro excluí-lo mais tarde. Era mais tarde, então eu o excluí. E então minhas especificações falharam.

Havia até um comentário com um link para este tópico no código.