Haskell Code Generator

    Some time ago I decided to experiment with microservices on Haskell. The architecture of the project involves the creation of a large number of micro-projects, each of which is responsible for one small amount of tasks. After some time, I was tired of creating these projects manually, as well as writing template code for each entity from the database. To solve this problem, I developed a small utility that allows you to generate boilerplate code. Under the cut - more.


    imageIn principle, code generators around are a dime a dozen, but they are all specific and, as a rule, are intended for one specific language or even a project in that language. Of course, after seeing how easy it is to generate code for entities, say, in Ruby on Rails, you begin to really envy the rubists. Among the Haskell packages, I found only two more or less worthwhile code generators: Hi and Haskeleton .

    Hi can generate code for the whole project, which is very cool. But this small spoon of honey is generously diluted with a barrel of tar. In order to generate the project code, you need to pass a Hi link to the URL of the project template, which still needs to be found somewhere. Each time, Hi will download templates from this link, instead of caching the downloaded. Hi supports just a few substitution variables: package-name, moduleName, author, email, and this is a complete file.

    Haskeleton is a sophisticated project template for Hi, very well thought out . It even includes the Haskeleton.hs utility, which allows you to create empty modules and insert imports of these modules in all suitable places. However, empty modules are not what I need.

    The requirements that I made for the code generator were as follows:

    • Global and local repositories. I would like to download templates once or occasionally, but certainly not every time you generate something. I would also like to be able to create my own templates in the local repository without the need to upload them somewhere outside.
    • Ability to generate template code of entities. For example, for the Article table, generate all the code that can extract / save data to this table, present it in JSON format, etc. - In short, generate a model code.
    • The ability to generate project code in its entirety, as Hi does. In the process of generating project code, be able to use arbitrary templates, as Hi does not. Those. use not only predefined variables, but any, including nested objects, and lists.

    Based on these requirements and the availability of ready-made libraries in Haskell, I chose the hastache package for template code based on Mustache syntax, as well as aeson so that the parameters for the template can be transferred in JSON format.

    The local template repository is located in the ~ / .trurl / repo directory, all the entity and project templates downloaded from the global repository are added there . The project template is a tar archive. An entity template is just a file. For example, suppose there is a myentity.hs file with the following boilerplate code:

    data {{entityName}} = {{entityName}}{{#props}} {{type}}{{/props}}
    insert{{entityName}} :: Pool Connection -> Maybe {{entityName}} -> ActionT Text IO ()
    insert{{entityName}} _ Nothing = return ()
    insert{{entityName}} pool (Just ({{entityName}} _{{#props}} {{name}}{{/props}})) = do
         liftIO $ execSqlT pool [{{#props}}(show {{name}}){{^last}}, {{/last}}{{/props}}] "INSERT INTO {{entityName}}({{#props}}{{name}}{{^last}}, {{/last}}{{/props}}) VALUES({{#props}}?{{^last}},{{/last}}{{/props}})"
         return ()
    

    Run trurl with the following parameters:

    trurl new Article myentity '{"entityName": "Article", "props": [{"name": "title", "type": "String"}, {"name": "body "," Type ":" String "}, {" name ":" year "," type ":" Integer "," last ": true}]} '

    As a result, we get the following code:

    data Article = Article String String Integer
    insertArticle :: Pool Connection -> Maybe Article -> ActionT Text IO ()
    insertArticle _ Nothing = return ()
    insertArticle pool (Just (Article _ title body year)) = do
         liftIO $ execSqlT pool [(show title), (show body), (show year)] "INSERT INTO Article(title, body, year) VALUES(?,?,?)"
         return ()
    

    Similarly, you can generate template projects using the create command. True, you must adhere to the following rules:

    • All files that need to be renamed according to the project name must have the name "ProjectName".
    • All mustache template files in a template project must have the extension ".template".
    • You can pass any JSON parameters to mustache templates, as well as to templates of individual files created with the new command. At the same time, at least one parameter will always be available, even if you do not pass JSON at all. This is the string ProjectName containing the name of the project.

    In order to download all available templates from the global repository, you need to run the trurl update command.

    Using the trurl list command, you can get a list of all available templates.

    The trurl help [template] command will show a hint on how to use the specified template.

    The trurl new [newfilename] [file_template] [json_params] command is intended to generate exactly one file.

    The trurl create [newprojname] [project_template] command"Is intended to generate an entire project, JSON parameters are optional here.

    There are currently only a few templates available for trurl in the global repository. These are the templates for my micro services (scotty-mysql and scotty-entity), and haskeleton, which I added at the request of one of the trurl users. You can create your templates, just send me a pull-request at github.com/dbushenko/trurl/tree/master/devrepo and I will add them to the main repository.

    The trurl project is young and therefore not without flaws. It is not always convenient to pass long JSON strings for generating entities, not every template can be generated using mustache, the main template repository is not very conveniently arranged. All these are areas for improvements that I am actively working on and I will be glad if others join me.

    And the last one. I chose the name trurl in honor of the space designer Trull from the works of Stanislav Lem “The Seven Travels of Trull and Clapautius,” you saw the portrait of Trull at the beginning of the article.

    Also popular now: