VARUNA JAYASIRI

@vpj

Open sourcing nearby.lk data models library

April 29, 2016

We did a re-write of the nearby.lk data model library, and we decided to open source the core of it. It supports JSON or YAML data files, and parses them based on a specification (like a schema).

The specification is a coffeescript class with a render function. The editor checks the input against the specification and uses the render function to show the parsed input. The editor can either work in YAML mode (plain YAML input) or form input mode (user fills up a form). It can handle quite complex data models.

Here's the editor accepting (and rendering a resume).

This is the class for the Resume example.

 class Resume extends Base
  @extend()

Set the type of the model.

  type: 'Resume'

Define properties. The type of the property is determined based on the property parameters. It defaults to Value property.

  @property 'name', {}
  ...

oneof specifies that the property is one of the models specified.

  @property 'address',
   oneof: ['Null', 'Address']

This is a list of values. rows and columns are schema parameters for values.

  @property 'statement',
   list:
    rows: 3
    columns: 50

This property is a list of other models.

  @property 'timeline',
   list:
    oneof: ['Experience', 'Education', 'Recognition']
    defaultValues: -> {from: '2010', to: '2020'}

  ...

These are some private functions of the Resume model.

  _education: ->
   res = (e for e in @_values.timeline when e.type is 'Education')
   res.sort (x, y) -> y._values.from - x._values.from

  ...

This is the template to render the output.

  template: (self) ->
   values = self._values
   education = self._education()
   experience = self._experience()
   recognitions = self._recognitions()

   @div ".resume", ->
    @div ".row", ->
     @div ".six.columns", ->
      @div ".name", "#{values.name}"
      @div ".role", "#{values.role}"
      if values.website isnt ''
       @div ".website", ->
        @a href: "#{values.website}", "#{values.website}"

     if values.address.type isnt 'Null'
      @div ".three.columns.address", ->
       values.address.weya this

     ...

New properties (e.g. image uploads) can be defined similarly.

Why did we need this?

On nearby.lk, we have local business information. Initially it was basic information like contact numbers, a small description, etc. Later, more details needed to be included. Also, the page structures differed based on the type of location. We didn't want to have the content in free flow. It's easier to make mistakes and not have uniform style with free text content. Also you can do a much better search with structured content.

nearby.lk is not yet running this re-written library. A little more work needs to be done to integrate this with the nearby search engine.

How to use it?

These samples show the classes for Resume example.

The library depends on Weya and Mod libraries for rendering and module management.

Embedding the editor

Mod.require 'Models.Editor', (Editor) ->
 editor = new Editor
  model: 'Resume' #Base model name
 editor.render element,
  width: width
  height: height
  onRendered
 onRendered = ->
  editor.yaml() #yaml edit mode
  editor.structured() #structured edit mode
  editor.resize  #resize editor
   width: width
   height: height
  editor.getModel() #return model object
  #returns json object omiting default values
  editor.getModel().getJSON()
  #returns full json object
  editor.getModel().getJSONFull()
  editor.setJSON jsonObject #set json object content

Using models

Mod.require 'Models.Models', (Models) ->
 ModelClass = Models.get 'Resume'
 model = new ModelClass
 #parse json object
 results = ModelClass.parse jsonObject
 #results.score = How maching it was [0..1]
 #results.errors = List of errors when parsing
 #results.value = model
 #returns json object omiting default values
 model.getJSON()
 #returns full json object
 model.getJSONFull()
 model.render element
 model.html() #returns html
We did a re-write of the <<http://nearby.lk(nearby.lk)>> data model library, and we decided to open source the core of it. It supports JSON or YAML data files, and parses them based on a specification (like a schema). >>> <<< <iframe src="https://ghbtns.com/github-btn.html?user=vpj&repo=models&type=star&count=true&size=large" frameborder="0" scrolling="0" width="160px" height="30px" </iframe> The specification is a coffeescript class with a render function. The editor checks the input against the specification and uses the render function to show the parsed input. The editor can either work in YAML mode (plain YAML input) or form input mode (user fills up a form). It can handle quite complex data models. Here's the editor accepting (and rendering a resume). <!> <<< <iframe src="http://vpj.github.io/models/" width="100%" height="500px" style="outline:none;border:1px solid #eee"></iframe> This is the class for the **Resume** example. ```coffee class Resume extends Base @extend() Set the type of the model. ```coffee type: 'Resume' Define properties. The type of the property is determined based on the property parameters. It defaults to --Value-- property. ```coffee @property 'name', {} ... ``oneof`` specifies that the property is one of the models specified. ```coffee @property 'address', oneof: ['Null', 'Address'] This is a list of values. ``rows`` and ``columns`` are schema parameters for values. ```coffee @property 'statement', list: rows: 3 columns: 50 This property is a list of other models. ```coffee @property 'timeline', list: oneof: ['Experience', 'Education', 'Recognition'] defaultValues: -> {from: '2010', to: '2020'} ... These are some private functions of the **Resume** model. ```coffee _education: -> res = (e for e in @_values.timeline when e.type is 'Education') res.sort (x, y) -> y._values.from - x._values.from ... This is the template to render the output. ```coffee template: (self) -> values = self._values education = self._education() experience = self._experience() recognitions = self._recognitions() @div ".resume", -> @div ".row", -> @div ".six.columns", -> @div ".name", "#{values.name}" @div ".role", "#{values.role}" if values.website isnt '' @div ".website", -> @a href: "#{values.website}", "#{values.website}" if values.address.type isnt 'Null' @div ".three.columns.address", -> values.address.weya this ... New properties (e.g. image uploads) can be defined similarly. ##Why did we need this? On <<http://www.nearby.lk(nearby.lk)>>, we have local business information. Initially it was basic information like contact numbers, a small description, etc. Later, more details needed to be included. Also, the page structures <<http://www.laundromat.lk/(differed)>> based on the <<http://www.nearby.lk/templeoftooth(type of location)>>. We didn't want to have the content in free flow. It's easier to make mistakes and not have uniform style with free text content. Also you can do a much better search with structured content. <<http://www.nearby.lk(nearby.lk)>> is not yet running this re-written library. A little more work needs to be done to integrate this with the nearby search engine. ##How to use it? <<https://github.com/vpj/models/tree/master/js/samples(These samples)>> show the classes for Resume example. The library depends on <<https://github.com/vpj/weya(Weya)>> and <<https://github.com/vpj/mod(Mod)>> libraries for rendering and module management. ###Embedding the editor ```coffee Mod.require 'Models.Editor', (Editor) -> editor = new Editor model: 'Resume' #Base model name editor.render element, width: width height: height onRendered onRendered = -> editor.yaml() #yaml edit mode editor.structured() #structured edit mode editor.resize #resize editor width: width height: height editor.getModel() #return model object #returns json object omiting default values editor.getModel().getJSON() #returns full json object editor.getModel().getJSONFull() editor.setJSON jsonObject #set json object content ###Using models ```coffee Mod.require 'Models.Models', (Models) -> ModelClass = Models.get 'Resume' model = new ModelClass #parse json object results = ModelClass.parse jsonObject #results.score = How maching it was [0..1] #results.errors = List of errors when parsing #results.value = model #returns json object omiting default values model.getJSON() #returns full json object model.getJSONFull() model.render element model.html() #returns html