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.