ORM’s Dreaming Big: Pt 1 (Schema)

So I’ve came across waterline. Waterline is unique in that it isn’t just an interface to sql or mongodb but anything.* Anything*. (*Anything that someone has made an adapter for it). Unfortunately my experiences with it have led me to believe it is not a good fit for a full stack outside of sails. I’ve written more about my issues with the framework here. Now, I’m aksing for a lot therre so I don’t expect it to go anywhere. But I made me have a three day coding sprint of trying to develop my own ORM the way I wanted it. As I continued, I realized it is a ton of work alone and not all of the things I desire are available to mooch off of. But the Idea… The Dream… That can live on…

What would it be used for?

This is one of the reasons why I wrote this post. Waterline is awesome. However, It left me wanting more and questioning if supporting too much is giving me less. As a result, the interfaces I believe are of upmost importance to provide compatibility with is.

  • Memory – This was a proof of concept by them, however it can be implemented in a fast manner. Libraries like Lazy.js will compile all the arguments so it s only ran in a single loop. Additionally, supporting proper indexes can add even more speed to it. However, the point of being able to use in memory is so that you can create “collections” easily, beautifull and query them as you would anything else
  • LocalStorage – This is another clientside feature that can be implemented. LocalStorage is an interesting beast but nonetheless quite manageable. WHat you would do is store each everything to start with connection/model where connection and model would be the connection and model name. From there you would store indexes under connection/model/indexName and probably each object in its own place such as connnection/model/ObjectID. This will allow you to not load too much at once and be able to asyncrnously retrieve objects as you need them instead of loading everything into memory and hoping all goes well.
  • HTTP- By providing a wrapper to create HTTP calls, you can interface with your database easily as if it was again on the server. Of course a serverside implementation is also necessary, however I think that’s relatively simple in the long run. Perhaps that begs the question of creating “Users” that can interface with your ORM.
  • FileSystem – Mongodb is the standard, without a doubt. However, I’m a strong believer in diversity (when it’s convenient to say I am). As a result, creating a filesystem document based framework doesn’t see too far off or out of line. It would most likely be quite similar to localStorage actually
  • MongoDB – Mongo in many ways provided the breakthrough db. Might as well still be able to interface with it

Sugery Snacks

The Validator

The validator Is without a dote one of the most important features to any database. The purposes of a validator are not just for the databases purposes but also for the clientside. When creating a form, being able to just hook in and apply a validator is without a doubt one of the sweetest things possible. Unfortunately, those validation parameters generally are not included in the database as well. This is partly a good thing since you don’t users to see all of your internals, however for some code rewriting your models is kind of a pain in the ass. Just tedious work. As a result, here is the first commandment

  • A Validator can also be easilly hooked into any form
  • A Validator can also easily be used to generate forms

The second part is obviously much more complicated as you can see here and here.  But we’re dreaming big here right? No holds bar. Whatever we damn well please. And I would be pleased to not have to rewrite code for every single form I ever come up with.

As for Creating it, Here is a Laundry list of features….

“Native” Types

The Native Types I would prefer to keep as simple as possible

  • Number
  • Buffer
  • String
  • JSON
  • Any
  • Typed ObjectID – Can specify a specific Model(s) allowed.
  • Typed Array – Can Specify the Type of Array it will be (Any is also allowed)

The reason long, date and others are not supported is because those will end up being compiled to these native types anyway. The Object Id is the only thing that’s really different. Numbers, HashMaps and ObjectIds are the only thing that cannot be evaluated to an array.

to use these

prop1:Number, //You can provide the object class
prop2:"buffer" //You can provide a string specifying the type
prop3: ["string"] // You can create a typed array
prop4: {
  native:Object //you can specify the type explicitly
}
prop5: Framework.Types.ObjectID //This specifies any other document
prop6: "objectid:modelname" //This specifies that you expect it to use another model
prop7: AModelClass, //This specifies that you expect to use that other model. This will be the same as above
prop8: {
  native: "objectid"
  model: "modelname" //Specify the type explicitly
}
prop8: null, //Specifies Anything
prop9: FrameWork.Types.Anything //Specifies AnythingAswell
Additional Types

