Diferenças entre Phoenix e Rails através dos olhos de um convertido

O que mais impressionou o ávido hacker quando ele começou a estudar Elixir com Phoenix.

Nota


Eu sou uma pessoa simples e não vou me aprofundar. Portanto, haverá diferenças no nível camponês, mas nada será dito sobre a diferença no nível do lançamento do aplicativo, sobre os princípios da máquina virtual Erlang e o protocolo OTP.


Impressão principal


Elixir / Phoenix é muito parecido com o Rails e, ao mesmo tempo, nem um pouco como ele. Como algumas frases em inglês: individualmente, as palavras são familiares, mas juntas não são claras.


Erlang vs Ruby


Pensar em rublos e tentar escrever em um elixir é difícil. Você costuma chegar a becos sem saída, porque o que você quer fazer não é o que costumava fazer ... ou, de fato, você não quer isso.


Quanto ao resto, as pessoas escrevem livros sobre as diferenças de Erlang e Ruby, então serei breve. Para mim, as principais emboscadas foram a substituição de "locomotivas a vapor" por canos, com uma reorientação do pensamento ao funcionalismo (o benefício era inject experiência antiga inject e um amor compartilhado por inject / foldr ) e, subjetivamente, requisitos mais rigorosos de tipo de dados (embora oficialmente , ambos os idiomas com digitação dinâmica estrita).


A correspondência de padrões não causou nenhuma surpresa, e eu ainda não entendi por que havia tanta conversa sobre ele. Apenas uma ferramenta interessante.


Escopo geral


No Elixir, tudo está nos módulos. Nenhum escopo global. Chama C #.


Em outras palavras: o trilho é plano e, em alguns lugares, interfere na criação de uma hierarquia (lembro-me de uma vez que havia bugs com controladores em módulos). Elixir - pelo contrário, tudo está em módulos. No trilho, você pode adivinhar o objetivo do objeto pela classe pai e no elixir pelo nome completo da classe / módulo.


Compilabilidade


Por um lado, é isso que às vezes me faltava no trilho. Como você pode encontrar uma boa metade dos erros na compilação e não no tempo de execução na produção. A compilação, por outro lado, leva tempo. Mas, por outro lado, é necessário um pouco, e ainda não vi grandes projetos no elixir (e não é pelos preceitos de erlang escrever grandes monólitos). Para completar, os caras do elixir fizeram um ótimo trabalho ao recarregar dinamicamente o código e a página. E até agora, a velocidade do trabalho, associada à falta de zeus / primavera sem Deus, aquece minha alma.


Claro, isso também dá origem a contras, mas elas saem muito mais tarde. Em algum lugar na área de ambiente de produção e implantação. Mais sobre isso abaixo.


Aqui está um ponto interessante que fisicamente não pode acontecer no trilho: migrações e outras coisas que nos trilhos via rake in elixir exigem a compilação do projeto e algo assim pode acontecer: esqueceu de escrever rotas, o assistente de caminho na visão se refere a elas, mas as migrações caíram. No começo - extremamente incomum.


A documentação


O site com a documentação do elixir parece muito mais vigoroso que o rubidock e o apidok. Mas aqui está a quantidade de documentação e exemplos - é aqui que o ruby ​​/ rails está muito à frente. O Elixir carece de exemplos para tudo um pouco mais complicado que as fezes. E a descrição de alguns métodos, de fato, não foi além da assinatura. Foi difícil para mim, pois estava acostumado a esfregar uma abundância de exemplos e descrições, com alguns métodos de elixir. Às vezes, eu precisava bisbilhotar e experimentar por um longo tempo para entender como usar esse ou aquele método, porque não conheço o idioma tão bem que posso ler os códigos-fonte dos pacotes livremente.


Independência da localização do arquivo e do seu conteúdo


Como se costuma dizer "com grande poder vem grande responsabilidade". Por um lado, você pode fazer uma bacanal e decompor objetos para que o inimigo definitivamente não passe. Por outro lado, você pode nomear os caminhos de maneira mais lógica e clara, adicionando níveis lógicos de diretórios que não estão na hierarquia de classes. Em particular, podemos recordar pioneiros e afins com a ideia de combinar tudo relacionado à ação em um só lugar. No elixir, isso pode ser feito sem bibliotecas de terceiros e montes de classes simplesmente movendo corretamente os arquivos existentes.


Caminho de solicitação transparente


Se no Rails a pergunta sobre rack é um atributo indispensável em qualquer entrevista, porque o rail é a ponta do iceberg e, de tempos em tempos, você deseja criar seu middleware. Que no elixir de tal desejo não surge de todo (embora talvez eu ainda seja jovem e tudo esteja à frente). Há um conjunto explícito de pipeline através do qual a solicitação passa. E você pode ver claramente onde a sessão é buscada, onde o flash-messge é processado, onde o csrf é validado e tudo isso pode ser controlado como você quiser em um só lugar. No trilho, toda essa fazenda é parcialmente pregada e parcialmente espalhada em lugares diferentes.


Rotas de dentro para fora


