📜 ⬆️ ⬇️

Inventing a bike on Scala - your ORM Framework, WebServer (RESTful and MVC)

This article is a logical continuation of my previous post. Inventing a bicycle in Java - we write our own Framework (DI, ORM, MVC and etc) . Several months have passed since my first Java framework was published. I was lucky and I applied my development in a commercial project. In practice, it turned out that my many assumptions, as it would be convenient to use, turned out to be wrong. But I was not filonil and rewrote and supplemented the library. If you compare the API in my first article with what is now in the library, you will see progress.

But back to Scala. I watched how the framework and Play and Spray are arranged. I noticed such a trend that they are all sharpened on architecture in the style of Actors (actors) to ensure Highload. This is of course all correct and promising. But for some reason, the pursuit of this made the coding of projects a little bit more complicated. It turned out that if you don’t have a normal Highload project, then Play and Spray didn’t fall at all and there are no alternatives for implementing one of the advantages of Scala, writing fewer letters than in Java. Especially looking in the direction of Spring boot, Spring Data and so on. Everything is nice, short and beautiful. And in Scala, actor-style libraries are similar to the first versions of J2EE in terms of usability.

image

')
I began the study of Scala with the reading of the book by Horstman K. - Scala for the impatient (there is a Russian edition). Then there was a break of several months during which I was grinding my Java framework and recalled the Scala buns with doubt. But in the end I finally decided and started writing a library on Scala. I noticed two nuances:


First, I wrapped my Java library on Scala (working with Json). Then, where I’ve clearly stuck with architectural features in the depth of my Java implementation with Jetty, I rewrote this site from Java to Scala. And there and there I got a great experience. I personally made sure that writing Scala code is really shorter and faster (especially when you memorize the syntax of a language). And that it is possible without problems to use all the accumulated baggage of existing Java libraries and Frameworkes in your Scala project. I am silent about the magic of Scala, which allows you to make DSL (Domain-specific language). Recall the cause of the OOP (Object-Oriented Programming), this desire to make the code readable and understandable at the level of human-readable expressions. Scala allows you to do even a higher level. For example, you can describe your control structures similar to if, for, switch, and so on (applied this in ORM for transactions).

As a result, the code on the github framework github.com/evgenyigumnov/scala-common

An example of a web service using this framework on a github github.com/evgenyigumnov/example-scala

Example structure:
./: build.sbt ./javascript: user.js ./pages: index.html layout.html login.html ./sql: 1.sql ./locale: messages_en.properties ./src/main/scala/com/igumnov/scala-2.11/example: ExampleUser.scala SiteServer.scala 


build.sbt
 name := "example-scala" version := "1.0" scalaVersion := "2.11.7" libraryDependencies += "com.igumnov.scala" % "scala-common_2.11" % "0.5" //    libraryDependencies += "com.h2database" % "h2" % "1.4.187" //   //  Bootstrap, AnglularJS    webjars  libraryDependencies += "org.webjars" % "angular-ui-bootstrap" % "0.12.0" libraryDependencies += "org.webjars" % "angularjs" % "1.3.8" libraryDependencies += "org.webjars" % "bootstrap" % "3.3.1" 