You may also use custom schematypes. However, schematypes will not have all the features that a validator expects. In addition, you may also provide anything that you would write in a custom schematype within the schema as well.

To use a custom schematype

prop1: {
  native: CustomSchemaTypeClass
}
prop2: {
  native: "customschematypeclass"
}

If you provide a string, your validator will create a dependency on that schematype. This means if that schematype is not available in your framework, until it is your model cannot be used. Below cannot be used with Custom SchemaTypes and only available to the Schema. Additionally, you can provide custom options that will override the SchemaTypes original Options

prop1: {
  native: "customschematypeclass",
  a_custom_option: "a value"
}
prop2: {
  native: CustomSchemaTypeClass({
     a_custom_option: "a value"
  })
}
Basic Validators
  • Required – Cannot be null or undefined
  • Final – After first created, cannot be set again
  • Unique – this will also create an index.

These are simple and straight forward

Default

At times you may want to provide a default. And now you can in three different ways

property:{
  native: [String],
  default:["value"],
}
property2:{
  native: [Number],
  default:function(){
    return Math.random();
  }
}
property:{
  native:[ObjectID]
  default:function(next){
    Query().find({something:value}).exec(next);
  }
}

 Validators Available in Custom SchemaTypes

The following are available to use within your Schema as well as SchemaTypes. Schematypes are interesting in that they can be extended indefinitely however only return the Schematype. THis is done because of the following

function CustomSchemaType(options){
  if(!(this instanceof CustomSchemaType)) return new CustomSchemaType(options);
  this.options = options
}

CustomSchemaType.prototype = function(options){
  options = _.merge(this.options,options);
  return new this.constructor(options); 
}

Simply Put, you can extend and extend and extend away

Custom Validators

Custom validators come in two flavors: Syncronouse and Asyncronous. This will be a theme as we continue

syncProprty:{
  native:Number,
  validator: function(value){
    return false;
  }
},
asyncProperty:{
  native:Number,
  validator: function(value,next){
    next(false);
  }
}
Value to Array Validation

The idea here is that you may want to compare an value to the array of values. Something like

//BAD!
arrayCompareBad:{
  native:Number,
  validator: function(value){
    return [1,2,3,4].indexOf(value);
  }
},
//Good.
arrayCompareGood:{
  native:Number,
  in:[1,2,3,4]
}

But that is slow as you create an array every time. Instead the idea is you’d be able to define it before hand

  • In – Ensures that the value is in the values
  • Not In – Ensures the value is not in the values

Now you can provide this value up front or provide it through a Syncronous or Asyncronous Function. It’s important to note that anything can use this syntax.  Enum’s have bothered me for quite some time. Any value can be compared to other values to enusre they are restricted by a certian subset. Enums used to only apply to strings however they can be applied to numbers, Buffers and yes even Arrays. Hashmaps and ObjectIds are a different beast however. Hashmaps are keys so there is no point in attempting to give it an enum. ObjectID’s require that certian ObjectId’s already exist. Now, this can be done however At that point you would need to specify a query and do it asyncronously.

Array to Array Validation
  • Any – True If At least One of Validator Array Values are present
  • All – False Unless all of the Validator Array Values are present.
  • More – True if there is more than just the Validator Array Values present
  • Not Any – False if at least Obe of the Validator Array Values are present
  • Not All – True Unless all of the Validator Array Values are present
  • Not More – False if there is more than only the Validator Array Values are present. Will also return true if empty.

You can use these like so

property:{
  native: [String],
  any:["any","of","these"],
  not_more:["any", "of", "these", "and", "no","more"]
}
Population and Save Overrides

At times you will want to Populate your data from a source other than the database. Additionally, you will want to override how that property is stored after its been validated. An example would be “User.notifications”. To duplicate the data would be absurd and if you are storing every ObjectId, you may run into numbers in the thousands. However, you can have that particular part populated on the fly.