No Rails, uma situação em que uma ação pode responder em vários formatos é a norma. Até (.:format) colocado nas rotas. No elixir, devido à propriedade mencionada acima com o pipeline, o pensamento de um formato analógico não aparece. Diferentes formatos seguem um pipeline diferente e têm URLs diferentes. Para mim é tão saudável.


Circuito no modelo


Geralmente é um conto de fadas. Conforme você descreve os campos do modelo, será assim. Nenhuma conversão implícita de tipos. Além disso, não há muletas para restringir o acesso ao campo, que está no banco de dados, mas, por algum motivo, ele não pode ser usado em um aplicativo da web.


Validações e retornos de chamada


Não há retornos de chamada no elixir. Lá tudo é mais direto. E acho que gosto disso.


Em vez do caminho de trilhos no conjunto de alterações do elixir, que combina parâmetros fortes, validações e alguns retornos de chamada. E o restante dos retornos de chamada passa pelo Multi , o que torna possível coletar várias operações, executá-las transacionalmente e processar o resultado.


Em suma, tudo é apenas diferente. No começo, isso é incomum. Em alguns lugares, isso me enfurece muito, porque você não pode simplesmente colocar outro retorno de chamada para tudo e não pensar em diferentes casos de negócios. E então você começa a perceber o "charme inexplicável" , porque precisa fazer o que é certo e não como costumava fazer.


Trabalhar com um banco de dados


Em vez de ActiveRecord, alguns Ecto.Repo , Ecto.Query e vários outros de seus irmãos apareceram. Contar todas as diferenças é um artigo separado. Portanto, direi as principais sensações subjetivas.


Na depuração, mais conveniente que o AR. Como existe um escopo geral, as constantes do caminho de carregamento são carregadas ao acessá-las e você pode simplesmente abrir os rails c , escrever User.where(email: 'Kane@nod.tb').order(:id).first e obtenha o resultado.


No Elixir, o console não é suficiente. É necessário executar várias ações:


  • importe um método para criar uma consulta sql: import Ecto.Query, only: [from: 2] ;
  • adicione classes para evitar o nome completo dos nomes:
    • alias MyLongApplicationName.User - para escrever User vez de MyLongApplicationName.User ;
    • alias MyLongApplicationName.Repo - da mesma forma para acessar uma classe que pode executar sql e retornar resultados;
  • e só agora você pode escrever from(u in User, where: u.email == "Kane@nod.tb") |> Repo.one

Por outro lado, no código do aplicativo, essas “formalidades” fornecem um código mais legível, além de haver a sensação de que você está no controle do que está acontecendo, e não está vivendo sua própria vida. Ou seja, você escolhe quais métodos, modelos e outros objetos precisa trabalhar, carrega-os explicitamente e os utiliza.


Nome da aplicação


Na imagem e semelhança do Rails, assumi que o nome do aplicativo é usado em um par de configurações e é isso. Portanto, não prestei atenção ao tamanho do nome. Mas em vão. No Elixir, o módulo com o nome do aplicativo é o nível superior na hierarquia de módulos do aplicativo Web e aparecerá em todos os lugares.


Liguei para minha sandbox Comindivion. E agora sofro um pouco, pois esse é um nome bastante longo e você precisa escrevê-lo constantemente. Tanto nos arquivos de classe quanto no console ao chamar qualquer coisa. A propósito, sim, quem se importa, aqui está a caixa de areia no GitHub .


N + 1


No Rails, nós o tiramos da caixa, mas no Elixir fora da caixa não existe esse problema. Lá, no estágio de montagem da solicitação, você pode especificar quais relações são necessárias e elas serão carregadas durante a execução dessa mesma solicitação. Não foi enviado? Você não terá acesso a esse relacional. Tudo é simples e bonito.


Processamento e resposta de solicitação


Em resumo: na fênix, tudo é mais óbvio do que no trilho.


Em todos os lugares


Como o estado não é armazenado em um monte de objetos diferentes, ele deve ser arrastado em um objeto. Lembra a request do ActionController , apenas mais abrangente. Ele é chamado em connection Phoenix. Ele contém tudo: request , flash , session e tudo. Ele aparece em uma ligação de tudo o que está conectado ao processamento da solicitação recebida.


Aqui e contras, uma vez que o primeiro é muito preguiçoso para esculpir em todos os lugares e não entender completamente o porquê. O trilho nesse sentido corrompe. Você escreve render ou flash e não há pensamentos de que essa ação esteja relacionada à conexão. E no Phoenix conn constantemente lembra você de trabalhar com uma conexão ou soquete específico, e não apenas os métodos são chamados e a mágica acontece por dentro.


Parcial e modelo


Em Phoenix, não há separação entre parcial e modelo. Em última análise, toda a função. Há mais um charme aqui: o trilho, mesmo no ambiente de prod, constantemente se arrasta por trás das visualizações no disco e gera IO mais uma sobrecarga para convertê-los de erb / haml / etc para html. E no Elixir tudo é uma função, incluindo visualizações. Compilou a visualização de uma vez por todas: obtém os argumentos, cospe html, não vai para o disco.


Visualizações


No Rails, view é entendida como parcial e templates, enquanto em Phoenix eles estão em templates, e views, grosso modo, existem diferentes maneiras de apresentar dados. Em particular, existem substituições de renderização.


Ou seja, por padrão, o controlador não renderiza nada. Tudo é chamado explicitamente. E se você não possui um parcial e realmente não precisa dele (por exemplo, no caso de json, quando é facilmente criado por uma classe de serviço), você redefine a renderização da seguinte maneira:


 def render("show.json", %{groups: groups}) do %{ groups: groups } end 

E o parcial não é mais necessário.


Heplers


Não há nenhum em Phoenix. E isso é demais! Nos auxiliares dos trilhos, geralmente, todo o lixo é coletado, que era preguiçoso para enfiar nos cantos ou apenas necessário para encher rapidamente alguma coisa.


No entanto, os métodos no controlador, visualizações, etc. Você pode adicionar. Isso é feito em um local especial web/web.ex e parece bastante decente.


Estática


No desenvolvimento, tudo está como sempre, exceto que na Phoenix eles ainda estragaram a recarga ao vivo, a primeira chamada "Uau!" efeito Foi quando mudei o css, retornei ao navegador e as alterações já estavam carregadas.


Na produção em Phoenix, o comportamento da estática é um pouco diferente do comportamento dos trilhos. Por padrão, os lugares onde você pode arrastar as estatísticas estão explicitamente registrados e você não pode simplesmente adicionar arquivos aos ativos para distribuí-los. Ainda existe um mapeamento de ativos padrão, para que você não vagueie pelo FS mais uma vez, mas imediatamente pegue o arquivo desejado e o entregue.


Ativos


Fora da caixa em Phoenix - brunch . Você pode substituí-lo pelo webpack . Mas há uma piada bastante verdadeira sobre o fato de muitos projetos serem dobrados no estágio de configuração do webpack.


Em resumo, js e css são mais ou menos coletados, mas com o restante da estática no brunch, não é muito. Copie-o com as mãos diretamente para o projeto a partir de node_modules (não gosto dessa opção) ou escreva ganchos no bash. Por exemplo, assim .


Trabalhar com SSL


Fora da caixa em Phoenix, existe um pequeno servidor http chamado cowboy . Parece um puma rubi. Eles ainda têm o mesmo número de estrelas no GitHub. Mas, de alguma forma, não obtive configurações SSL em nenhuma das opções acima. Especialmente com o Let's Encrypt , um arquivo de configuração adicional do servidor da web e renovação regular do certificado. Então, como um servidor http - ok, mas para ssl eu pego um proxy para localhost via apache / nginx.


Implantar


Geralmente é diferente em comparação com o trilho. No Rails, na versão mínima, ele dirigiu o nabo para o servidor, dançou com um pandeiro para pacotes, configurações, ativos e lançou o aplicativo. E o elixir compila e cavar no bonde persuadir um nabo não vai andar. Precisa coletar o pacote. E aqui começa:


  • você descobrirá por que os aplicativos são necessários no mix.exs , porque sem mix.exs los corretamente no mix.exs , erros maravilhosos;
  • você aprende que as variáveis ​​de ambiente são compiladas no momento em que o pacote foi criado, e não no momento em que foi lançado, e isso é pela primeira vez uma grande surpresa; então você aprende sobre relx junto com RELX_REPLACE_OS_VARS=true e deixa um pouco de lado;
  • você está surpreso que no pacote compilado para produção não há nada semelhante ao rake, em particular não há migrações e você precisa executá-las de alguma forma separadamente, por exemplo, do ambiente dev através do encaminhamento de porta para o banco de dados (ou via eDeliver , que fará a mesma coisa) .

E então, conforme você lida com o acima, os profissionais começam:


  • você pode tornar o pacote auto-suficiente e não colocar nada das dependências no veículo de combate; basta descompactar o tarball e executar o conteúdo; a menos que erlang possa ser necessário para ser implementado, pois sua versão de compilação cruzada é um pouco trivial na montagem;
  • Você pode fazer uma versão de atualização para implantar sem tempo de inatividade.

Debag


Elixir tem Pry e funciona como rubis. Existe até uma contraparte dos rails c que se parece com o iex -S mix .


Mas na produção, você precisa usar o console de maneira diferente, pois o pacote foi criado e o mix não está nele. Você precisa se conectar a um processo de trabalho. Isso é radicalmente diferente dos trilhos e, no começo, você gasta muito tempo pesquisando o caminho para lançar o console elixir em produção, porque está procurando algo semelhante ao trilho. Como resultado, você entende que precisa fazer tudo diferente e chamar algo como: iex --name trace@127.0.0.1 --cookie 'from_env' --remsh 'my_app_name@127.0.0.1' .


Para continuar ...


Ah, esqueci alguma coisa. Oh bem. É melhor você nos contar o que o surpreendeu no Elixir, em comparação com outros idiomas.

Source: https://habr.com/ru/post/pt423459/


All Articles