On the one hand, the subject was really square. On the other hand, it was round. But on the third side, with which there must be a triangle, the subject went out crooked and oblique.
- Alexis goes to the meeting? - Lenochkin stuck in the door interested physiognomy.
- Alexis to the conference does not go. Aleshenka writes an article.
- About the cubes?
- What other dice? - I lowered my eyes, in my hands and the truth was the ill-fated cube. That is a ball. That is a diamond.
- Not about the cubes! And not about balls. About templates.
- I'll tell them so! Pattern, ah. - Helen already ran further along the corridor.
"About templates. Even about three different templates." More precisely, for three reasons to use patterns in server code. And none of these reasons will be about HTML.
In the examples, I used the Mustache syntax, due to the laconic syntax and the presence of implementations for everything that moves. Mustache practically does not allow itself liberties, unlike, for example, .Net Razor, which allows encoding inside a template, thus setting a bad example with a weak spirit to developers.
Code samples will be in python. The implementation of Mustache for Python is called pystache .
So, three reasons to let patterns in own life your code.
If you have a system within which there is some data — for example, data in a relational database or data obtained via API calls, sometimes you need to create artifacts based on this data.
An artifact can be, for example, JSON or plain text file, attachment, HTTP response. The main thing is that an artifact is essentially the result of applying a function from some relatively compact part of the data of your system. And the artifact has its own syntax.
Artifact can be a bank statement in text format for loading into the legacy system. An artifact can be the unloading of an electronic check in the form of a .json file, which will be sent as an attachment to the client by mail.
In all these cases, you will greatly simplify your life by using patterns to create artifacts.
What is a template engine? This is a library that will take an object model (context), take a pattern, apply one to the other - and produce a result. The object model and pattern is prepared by the programmer. The final result is prepared template.
Example: try to create a text message about the order.
We first prepare the object model:
def add_comma(list): for a in list[:-1]: a["comma"] = True def get_model(): res = { "documentId": 3323223, "checkDate": "01.02.2019 22:20", "posId": 34399, "posAddr": "Urupinsk, 1 Maya 1", "lines": [ { "no": 1, "itemtext": "Hello Kitty", "amount": 3, "sumRub": "55.20" }, { "no": 2, "itemtext": "Paper pokemons", "amount": 1, "sumRub": "1230.00" }, { "no": 2, "itemtext": "Book of Mustache", "amount": 1, "sumRub": "1000.00" } ], "total": { "amount": "3285.20" } } add_comma(res["lines"]) res["posInUrupinsk"] = res["posId"] > 34000 return res
The code is strictly blind. In a real code, there can be a database query, some kind of logic for calculating values (for example, we calculate the total.amount value based on the order items).
Pay attention to a few things:
The text of the mustache template looks like this:
{{#posInUrupinsk}} ! 100 . {{/posInUrupinsk}} {{^posInUrupinsk}} : {{/posInUrupinsk}} {{#lines}} #{{no}} ... {{itemtext}}: {{sumRub}} {{#comma}};{{/comma}}{{^comma}}.{{/comma}} {{/lines}} : {{total.amount}} --------------------------- N : {{documentId}} {{checkDate}}
We see in the template that the document header for orders in Uryupinsk is different from other cities. We also see that there is a period at the end of the last line with a commodity position, and a semicolon in all early positions. The attribute "comma" and the method "add_comma" in the model generator are responsible for this.
Code that applies context to a template is trivial:
model = get_model() with open(os.path.join("templates", "check.txt.mustache"), 'r') as f: template = f.read() check_text = pystache.render(template, model) print(check_text)
Result:
! 100 . #1 ... Hello Kitty: 55.20 ; #2 ... Paper pokemons: 1230.00 ; #2 ... Book of Mustache: 1000.00 . : 3285.20 --------------------------- N : 3323223 01.02.2019 22:20
Another tip: if the task allows, save the model itself with the rendered template (for example, in JSON format). This will help with debugging and troubleshooting.
The printer squeaked three times, giving out a new model. The triangular side was now a perfect triangle. The other two sides were square. The neural network lived its life and refused to give out a primitive model for all the concepts of 3D.
"I'll give Lenochka a cube." - I thought. Let him rejoice.
You may need to create JavaScript in runtime from inside the backend. What for? To create reports on the browser side, for example. Or get a script on F # from within a program on Go. Or Kotlin-ovsky code from within ReactJS (I can not imagine why this may be needed, but suddenly you have such specific inclinations).
In the case of code generation, it is better to first write the resulting code with your hands (what we want to generate) and only then break it into a template and a model. This approach will save us from melancholy excessive complexity of the model. It is never too late to complicate the model, but it is better to start with a simple one.
var report = CreateChart({ title: " - " }, type: "lineChart", sourceUrl: "/reports/data/0" ); report.addLine({ dataIndex:0, title: "", color: "green" }); report.addLine({ dataIndex:1, title: "", color: "red" }); report.render($("#reportPlaceholder1")); var report = CreateChart({ title: " - " }, type: "lineChart", sourceUrl: "/reports/data/1"); report.addLine({ dataIndex:0, title: "Hello Kitty", color: "#000" }); report.addLine({ dataIndex:1, title: "PokemonGo", color: "#222" }); report.addLine({ dataIndex:2, title: "Mustache", color: "#333" }); report.render($("#reportPlaceholder2"));
Here we see that we have from one to N graphs of lineChart on our page, each of which has its own data source, title and list of indicators. Modelka:
def get_model(): return { "charts": [ { "divId": "#reportPlaceholder1", "title": " - ", "sourceUrl": "/reports/data/0", "series": [ {"dataIndex": 0, "title": "", "color": "green"}, {"dataIndex": 1, "title": "", "color": "red"}, ] }, { "divId": "#reportPlaceholder2", "title": " - ", "sourceUrl": "/reports/data/1", "series": [ {"dataIndex": 0, "title": "Hello Kitty", "color": "#000"}, {"dataIndex": 1, "title": "PokemonGo", "color": "#111"}, {"dataIndex": 2, "title": "Mustache", "color": "#222"}, ] } ] }
Well, the template:
{{#charts}} var report = CreateChart({ title: "{{title}}" }, type: "lineChart", sourceUrl: "{{sourceUrl}}" ); {{#series}} report.addLine({ dataIndex:{{dataIndex}}, title: "{{title}}", color: "{{color}}" }); {{/series}} report.render($("{{divId}}")); {{/charts}}
Pay attention: such a “head on” approach to template making requires a separate effort to screen values in the model. if in series [0] .title we will sneak a comma or a quotation mark - "Hello Kitty \" "- the syntax of the resulting file will collapse with a bang. Therefore, write the screening functions and use them when creating models. Use formatters if the template engine is able to.
The third cube flew through the door and bounced off with a bang. Also worthless. Interesting. Can I roll a dice so that it slips under the door and reaches the end of the corridor? Is it possible to print rubber? Or is it better to make it filled with small such icosahedra filled with air? ...
A picky reader will say that this is also code generation, the translation of concepts from one programming language to another. To which we will reply to the picky reader that working with SQL or with any other query language to the database is a slightly separate area of programming and it is not obvious to everyone that if you can generate js scripts with templates, then SQL is also possible. Therefore, we will place requests in a separate case.
And so that the picky reader will not be bored, we will leave in the article only examples of several queries. You can figure out for yourself which model and which template is better suited here. In the examples on the githaba you can see what I did.
SELECT * FROM hosts WHERE firmware_id=1 AND bmc_login='admin' ORDER BY ip DESC; SELECT * FROM hosts ORDER BY name LIMIT 3; SELECT host_type_id, COUNT(*) FROM hosts GROUP BY host_type_id;
Templates (including for SQL) and code samples can be found on the github .
Source: https://habr.com/ru/post/454418/
All Articles