Adicionando um método `get_like` à classe PluginStore

Gostaria de enviar um PR para adicionar o método get_like ao PluginStore.

O get_like buscará todos os registros de um determinado plugin cujo chave comece com a mesma palavra.

Caso de uso
Se um plugin tiver duas ou mais entidades que deseja armazenar, digamos maçãs e laranjas, ele armazenará os dados como apple_1, apple_2… e orange_1, orange_2, partindo de um plugin chamado fruits.

Para buscar todas as laranjas, basta chamar PluginStore.get_like(‘fruits’, ‘orange’) etc.

O que a equipe e os desenvolvedores de plugins acham disso?

Não acho que já tenha precisado desse padrão pessoalmente. Você já o viu em plugins públicos, ou apenas no que você está criando?

Além disso, não é muito longo para digitar:

PluginStoreRow.where("plugin_name = 'fruits' AND key LIKE 'orange%')

Recomendo adicionar uma função ou classe auxiliar ao seu plugin se fizer isso com frequência. Também certifique-se de ter índices apropriados para essas consultas.

Estava trabalhando nesta funcionalidade para o plugin custom-wizards e queria buscar todos os magos.
A chave utilizada é uma string única para cada mago. Esse caso foi tratado buscando todas as linhas do plugin e filtrando-as com base no value, o que funciona bem para um número pequeno de registros, mas não é ideal para um plugin grande. Parece que a tabela plugin_store_rows foi projetada para armazenar dados do tipo configuração para o plugin.

Pessoalmente, acho que isso abriria a possibilidade de armazenar dados que não sejam do tipo configuração na tabela plugin_store_rows.

(Desculpe se parecer estranho)

Em geral, recomendo criar suas próprias tabelas por meio de migrações se a PluginStoreRow não puder ser consultada da maneira que você deseja. Isso é feito comumente em vários plugins agora e funciona muito bem!

Ah, eu não sabia disso. Muito obrigado.

O padrão é usar o campo key na tabela plugin_store_rows para armazenar tanto um namespace quanto um identificador único, ou seja:

<namespace>_<id>

Esse padrão é menos comum nos plugins principais do Discourse atualmente, com um declínio geral no uso do PluginStore, por exemplo:

No entanto, ele ainda é utilizado em alguns lugares, incluindo o próprio código-base principal do Discourse, por exemplo, no modelo Reviewables.

Eu também uso esse padrão em vários plugins.

A principal razão para o uso desse padrão é que a tabela plugin_store_rows é utilizada por múltiplos plugins (e alguns serviços principais), portanto, as colunas de identificação, ou seja, id e plugin_name, não podem ser usadas para identificação interna dentro de cada sistema que utiliza o PluginStore. Assim, um sistema baseado em strings é usado na coluna key.

Em relação à alteração da estrutura do banco de dados a partir de um plugin, @gdpelican tem um bom post sobre isso:

Pessoalmente, ainda estou bastante cauteloso em fazer isso, pois ao trabalhar em um plugin de terceiros, você não tem controle sobre o namespace, se seu plugin será removido e quais alterações potencialmente conflitantes podem ser feitas no Discourse principal.

Como @gdpelican menciona, você precisa fornecer uma maneira para o usuário do plugin remover as alterações no banco de dados se ele desinstalar o plugin.

Forneça um método para os usuários limparem as alterações no seu banco de dados caso não queiram mais o seu plugin. Eu fiz isso com uma tarefa rake.

Acho que isso é muito técnico para a maioria dos usuários de plugins e representa um risco se eles não estiverem cientes desse detalhe.

Além disso, ainda não encontrei uma necessidade real de sair dos limites do PluginStore e CustomFields.

Dito tudo isso, pessoalmente, eu estaria a favor de um novo método nesse sentido no PluginStore, pois acho o padrão útil.

@david, estaria interessado em ouvir sua opinião sobre o acima também.

Como @eviltrout já disse, estamos usando migrações e tabelas dedicadas em vários plugins agora, com grande sucesso. A capacidade de impor restrições de banco de dados ajudou a melhorar o desempenho (pesquisas em qualquer coluna) e a integridade dos dados (por meio de índices exclusivos). Essas duas coisas provaram ser especialmente importantes na escala de alguns de nossos clientes hospedados — algo que eu realmente não considerei antes de entrar para a equipe.

O primeiro plugin substancial em que trabalhei foi o chat-integration, e implementei um “fake activerecord” muito complicado, que se apoia no armazenamento de plugins. Em retrospecto, tabelas dedicadas teriam sido uma escolha muito melhor, e posso considerar migrar o plugin para isso no futuro.

Concordo, quando se trata de modificar tabelas principais. Adicionar ou modificar colunas em tabelas existentes pode ter consequências não intencionais mais tarde e permanecerá mesmo se o plugin for desinstalado. Recomendo fortemente não fazer isso.

Tabelas dedicadas, por outro lado, apresentam risco relativamente baixo. Se o plugin for desinstalado, elas simplesmente permanecerão, sem quaisquer efeitos colaterais negativos (desde que você não introduza restrições de chave estrangeira). Deixar dados espalhados não é “pior” do que usar o armazenamento de plugins.

Em termos de limpeza, poderíamos considerar fornecer tarefas rake que “revertam” as migrações de plugins para limpeza. Mas, para ser honesto, não acho que isso será muito utilizado. Minha suposição é que as pessoas raramente desinstalam plugins e, quando o fazem, preferem manter os dados caso queiram reinstalá-los novamente.

Como autor desse código, talvez eu devesse esclarecer um pouco. Os IDs de prioridade são constantes e não dados relacionais. Eu recomendaria absolutamente não usar algo_id quando os IDs não são conhecidos previamente. Neste caso, cada prioridade é considerada um singleton, e imaginei que qualquer tabela que eu criasse seria essencialmente uma cópia do PluginStore!