Loading..

2016-07-19 @ 09:16

Introducing Kindergarten

Why Did I Develop Kindergarten?

I am a very busy man! I have a full-time job and 2 kids waiting for me at home, therefore I don't really have time to code in my spare time, but I did find a time to create Kindergarten for a good reason. Yeah, of course I know there are already more JavaScript libraries than necessary..

Initially Kindergarten was developed by Hartog de Mik as ruby library when we used to work at OrganisedMinds. I was pretty excited about Kindergarten, because it helped us to make our complicated authorization logic more transparent. Before Kindergarten we used cancan and kept the authorization rules in one huge file. 5 years later I have started working in company that have over 100 developers and our authorization rules are insanely complex! The product has hundreds of settings and the current user can have many roles and there are some partner specific rules as well. It's crazy! As we are moving towards to split the application into micro-services in the back-end and in the front-end, this is starting to be a big problem. We currently don't have any way to share the authorization rules throughout all projects, services, libraries etc. That's why I came with the idea of implementing Kindergarten in JavaScript, because I hope it will help us to have a clean and consistent way to modularize our application.

How to Use Kindergarten

The core of Kindergarten are modules. We call those modules perimeters, because they usually represents some area in your application (specific page, widget, table, button etc.). Perimeters doesn't really have to represent a view, they can just act as a container for methods that logically belong together too. Perimeters are included into Sandbox which is another core part of Kindergarten. Perimeter can specify which methods should be exposed into the Sandbox and the rest of the methods will be private (can be used only internally by perimeter). Each perimeter must have a purpose which will act as namespace for all exposed methods. Perimeter has always access to the current user of the application (called the child). Let me show you a simple example of such perimeter.

import { createPerimeter } from 'kindergarten';
// or 
// import { Perimeter } from 'kindergarten';

const articlesPerimeter = createPerimeter({
// or
// const articlesPerimeter = new Perimeter({
  purpose: 'articles', // a namespace for all exposed methods

  govern: {
    // everybody can read articles
    'can read': () => true,

    // only admin or creator of the article might edit it
    'can update'(article) {
      return this._isAdminOrCreatorOf(article);
    },

    // User that can update articles can destroy them as well
    'can destroy'(article) {
      return this.isAllowed('update', article);
    }
  },

  // Those methods will be available through the Sandbox
  expose: [
    'read',
    'update',
    'destroy'
  ],

  _isAdminOrCreatorOf(article) {
    // Perimeter has always a valid refernce to it's child
    return this.child.role === 'admin' || (
      this.child.role === 'moderator' &&
        this.child === article.author
    ) && !this.child.isBanned;
  },

  @guard
  read(article, attrs) {
    // instead of @guard you can call:
    // this.guard('read', article);
    return article.read(attrs);
  },

  @guard
  update(article, attrs) {
    return article.update(attrs);
  },

  @guard
  destroy(article, attrs) {
    return article.destroy(attrs);
  }
});

export default articlesPerimeter;

OK, we have a perimeter now. We can now create a simple Sandbox. It's simple:

import { Sandbox } from 'kindergarten';
import Article from 'wherever/you/store/Article';
import articlesPerimeter from 'wherever/you/store/articlesPerimeter';

class ArticlesController extends Sandbox {
  constructor(currentUser) {
    super(currentUser);
    // This method comes from Sandbox
    this.loadModule(articlesPerimeter);
  }

  read(id) {
    const article = Article.find(id);
    this.articles.read(article);
  }

  update(id) {
    const article = Article.find(id);
    this.articles.update(article);
  }

  destroy(id) {
    const article = Article.find(id);
    this.articles.destroy(article);
  }
}

export default ArticlesController;

We have created an articles controller that inherits from the Sandbox. You don't necessary have to inherit from the Sandbox, but use it standalone instead. For example like this:

import { createSandbox } from 'kindergarten';
import Article from 'wherever/you/store/Article';
import articlesPerimeter from 'wherever/you/store/articlesPerimeter';

class ArticlesController extends Sandbox {
  constructor(currentUser) {
    this.sandbox = createSandbox(currentUser, {
      perimeters: [
        articlesPerimeter
      ]
    });
  }

  read(id) {
    const article = Article.find(id);
    this.sandbox.articles.read(article);
  }

  update(id, attrs) {
    const article = Article.find(id);
    this.sandbox.articles.update(article, attrs);
  }

  destroy(id) {
    const article = Article.find(id);
    this.sandbox.articles.destroy(article);
  }
}

export default ArticlesController;

It really depends on you if you prefer OOP over functional programming or vice versa. Let me show you what we can do with the articles controller:

import ArticlesController from 'wherever/you/store/ArticlesController';

const normalUser = {
  role: 'normal-user'
};

const articlesController = new ArticlesController(
  normalUser
);

articlesController.read(1); // shows the content of the article
articlesController.update(1); // throws AccessDenied error

const admin = {
  role: 'admin'
};

const adminArticlesController = new ArticlesController(
  admin
);

articlesController.update(1, {
  title: 'My New Super-Awesome Title'
}); // no problemo

Kindergarten Can Do a Lot More than That