image: {
  native: string,
  native_populate: Buffer,
  populate: function(storedvalue,next){
    fs.readFile(storedvalue,next);
  }
}

It should be noted that these aspects should probably also be available as a stream. Something like this would also work

function MyReadableStreamClass(storedvalue){
  ReadableStream.call(this);
  this.storedvalue = storedvalue;
}
image: {
  native: String,
  native_populate: Buffer
  populate: MyReadableStreamClass
}

This will create the readable stream on the fly. It should be noted that different things will populate in different ways. As a result, while this may send raw data, another might send JSON.

Now, you may be populating data, however what happens when someone wants to save something.

image: {
  native: Buffer
  depopulate: function(args,next){
    var ext = mime.findOutMimeExtension(args.name)
    var name = this.instance.id+"/image."+ext;
    fs.writeFile(name, args.buffer, function(e){
      if(e) return next(e);
      next(void(0), name); //name will be stored with the doc
    });
  }
}

//or

image: {
  type: Buffer,
  depopulate: MyWritableStreamClass
}
Digestors (Constructor Overloading) and Verbosity

Your developers may  want to be using Moment’s as dates. However, expect to be able to send a normal Javascript date as something to be stored. This is where digestors and Verbosity comes in

date: {
  native: Number,
  digestor: [function(date){
    if(date instanceof Date) 
      return date.getTime();
  }, function(time){
    if(typeof time == "number") 
      return time;
  }, function(moment){
    if(moment instanceof Moment) 
      return moment.valueOf();
  }]
}

Here we can see that any of the above values will be considered a valid number. As a result, you don’t have to worry about what you set the Date to be. If you want to always get the date as a moment

date:{
  native:Number,
  verbose:function(value){
    return moment(value);
  }
}

This will allow you to easily do whatever you want with the number without changing your database.

Schema Methods

Virtual Properties

Now, we’ve seen actual properties, but there are some properties that will not be stored but are derived from the instance itself

var schema = new Schema(validations);
schema.virtual("virtual_property", function(){
  // getter
  return this.stringA +"-"+ this.stringB;
},function(value){
  value = value.split("-");
  this.stringA = value[0];
  this.stringB = value[1];
});

Schema Indexes

The last and probably the most impotant is the indexes. With indexes. Now, Indexes are not and should never be available to a custom schematype. Additionally, because indexes can be so flexible, It brings up some interesting decisions. It’s important to note, not only can you index normal properties however you can also index virtual properties as well. Indexes available are

var schema = new Schema(validations);

schema.index("propertyname");
schema.index("uniqueproperty", "unique");
schema.index("functionalproperty", function(a,b){
  return b - a;
});
schema.index("callbackproperty", function(a,b,next){
  async.map([a,b],fs.stat,function(err,res){
    if(err) return next(err);
    next(void(0), res[0].size - res[1].size);
  });
});
Validate

Using validate is simple.

  • If the object is JSON – will validate the json
  • If the object is DOM element – will validate the dom element if its a form. If it is not, will throw an error.

This will be available from the model via Model.validate which is is basically Model.constructor.validate.bind(Model.constructor)

Finishing words

It’s important to note that the schema is simply a validator and provides database indexes. You cannot make queries with it and it essentially does nothing except provides important information for storing the data. Ideally, you will want to do as much as you can with the SchemaTypes so that you can reuse more code. And the indexes, virtual properties, what’s required and etc is Schema to Schema dependent. If you don’t like me dreaming big, well… To be honest… I believe dreaming big is part of the reason why I am here today. Because I see what I want to make and I go out and try to do it. And If I cannot, I flesh out the idea so much so that I can look back on it and say “If only”.

ClockIn: Preparing the Shortcode

Last time I basically shutdown all possibilities of using google calander, but thats fine, SQL is an efficient alternative and it should be no issue.

CREATE TABLE Clock_ins (
 time datetime DEFAULT NOW() NOT NULL,
 duration int DEFAULT 0 NOT NULL,
 user tinytext NOT NULL,
 project tinytext NOT NULL
 );

