šŸ“œ ā¬†ļø ā¬‡ļø

I build websites! Like a boss

Like a boss

www.youtube.com/watch?v=NisCkxU544c

After watching the presentation of Yuri ( yrashk ) with Erlang Conference about web frameworks in Erlang, I became interested and decided to make saytik not on node.js, as I originally intended, but using some Erlang framework.

In the end, I came across a Chicago Boss framework. I have heard about the framework myself before, but I haven’t been able to use it yet.
')


What does the boss give us? In general, they position themselves as ā€œrails-type, but on an erlang.ā€ With the rails, by the way, I also didn’t have anything at all, so I didn’t care. But I’ve dealt with MVC because at work we have a solid ASP.NET MVC2. So the basic concepts are clear.

BossDB is available; it is, in fact, ActiveRecord, written using parameterized modules, supporting different backends. Memory, Mnesia, MongoDB, MySQL, Postgres. According to the developers, to support the new database, you need to write about three hundred lines of code.

There are Django Templates, with almost full coverage of all features, filters, and more.

There are other things that I haven’t really played with yet ...

Installation


With the installation on ubunt there was a small problem that is treated by downloading the old version of erlang and copying one header file from there (lib / kernel / src / inet_dns.hrl), which for some reason is missing from the ubuntov delivery; still had to put the erlang-dev package.

After this is done
make make app NewSite cd ../NewSite 


Getting to understand ...



In principle, there is a good tutorial An Evening with Chicago Boss . But it is a little outdated, and slightly does not correspond to the realities of the latest versions.

Tasks




Project structure


 /controller /view /model /log /static /test 

all three are clear
 /admin -  ,  /    ,    /ebin -  ,      /lang -   .          /lib -   ,      .       /mail -        ,       


If you run the project through ./start-dev.sh, then the boss will do a hot reboot of the whole machine, so you can develop it without restarting the server, which is very convenient.

Model


Model API

The model is a parameterized erlang module, a specific thing, I don't know much about it. For now, just using it as is.

For example, user model
 -module(user, [Id, FirstName, LastName, Email, PasswordHash, CreateTime]). -compile(export_all). 


This is already a model. It has properties, as well as automatically generated field access methods. Properties that end in Id or Time are special. Some - to create links and dependencies between objects, the second will only accept tuples in a format that gives erlang: now ()

After the model code has been saved in /models/user.erl , the admin panel will access the list of current models and class documentation.

You can create validation methods for the model, but we’ll skip this for now.

Bossdb


BossDB API

This is an API for working with a database.
The boss stores all entries with an Id of the following form: <model_atom> is <unique_id> , where model_atom is an atom described in the model module :), and unique_id is a numeric identifier that does not repeat even for different models, so if you have the user and article, user-1 and article-1 models cannot exist. Considering that the numbers in the Erlang are limited by the amount of RAM, you can not worry about id overflow :).

Of particular interest are search operations. They have a short form, writing down with mathematical symbols, such as ε, āˆ‰, ≁ and others.

Sampling can be done by transferring string Id records. Because it is prefixed with the model name - there will be no confusion.
 User = boss_db:find("user-1"). 


Controller


Controller API

The controller is also a parameterized module. But he has only one parameter. This is the field in which the http request will be located. The request has methods for accessing form fields (in the case of POST) and headers.

 -module(user_controller, [Req]). -compile(export_all). index('GET', []) -> ok. 


Here is a simple controller that simply renders the default template when a GET request is made to the address / user / index

You can write a controller that displays the user on his Id, as well as understanding the different methods of issuance.
Parsing a URL is as follows:
 -module(user_controller, [Req]). -compile(export_all). index('GET', []) -> {ok, [{users, boss_db:find(user, [])}]}; index('GET', ["id", Id, "method", Method]) -> case boss_db:find(Id) of {error, Reason} -> boss_flash:add(Req, error, "Invalid User", "User not found"), ok; {user, User} -> case Method of "json" -> {json, User} %%, ,   -    _ -> {ok, [{user, User}]} end end. 


What is going on here?
If an empty request comes to us - then find all the objects of the User model, and render the default template by passing a list of parameters to it, one of which will be users, containing all users.
If the request has id & method parameters - try to find the user by Id, in case of failure, create a boss_flash with an error message and render the template.
If the user is found then do the following — if the method contains the string ā€œjsonā€, return the data as a JSON object. For any other method, render the HTML template with user data.

Template


Template API
uses the Django template system, through the ErlyDTL library

For example, here is a template for our / user page.
 {{boss_flash}} <!--        ,     --> {% if users %} <h1>{% trans "User List" %}</h1> {% for user in users %} {% trans "First Name" %} : {{ user.first_name }}</br> {% trans "Last Name" %} : {{ user.last_name }}</br> <a href="/user/id/{{user.id}}/method/html">{% trans "View" %}</a> <a href="/user/id/{{user.id}}/method/json">{% trans "JSON" %}</a> {% endfor %} {% endif %} {% if user %} <h1>{% trans "User Info" %}</h1> {% trans "First Name" %} : {{ user.first_name }}</br> {% trans "Last Name" %} : {{ user.last_name }} {% endif %} 


The trans function will automatically translate strings. Also, all the strings for translation are automatically selected from the template files and go to the translation web interface in the admin panel. Files - regular * .po
boss_flash is a special construct for displaying temporary messages. No need to steam with sessions or passing a message through parameters or hidden fields. Very comfortably. An identical mechanism is used in validator messages in ASP.Net MVC.

Authorization


Login made cleverly. In each controller, you can write the before_ / 1 function, which receives the name action and for it should return either a tuple {ok, AdditionalParameters} , or {redirect, "/ url / to / redirect"} .
Usually it is stored in a separate module in the / lib folder .

 -module(auth_lib). -export(require_authentication/1). require_authentication(Req) -> case boss_session:get_session_data(Req, principal) of {error, Reason} -> {redirect, "/login"}; PrincipalId -> case boss_db:find(PrincipalId) of {error, Reason} -> boss_flash:add(Req, error, "User not found", Reason), {redirect, "/error"}; {user, Principal} -> {ok, Principal} % Principal -     action   end end. 


In order to use this method, the controller must have methods with three parameters
 -module(protected_controller, [Req]). -compile(export_all). before_("public") -> ok; %%  /protected/public     % before_(_) -> auth_lib:require_authentication(Req). %%     % index(Req, [], Principal) -> {ok, [{principal, Principal}]}. public(Req, []) ok. 


If authentication is successful, then the current user will be passed to the third parameter. Otherwise, it will be redirected to / login.
If the user has been authenticated, but the record has disappeared from the database, it will be redirected to / error, and an error message will be added.

Now you need to write a controller to check.
 -module(login_controller, [Req]). -compile(export_all). index('GET', []) -> ok; index('POST', []) -> Email = Req:post_param("email"), PasswordHash = erlang:md5(Req:post_param("password")), case boss_db:find(user, [ email = Email, password_hash = PasswordHash], 1) of {user, Principal} -> boss_session:set_session_value(Req, principal, Principal.id), {redirect, "/protected"}; {error, Reason} -> boss_flash:add(Req, error, "Invalid User"), ok end. 

Note, the 'equals' character in the expression is not (ASCII =), but (UTF-8 =).

Conclusion


You can still write a lot of things, but slightly tired :)
In principle, everything is written fairly quickly and fun. This is my first working Erlang code. For now, I like everything.

Note: in the article code, it is clear that mistakes, unfinished guards and other trifles. Please do not kick much, I write on the Erlang the second day.
Write in a personal, correct article.

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


All Articles