I have shown you just a very simple example of possible usage of Kindergarten, but you can do a lot more than that! The Sandbox of the Kindergarten is protected by a governess: she is the one responsible for all rules to be respected on the Sandbox. Sandbox is using HeadGoverness by default, but there more governesses available in Kindergarten (see: https://github.com/JiriChara/kindergarten/tree/master/spec/kindergarten/governesses). There are also alternative ways to define a rules on the perimeter and much more. If you are interested to learn more please read the README or check the docs. Happy coding!

show more »

2016-01-25 @ 10:35

Make Vim Command Line Act As Bash Or ZSH

I use Vim for 5 years now and there was a one thing which was kind of driving me crazy: the command line of the Vim. Vim has a very strange mapping when it comes to it's command line e.g. <C-b> will move the cursor to the beginning of the line. I mean if you use linux shell as much as I am, then you will find yourself hitting <C-a> quite frequently, but it is not going to make the right job for you. If you wish the Vim command line to work in a same way as Bash, Zsh or ehm Emacs use the following mappings:

cnoremap <C-a> <Home>
cnoremap <C-b> <Left>
cnoremap <C-f> <Right>
cnoremap <C-d> <Delete>
cnoremap <M-b> <S-Left>
cnoremap <M-f> <S-Right>
cnoremap <M-d> <S-right><Delete>
cnoremap <Esc>b <S-Left>
cnoremap <Esc>f <S-Right>
cnoremap <Esc>d <S-right><Delete>
cnoremap <C-g> <C-c>
show more »

2015-08-14 @ 10:48

Build a Simple Channel-Based Publish/Subscribe Helper for Backbone

Backbone provides a handy Events module which can be mixed in to any object, giving the object the ability to bind and trigger custom named events. It is very useful if you wish to attach a callback function when for example a model attached to a view was changed:

var GreeterView = Backbone.View.extend({
  template: _.template('Hello <%= model.name %>!'),

  initialize: function () {
    this.model.on('change', this.render, this);
  },

  render: function () {
    this.$el.html(this.template({ model: this.model.attributes }));
    return this;
  }
});

var User = Backbone.Model.extend({});

var user = new User({ name: 'John Smith' });

var greeterView = new GreeterView({
  model: user
});

greeterView.render()

console.log(greeterView.$el.html()); //=> 'Hello John Smith!'

user.set('name', 'Johnny Smith');

console.log(greeterView.$el.html()); //=> 'Hello Johnny Smith!'

If you need two views to communicate without coupling them together, then it might get a bit trickier. I prefer channel-based publish subscribe communication for object which shouldn't necessary have to know each other. Let's imagine we have 2 views: ContentView and NavView and we would like to replace the content of the ContentView with some text when user clicks on link inside of a NavView template. ContentView does not know anything about NavView and it doesn't have a reference to it's instance to get the advantage of using events. We might create a parent view, which would work as a mediator between those two views, but things would get bit messy later on when application grows and many other views need to communicate with each other too. My preferred solution is (as I mentioned earlier) channel-based/topic-based publish subscribe pattern. You might want to use an external library like: Backbone.Radio, or Backbone.Wreqr, but both of them are in my opinion unnecessarily large and complex. Let me show you how you could implement your own channel-based publish subscribe "library" on few lines of code. suspect. Let's call it simply Vent.

// utils/vent.js

'use strict';

var Backbone = require('backbone');
var _ = require('backbone/node_modules/underscore');

var Channel = require('utils/channel');

var Vent = function () {
    this._channels = [];
};

_.extend(Vent.prototype, Backbone.Events, {
    channel: function (channelName) {
        var channel = _.find(this._channels, { name: channelName });

        return channel || (_.bind(function () {
            this._channels.push(new Channel(channelName));
            return this.channel(channelName);
        }, this)());
    }
});

module.exports = new Vent();
// utils/channel.js

'use strict';

var Backbone = require('backbone');
var _ = require('backbone/node_modules/underscore');

var Channel = function (name) {
    this.name = name;
};

_.extend(Channel.prototype, Backbone.Events);

module.exports = Channel;

So your views can communicate simply like this:

var Backbone = require('backbone');
var vent = require('utils/vent');

var ContentView = Backbone.View.extend({
  initialize: function () {
    vent.channel('root').on('show:content', this.showContent, this);
  },

  showContent: function (content) {
    // show the content ...
  }
});

var NavView = Backbone.View.extend({
  events: {
    'click a': 'notifyContent'
  },

  notifyContent: function () {
    vent.channel('root').trigger('show:content', 'The new awesome content');
  }
});

Conclusion

If you build a Backbone application w/o using of Marionette or some other extending libraries and you need a simple way object can globally communicate with each other, you might implement you own channel-based publish subscribe thingy, before sticking to some "over-complicated" vendor library.

show more »

About

Jiri Chara My name is Jiří Chára and I am an addicted JavaScript and Ruby developer. I was born in Plzeň the home of the Pilsner Beer. I currently live in Munich, Germany together with my wife, my daughter and my son. I currently work as a senior front-end engineer at AppDirect.

see more »


Copyright © 2016 Jiří Chára. All Rights Reserved.