Now, theres 3 main UIs I need to focus on

  1. Clock In, Clock out-This will be in the sidebar, or really where ever someone wants to put it
  2. Project Chooser-When we begin to clock in, we need to be able to choose from our existing repositories
  3. Clock In Viewer (User and Project mode)-Whats a database if we can’t view it? Here we give sexy charts for people to look at to make them feel comfortable (We’ll worry about this later

So For Clock In, Clock out…. We’re going to want to add in a some WordPress Shortcode tags so that we can place it anywhere we want Our shortcode is going to be called [clock_in] and theres three major things we’ll be needing from the user -Their user id -their github id -The project they’re going to clockin to

function clock_in_setup( $atts, $content=null) {
 $current_user = wp_get_current_user();
 $message;
 $href;
 $type = "ajax";

 if ( !($current_user instanceof WP_User) ){
 $message = "Please Login First";
 $href = wp_login_url( get_permalink() );
 $type="self";
 }else if(($meta = get_user_meta($current_user->ID, "clockin")) == array()){
 $json = json_decode(file_get_contents(plugin_dir_path( __FILE__ )."/secret.json"));
 $cid = $json->cid;
 $redirect_uri = plugins_url("auth.php", __FILE__);
 $state = "clock-in_plugin".$current_user->ID;

 $href = "https://github.com/login/oauth/authorize";
 $href .= "?client_id=".$cid;
 $href .= "&redirect_uri=".$redirect_uri;
 $href .= "&state=".$state;

 $message = "Authorize our plugin";
 $type="blank";
 }else if(isset($atts["cur_project"])){
 $nonce = wp_create_nonce("clock_in");
 $href = admin_url('admin-ajax.php?action=clock_in&proj='.$atts["curproject"].'&nonce='.$nonce);
 $message = "Clock in!";
 }else if($meta["clocked"] == true){
 $nonce = wp_create_nonce("clock_in");
 $href = admin_url('admin-ajax.php?action=clock_out&nonce='.$nonce);
 $message = "Clock out!";
 }else{
 $href = plugins_url("clocked.php", __FILE__)."?action=in";
 $nonce = wp_create_nonce("clock_in");
ob_start();
?>
 Choose a project to Clock Into
 <div class="clockin-wrap">
 <a style="display:inline-block;height:144px;width:64px;background-color:#000;"></a>
 <div class="clockin_projects" style="display:inline-block;height:144px;width:144px;">
 </div>
 <a style="display:inline-block;height:144px;width:64px;background-color:#000;">
 </a>
 </div>
<?php
 wp_enqueue_script ('clock_in_proj');
 wp_localize_script('clock_in_proj', 'clock_in_vars', array("github_user"=>$meta["github"], "clockin_uri"=>admin_url('admin-ajax.php?action=clock_in&&nonce='.$nonce)));
 return ob_get_clean();
 }
 ob_start();
 ?>
 <div class="clockin-wrap">
 <a class="clockin_anchor" href=<?php echo $href; echo ($type != "ajax")?" target=".$type.'"':''; ?> ><?php echo $message ?></a>
 </div>
 <?php 
 if($type == "ajax"){
 wp_enqueue_script ('clock_in');
 }
 return ob_get_clean();
}

What I’m doing here essentially is creating a link where ever the shortcode is. The Link will either;  direct to login, direct to github authorization, if its a project page, allow person to clock in, display logged persons projects to clock in or show clockout link. Next we need to make sure we authenticate with github, then display our projects properly

Google API + WordPress: A Problem, an Inconvienience and a Killer

I started off planning on using Google Calanders to store my clockin information. I was doing this for one main reason: Its important that how I store the information is standardized in case I attempt to export my plugin to node.js or another platform. But there is a problem I ran into that I realized may turn very annoying or worse.

Refresh Tokens are not unlimited

this will create an awkward situation once the refresh tokens run out and the website owner is forced to login and authorize again

And as a minor inconvienience

Google Authorization Doesn’t allow Get parameters in the url

Perhaps this is a problem on WordPress side more than googles in the way they setup their admin. Essentially I’m forced to create a plugin url that is independent from the direct flow. Making either the user Leave the admin interface conpletely or doing it through a popup. Then Instead of storing it in WordPress Options, we’d be storing it in a seperated SQL table to avoid loading wp-load.php. These are definitely managable, though not entirely preferable. If I’m using a content management system, I’d rather feel as though I can do everything in a very sexy and clean manner. Rather than go through hoops.

nonetheless I will continue to make the plugin as is simply for experience sake. Though I recognize this is definitely not final version

Update: What Killed using Google API as a web service was the fact that I am forced to use give my webserver an key file. This will not make future installations happy for random people. As a Result, I’m going to drop google calander from the design spec and move onto something else. This isn’t necessarilly Google Calanders Fault, but rather a design philosophy Flaw. I wanted to Use Google Calanders as My database, but I didn’t want to have to make installation a hassle. as a result, I gotta move on.

I’ll go with what is important

  1. Start DateTime
  2. Duration in seconds
  3. Github User
  4. Github Project

Transfering My Old Stuff

One of the things I’m going to have to do is be able to look at my old project in order to incorperate things I especially liked into WordPress. Among them are my Clock In system, Fractals. Now This isn’t just a two step process since most of my experimental work is on the server (which is bad mkay [your [production site should never be your experimental area {but for every rule there are exceptions}]) as a result I have to do a few things….

  1. Setup a local server
  2. Import all my files into the server
  3. Import my SQL database into the server
  4. Do little things to make sure everything is in proper working condition

Setting Up my Local Server

Setting up a local server can be done in a few ways. As I’m on windows, things become like cake. I’ve also done this on linux, and for what its worth, there is plenty of documentation out there to do it. For my purposes, I like to avoid ISS. The reason for this is I found it previously to have a bad user interface. What I’ve enjoyed in the past is Xampp.

xampp

 

Its a simple Install, It works, and Easily accessible. Theres not much else you can ask for from a service, or anything really.

Now, I don’t want to turn this into a tutorial but more of a general description of my experience. Nonetheless, Due to the nature of this, Its going to be one and I’m aware of that.

Importing All My Files

Now, I already have Filezilla installed because its near the best ftp (file transfer protocol) experience I’ve had. but just for giggles I’ll show you another cute little trick

windows ftp

 

Whenever you have any folder open in windows, you can change the path to be “ftp://your-web-server” and you will have an easy to use method of accessing your webservers files. Now I am using filezilla currently, but I should be using a revision control system such as Mecurial, tortoise or even Github. I’ll definitely say I learned my lesson about version control over the last couple months.

If your going to be installing FileZilla like I’m using, remember to install the client version. The Server is quite literally for servers (I’ll go over that on another topic), its meant to be able to receive FTP calls, not make them. Servers are listeners not talkers.

After you’ve installed, got Xampp and FileZilla installed, We want to do a few things….

  1. Figure out what our hosts ip address is
  2. Create an FTP user so we can access our files
  3. Login

Our Ip address will be localhost.
There are other ways to figure it out like understanding what our ip address is to ourselves. but when it comes down to it, localhost is the clean and simple way.

Creating an FTP user
On the Xampp Panel, you want to click “admin” for the FileZilla Section

admin

From there we open up our admin menu and go to user accounts

fzadminThen we add our user

fzuseraccountsSimple as that

Back to our Filezilla client
filezilla

 

Up top, we want Localhost, Username, Password, Go!
You can also save, but I don’t need to worry about that right now.

From there I connected to my old server, dropped all my stuff in the htdocs folder of my local server, which is located directly under your xampp root.

Import my SQL database into the server

This is a pretty simple process. You’re going to want to go to your original sites SQL admin page. Go to export, save the text file. Then go to your local page go to import and choose the text file.

SQL Export

The File

import

Making Sure Things are working Properly

This changes from system to system. For me This is completely custom and as much as I’d love to show how the system works. More importantly I’d just like to get it overwith. see you tomarrow! 🙂