SiteServer.scala
 package com.igumnov.scala.example import java.util.Calendar import com.igumnov.scala._ import com.igumnov.scala.webserver.User object SiteServer { def main(args: Array[String]) { //      ( 3 ) ORM.connectionPool("org.h2.Driver", "jdbc:h2:mem:test", "SA", "", 1, 3) //             ORM.applyDDL("sql") //      WebServer.setPoolSize(5,10) //    - WebServer.init("localhost", 8989) //       WebServer.loginService((name) => { val user = ORM.findOne[ExampleUser](name) if (user.isDefined) { Option(new User(user.get.userName, user.get.userPassword, Array[String]("user_role"))) } else { Option(null) } }) //           URL- WebServer.securityPages("/login", "/login?error=1", "/logout") //        user_role WebServer.addRestrictRule("/*", Array("user_role")) //        WebServer.addAllowRule("/static/*") //        classpath  webjars WebServer.addClassPathHandler("/static", "META-INF/resources/webjars") //       Java Script- WebServer.addAllowRule("/js/*") //         Java Script WebServer.addStaticContentHandler("/js", "javascript") //        (      ) //           -  WebServer.locale(Map("en" -> "locale/messages_en.properties"), (rq,rs)=>{ "en" }) //          WebServer.templates("pages",0) //     "/",        ,    index.html WebServer.addController("/", (rq, rs,model) => { model += "time" -> Calendar.getInstance.getTime "index" }) //     "/login",  ,    login.html WebServer.addController("/login", (rq, rs,model) => { "login" }) //  REST-   "/rest/user"      POST/PUT  JSON-  ExampleUser WebServer.addRestController[ExampleUser]("/rest/user", (rq, rs, obj) => { rq.getMethod match { case "GET" => { //  GET  ORM.findAll[ExampleUser]() //    } case "POST" => { //  POST  val user = obj.get user.userPassword = WebServer.crypthPassword(user.userName, user.userPassword) ORM.insert(obj.get) //     } case "DELETE" => { //  DELETE  val user = ORM.findOne[ExampleUser](rq.getParameter("userName")) //   demo    if(user.get.userName == "demo") throw new Exception("You cant delete demo user") ORM.delete(user.get) user.get } } }) //            JSON      Error WebServer.addRestErrorHandler((rq, rs, e) => { object Error{ var message:String =_ } Error.message = e.getMessage Error }) val users = ORM.findAll[ExampleUser] //      if(users.size==0) { //      val user = new ExampleUser user.userName="demo" user.userPassword=WebServer.crypthPassword(user.userName, "demo") ORM.insert(user) //  demo/demo    } //             Exception,   - :) WebServer.start } } 


ExampleUser.scala
 //     JSON          package com.igumnov.scala.example import com.igumnov.scala.orm.Id class ExampleUser { @Id(autoIncremental = false) var userName: String = _ var userPassword: String = _ } 


1.sql
 #         ORM   ExampleUser.class CREATE TABLE ExampleUser (userName VARCHAR(255) PRIMARY KEY, userPassword VARCHAR(255)) 


login.html
 <!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd"> <!--      layout  layout.html --> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorator="layout"> <body> <!--         layout.html --> <div layout:fragment="content"> <form name="form" action="/j_security_check" method="POST"> <div class="modal-header"> <h3 class="modal-title" th:text="#{login.title}"></h3> <!--       --> </div> <div class="modal-body"> <div class="form-group"> <input type="text" name="j_username" class="form-control" value="" placeholder="Login"/> </div> <div class="form-group"> <input type="password" name="j_password" class="form-control" placeholder="Password"/> </div> <div class="form-group"> <button type="submit" id="login" class="btn btn-primary">OK</button> </div> </div> </form> </div> </body> </html> 


index.html
 <!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd"> <!--      layout  layout.html --> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorator="layout"> <body> <!--         layout.html --> <div layout:fragment="content"> <!--     AngularJS--> <script src="/js/user.js"></script> <h1 th:text="${time}"></h1> //       <!--      UserCtrl --> <div ng-controller="UserCtrl"> <table class="table"> <thead> <tr> <th>Name</th> <th>Password</th> <th></th> </tr> </thead> <tbody> <!--      --> <tr ng-repeat="user in users"> <td>{{user.userName}}</td> <td>{{user.userPassword}}</td> <!--            --> <td><a href="#"><span class="glyphicon glyphicon-remove" tooltip="Delete" ng-click="deleteUser(user)"/></a></td> </tr> </tbody> </table> <div ng-model="user"> <!--    --> <div class="form-group"> <input type="text" class="form-control" ng-model="user.userName" placeholder="Login"/> </div> <div class="form-group"> <input type="password" class="form-control" ng-model="user.userPassword" placeholder="Password"/> </div> <div class="form-group"> <!--           --> <button class="btn btn-primary" ng-click="addUser(user)">Add</button> </div> </div> </div> </div> </body> </html> 


layout.html
 <!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd"> <!--      AngularJS --> <html ng-app="com.igumnov.common.example"> <head> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/3.3.1/css/bootstrap.min.css" /> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> </head> <body> <script src="/static/angularjs/1.3.8/angular.min.js"></script> <script src="/static/angularjs/1.3.8/angular-resource.min.js"></script> <script src="/static/angular-ui-bootstrap/0.12.0/ui-bootstrap-tpls.min.js"></script> <div class="container"> <!--      --> <div layout:fragment="content"></div> </div> </body> </html> 


user.js
 angular.module('com.igumnov.common.example', ['ui.bootstrap', 'ngResource']) .factory('User', ['$resource', function ($resource) { //  REST- User return $resource('/rest/user', {}, { list: { //   method: 'GET', cache: false, isArray: true //    }, add: { //   method: 'POST', cache: false, isArray: false //     }, delete: { //   method: 'DELETE', cache: false, isArray: false //     } }); }]) .controller('UserCtrl', function ($scope, User) { //    UserCtrl $scope.users = User.list({}); //       $scope.addUser = function (user) { //    User.add({},user,function (data) { //  REST- $scope.users = User.list({}); //   ,    }, function (err) { alert(err.data.message); //   ,   }); } $scope.deleteUser = function (user) { //    User.delete({"userName" : user.userName},user,function (data) { //  REST- $scope.users = User.list({}); //   ,    }, function (err) { alert(err.data.message); //   ,   }); } }); 


messages_en.properties
 login.title=Login 


In conclusion, I can say that Scala will be implemented in commercial projects written in Java. I am not going to rewrite there anything from what was done in Java, but I will definitely write new modules on Scala. The reason for this decision: Scala is faster, more convenient, cleaner code. In general, it is effective.

PS Since I am new to Scala, I am happy to listen to criticism on my code. I need feedback to understand what I'm doing wrong.

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


All Articles