Differences Phoenix and Rails through the eyes of the convert

    What most caught his eye to the avid rubist when he had just begun to study Elixir with Phoenix.

    Note


    I am a simple man and I will not go deep. Therefore, there will be differences between workers and peasants, but nothing will be said about the difference at the level of application launch, about the principles of the Erlang virtual machine and the OTP protocol.


    Main impression


    Elixir / Phoenix is ​​very similar to Rails and at the same time does not look like him at all. Like some English phrases: individually the words are familiar, but together it is not clear.


    Erlang vs Ruby


    Thinking on ruby ​​and trying to write on elixir is hard. You regularly come to a dead end, because what you want is not at all what you used to do ... or, in fact, you don’t want it at all.


    And the rest, about the differences between Erlang and Ruby, people write books, so I will be brief. For me, the main ambushes were replacing rail "locomotives" with a pipe, reorienting thinking to functionalism (the good was the old Haskell experience and the general love of inject/ foldr) and, subjectively, more stringent requirements for data types (although, officially, both language with strict dynamic typing).


    The pattern-matching did not cause any surprise, and I still did not understand why there was so much talk about him. Just an interesting tool.


    Common scop


    In Elixir, everything lies in the modules. No global scoop. Bounces C #.


    In other words: a flat rail doesn’t make it possible to create a hierarchy in some places (I remember there were once bugs with controllers in modules). Elixir - on the contrary, all by modules. In the rail, the object's purpose is guessed by the parent class, and in the elixir - by the full name of the class / module.


    Compilability


    On the one hand, this is what I sometimes lacked in the rail. Since you can find a good half of errors right at compilation, and not in runtime on production. On the other hand, compilation takes time. But on the third hand, it needs a bit, and I have not yet seen large projects on the elixir (and not according to Erlang’s covenants to write large monoliths). Finally, the guys from the elixir did a great job on dynamically reloading the code and page. And so far, the speed of work, coupled with the lack of godless zeus / spring, warms my soul.


    Of course, this also creates disadvantages, but they come out much later. Somewhere around the production environment and deployment. This will be below.


    Immediately there is an interesting moment that cannot physically happen in the rail: migrations and other things that in rails through rake, in elixir require compilation of the project and something like this might happen: forgot to write routes, they are referenced by path-helper in the view, and migrations have fallen off. At first - wildly unusual.


    Documentation


    The site with the elixir documentation looks much more cheerful than rubidok and apidok. But the volume of documentation and examples - this is what ruby ​​/ rails is far ahead. In Elixir there are a lot of examples for everything that is a little more complicated than a stool. And the description of some methods, in fact, did not go beyond the signature. I, as accustomed by rubies to an abundance of examples and descriptions, was difficult with some elixir methods. Sometimes I had to poke and experiment for a long time in order to understand how to use one or another method, because I don’t know the language well enough to read the source code of the packages freely.


    Independence of file location from its contents


    As the saying goes "with great power comes great responsibility". On the one hand, you can mess up the orgy and decompose objects so that the enemy will not pass. On the other hand, you can name paths more logically and visually, adding logical levels of directories that are not in the class hierarchy. In particular, we can recall the trailblazer and his ilk with the idea of ​​combining everything connected with action in one place. In the elixir, this can be done without third-party libraries and heaps of classes simply by correctly shifting existing files.


    Transparent query path


    If in Rails the question about rack is an indispensable attribute of any interview, because the rail is the tip of the iceberg and periodically you want to make your middleware. Then in the elixir of such a desire does not arise at all (although maybe I'm still young and everything ahead). There is an explicit set of pipeline through which the request passes. And there you can clearly see where the session is fetching, where the flash-messge is processed, where the csrf is validated and all this can be managed as it pleases in one place. In the rail all this farm is partially nailed and partially scattered in different places.


    Routes inside out


    In Rails, the situation when one action can respond in several formats is the norm. They even (.:format)laid right in the routes. In the elixir, because of the above-mentioned property with the pipeline, the thought of the analog format does not appear at all. Different formats go on different pipeline and have different urls. For me it is so great.


    Scheme in the model


    This is generally a fairy tale. How to describe the fields of the model, so be it. No implicit caste of types. Plus, there are no crutches to prohibit access to the field that is in the database, but for some reason it cannot be used in the web application.


    Validations and Callbacks


    There are no callbacks in the elixir. There is more and more rectilinearly. And it seems I like it.


    Instead of a rails-way in the changeset elixir , which combines strong_parameters, validations and some callbacks. And the remnants of callbacks go through Multi , which makes it possible to collect a bunch of operations, execute them transactionally and process the result.


    In short, everything is just different. At first it is unusual. Then in places he is wildly enraged, because you can’t just insert another callback for everything and not think about different business cases. And then you begin to notice the "inexplicable charm" , because you have to do it right, and not as used to


    Work with DB


    Instead of ActiveRecord, some Ecto.Repo , Ecto.Query and a few of their fellows appeared. To tell all the differences - this is a separate article. Therefore, I will say the main subjective feelings.


    In debug it is more convenient than AR. Since there is a common scoop, the constants from the load path are loaded when they are accessed and you can simply open rails c, write User.where(email: 'Kane@nod.tb').order(:id).firstand get the result.


    In Elixir, the console is not enough. We need to do a number of actions:


    • zaimportit method for building sql-query: import Ecto.Query, only: [from: 2];
    • fill classes so as not to write their full names through the dot:
      • alias MyLongApplicationName.User- to MyLongApplicationName.Userwrite instead User;
      • alias MyLongApplicationName.Repo - Likewise for accessing a class that can execute sql and give results;
    • and only now you can write from(u in User, where: u.email == "Kane@nod.tb") |> Repo.one

    On the other hand, in the application code these “formalities” give more readable code, plus there is a feeling that you control what is happening, and not it lives its own life. That is, you yourself choose which methods, models and other objects are needed for work, obviously you load and use them.


    Application Name


    In the image of Rails, I thought that the name of the application is used in a couple of configs and that's it. Therefore, the length of the name did not pay attention. And in vain. In Elixir, the module with the name of the application is the top level in the module hierarchy of the web application and it will appear everywhere.


    I've called my sandbox Comindivion. And now I suffer a little, since this is a rather long title and you need to write it constantly. Both in class files and in the console when you call anything. By the way, yes, who cares, here's a sandbox on GitHub .


    N + 1


    In Rails, we have it out of the box, but in Elixir there is no such problem out of the box. There, at the request build stage, you can specify which relays will be needed and they will be loaded during the execution of this request itself. Did not upload? You will not have access to this report. Everything is simple and beautiful.


    Request processing and response


    In short: in the phoenix everything is more clearly than in the rail.


    Everywhere conn


    Since the state is not stored in a heap of different objects, it has to be dragged along in one object. Reminds requestof ActionController, only more comprehensive. He is called in Phoenix connection. It contains everything in general: and request, and flash, sessionand everything is all everything. He also appears in the call for everything related to the processing of the incoming request.


    Here and minuses, as the first is very lazy to sculpt everywhere connand not fully understand why. Rail in this regard corrupts. You write render or flash and you don’t think that this is an action with a connection. And in Phoenix it connconstantly reminds of working with a specific connection or socket, and not just methods are invoked and magic happens inside.


    Partial & template


    In Phoenix, there is no separation between partial and template. Ultimately, all function. Immediately, there is another beauty: the rail, even in the prod environment, constantly climbs behind the views on the disk and generates IO plus an overhead to transform them from erb / haml / etc to html. And in Elixir everything is a function, and views as well. We compiled the view once and that’s it: it takes arguments, spits out html, it doesn't go to disk.


    Views


    In Rails, view means partial and templates, while in Phoenix they are in templates, and in views, roughly speaking, there are different ways of presenting data. In particular, there are "overrides" of the render'a.


    That is, by default, the controller renders nothing. Everything is called explicitly. And if you do not have a partial and you don’t need it (for example, in the case of json, when it is easy to build by the service class), you redefine the render like this:


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

    And the partial is no longer needed.


    Heplers


    They are not in Phoenix. And this is awesome! For in rail helpers, usually, any rubbish is collected that was either lazy to shove in the corners, or just needed a quick mess of something.


    However, the methods in the controller, views, and so on. You can add. This is done in a special place web/web.exand looks pretty decent.


    Statics


    In the development, everything is as usual, except that in the phoenix they are still screwed on a live reload, the first one causing “Wow!” Effect. This is when I changed css, I returned to the browser, and there the changes were already loaded.


    In production in Phoenix, the behavior of statics is slightly different than that of the rails. By default, there are clearly defined places from where you can drag a static and you cannot just add files to assets to distribute them. There is also a mapping of default assets, so that once again you don’t wander around the file system, but take the right file right away and give it away.


    Assets


    Out of the box in Phoenix - brunch . Can be replaced by webpack . But there is quite a truthful joke about the fact that many projects are bent at the stage of setting up a webpack.


    In short, js and css are more or less collected, but with the rest of the statics in the brunch is not very. It is either copy-paste it directly into the project from node_modules (I don’t like this option at all), or write hooks on the bash. For example, so .


    Work with SSL


    Out of the box in Phoenix there is a little http server called cowboy . It looks like ruby puma . They even have about the same number of stars on GitHub. But somehow I didn’t go to the SSL setting in any of the above. Especially with Let's Encrypt , an additional file of the web server config and regular certificate renewal. So, as an http server, it's ok, and for ssl, I take a proxy on localhost via apache / nginx.


    Depla


    It is generally different compared to the rail. In Rails, in its minimal form, he bent his turnip to the server, danced with a tambourine for a bundle, configs, assets, and launched the application. And the elixir is compiled andbury the trambow turnip is not a ride. Need to build a package. And here begins:


    • you find out why applications are needed mix.exs, because without correctly indicating them in the sale of wonderful errors;
    • you learn that environment variables are compiled at the time of package building, and not at the time of its launch, and this causes wild surprise for the first time; then you learn about relx with RELX_REPLACE_OS_VARS=trueand a little bit;
    • you are surprised that in the assembled package for production there is nothing like rake, in particular, there are no migrations and they need to be somehow separately run, for example, from a girlfriend through the port forwarding to the database (or via eDeliver , which does approximately the same thing) .

    And then, as with the above described, you will understand, the advantages begin:


    • you can make the package self-sufficient and don’t put anything at all on dependencies on the combat machine; just tarball unpack and run the contents; except erlang may be necessary, as its cross compile version is a bit non-trivial in the assembly;
    • You can do an upgrade release to deploy without downtime.

    Debag


    Elixir has Pry and works like rubies. There is even an analogue rails cthat looks like iex -S mix.


    But in the production console, you have to use it differently, since the package is assembled and not mixin it. You have to connect to a running process. This is radically different from the rails and at the beginning you spend a lot of time googling the way to start the elixir-console in production, because you are looking for something similar to the rail. In the end, you realize that all you need to do differently and cause something like: iex --name trace@127.0.0.1 --cookie 'from_env' --remsh 'my_app_name@127.0.0.1'.


    To be continued...


    Fuh, anyway, I forgot something. Anyway. Better tell me what surprised you in Elixir, compared to other languages.

    Only registered users can participate in the survey. Sign in , please.

    And anyway, how are you with Elixir?


    Also popular now: