Pages

Saturday, May 21, 2016

Ryan Collins has some great videos on how to use Perfect to serve web pages and how to use Mustache. These were great for helping me get things going, but I prefer a written format to video, so I am going to repeat a bit of what Ryan has done. Chris Manahan has a good writes up on getting a Perfect project running on how to use MySQL with Perfect so I won't repeat that.

So here we go:

My previous blog post shows how I created classes to serve my MySQL tables as JSON. Here is myPerfectServerModuleInit Where I add one route to return JSON from my auto_make table.

publicfunc PerfectServerModuleInit() {

Routing.Handler.registerGlobally()

// Create Routes

Routing.Routes["/auto_make"] = { _inreturnAutoMakeRecord() }

}

I'm creating a web service for my app, but I also want to have a web interface that allows me to administer content for the app. So I need to serve some static pages like my stylesheet to give the look and feel of my service and then of course some dynamic pages.

To get static pages I add a route and connect it to the StaticFileHandler that is part of the PerfectLib.

publicfunc PerfectServerModuleInit() {

Routing.Handler.registerGlobally()

// Create Routes

Routing.Routes["/*"] = { _inreturnStaticFileHandler() }

Routing.Routes["/auto_make"] = { _inreturnAutoMakeRecord() }

}

Now I need to have a file in the webroot. So in my Xcode project I create a group called webroot and add my .css file and background image:

This is great, but to get the server to find these files we need to have them copied into the webroot in the Products Directory. So I select my CarHealthServer project and the Build Phase. Then click on the "+" and add a "New Copy Files Phase"

Then I will click on the "+" in the copy files phase and add the files that are in my webroot group. Here I have added a couple of extra files, a vehicles.html and a shop messages.mustache. I'll talk about the .mustache file later.

So now when we run our server and go to http://localhost:8181/BackView.css in my browser I get the contents of my .css file.

I want a page that will let me create an html document that will get saved in a MySQL table. So I create a file call shopmessage.mustache:

(Sorry blogger is having problems with this, and I am feeling lazy so you get a picture or this file)

The important things here are "{{% handler:ShopMessageHandler}}" which specifies the class that will handle the dynamic content for the mustache file, and "{{doc}}" which is supplied by the handler. Here is my ShopMessageHandler.swift class:

In the valuesForRespose I create a dictionary of the values to be used in the .mustache file. In this case there is just one value and it is hard coded to a static string. But this now gives me the framework to develop my service.

Now when I go to http://localhost:8181/shopmessage.mustache I get this:

Friday, May 20, 2016

I decided to build the web service for my iOS and Android app using Perfect which is a Swift server implementation. I am using MySQL to host a database and wanted to a nice way to get the database tables written out as JSON. I had some problems but here is what I came up with:

My first attempt was to create a base class that I could subclass for each table type and do the read/write database operations and the JSON operations in the base class. But I ran into a lot of problems getting the base class to access the overridden properties and methods of the subclasses. Then I discovered this. Fist I create a simple protocol that all the record classes will use:

protocol DatabaseRecord : RequestHandler {

var sqlTableName: String {get}

var loaded: Bool {getset}

func loadRow(row: [String])

func getDictionary() -> [String : String]

func load(forName:String)

func load(forID:Int)

func getAll() -> [AnyObject]

init()

}

Then I create an extension to the protocol so all of this will get added to all of the classes that implement the DatabaseRecord protocol.

extensionDatabaseRecord {

The methods here to load from the database don't seem like a big deal, but this just didn't work as a super class because despite all my google searching and efforts I couldn't get the base class to call the subclass methods or get the sqlTableName from the sub classes. But as an extension this jus works without any strange code.

func loadFromDatabase(query:String) {

var mysql = MySQLConnection.mysql;

MySQLConnection.connectToMySQL()

defer {

mysql.close()

}

if mysql.query(query) {

iflet results = mysql.storeResults() {

if results.numRows() >= 1 {

iflet row = results.next() {

loadRow(row)

return

}

}

}

}

}

func load(forName:String) {

self.loadFromDatabase("SELECT * FROM \(sqlTableName) WHERE name = \"\(forName)\"")

This next method was where I had the most problems. I tried using Self, dynamicType, and everything else I could think of to get the sqlTableName from the subclasses and got it going but the creating objects of my subclasses and adding them to an array that I would return seemed impossible. Now as an extension the sqlTableName just works, and I can create a new object of the correct class using self.dynamicType.init() and add it to the array.

func getAll() -> [AnyObject] {

var records = [AnyObject]()

var mysql = MySQLConnection.mysql;

MySQLConnection.connectToMySQL()

defer {

mysql.close()

}

if mysql.query("SELECT * FROM \(sqlTableName)") {

iflet results = mysql.storeResults() {

results.forEachRow { row in

let obj = self.dynamicType.init()

obj.loadRow(row)

iflet o = obj as? AnyObject {

records.append(o)

}

}

}

}

return records

}

PerfectLib has a JSON encoder but when I tried to use it, it would crash. It was late and the JSON that I need to generate was simple so I just quickly wrote this to create the JSON for my table:

func encodeAsJSON(dicts:Array<[String:String]>) -> String {

var str: String = "["

for dict in dicts {

var first = true

for (key, value) in dict {

if !first {

str.appendContentsOf(",")

} else {

first = false

}

str.appendContentsOf("{\"\(key)\":\"\(value)\"}")

}

}

str.appendContentsOf("]")

return str

}

Now I can add a request handler to the extension and now all my database record classes have a easy way to take a http get request and turn it into JSON array.

func handleRequest(request: WebRequest, response: WebResponse) {

let records = getAll()

var dicts = Array<[String:String]>()

for rec in records {

iflet r = rec as? DatabaseRecord {

let dict = r.getDictionary()

dicts.append(dict)

}

}

response.appendBodyString(encodeAsJSON(dicts))

response.requestCompletedCallback()

}

}

So then I have a simple Swift class representing a SQL database record. For example:

And it is easy to create additional classes for database tables and add them to the routes. Of course I will want to create http handlers that will allow to more than just dump out tables to JSON, but this now gives me the base I need to create the rest of my web service.