Giter Club home page Giter Club logo

maki's Introduction

Maki

Project Status Build Status Coverage Status Total Contributors Sustainability

The complete stack for building extensible apps, faster than ever. Hand-roll your application by telling Maki what your application does, and it takes care of the rest – without getting in your way if you want to customize it.

  • Write Once, Deploy Everywhere Maki enables a standard definition for applications beyond simply web apps. Because of our resource grammar, we can build desktop and native mobile applications directly – all in supplement to your web application. All with the same code.
  • Resource-Derived Infrastructure REST makes a lot of sense for APIs – we take it one step further and build the entire application around Resources as named channels, serving events and static documents alike. Even Desktop applications built with Maki use the same, familiar API!
  • Robust Plugin Ecosystem Maki is an extensible framework – and there's already a huge list of plugins to provide common (and some not so common!) functionality to your application with almost zero-configuration. For example, Maki's identity protocol allows us to support both username/password auth and cryptographic identity!

Quick Start

You'll need node.js to build a Maki application. Additionally, MongoDB and Redis are the default storage and messaging engines, so you will need to install and configure them to use the defaults, or override them if you'd like to use something different. We'll be changing this in an upcoming release – see #58 for progress!

  1. Install Maki: npm install martindale/maki
  2. Create your app, perhaps in yourapp.js:
var Maki = require('maki');
var myApp = new Maki();

myApp.define('Widget', {
  attributes: {
    name: String
  }
});

myApp.start();
  1. Start your app: node yourapp.js – by default, accessible at http://localhost:9200

Behaviors

Maki applications allow you to construct pipelines, as follows:

// same as above
var Widget = myApp.define('Widget', { attributes: { name: String } });

Widget.pre('create', function(next, done) {
  var widget = this;
  // do something before creation...
  // call next() to continue creation, or done() to complete.  Pass done(err) to
  // abort the pipeline!  Useful for custom validation rules. :)
});

Widget.post('create', function() {
  var widget = this;
  // do something after creation!  This too is a pipeline – the tasks are
  // executed in the order they are injected.
});

Methods

All Maki resources expose these five methods, all of which follow the above pipeline:

  • query to select a list of documents,
  • get to get a single instance of a document by its identifier (ID),
  • create to create a new instance of a document,
  • update to change properties of a document, and
  • destroy to remove a document.

Plugins & Modules

Maki aims to be as lightweight as possible while still offering a base stack that implements #1. We've split out as many components as possible, and offer a list of plugins that can be used to add functionality to any Maki-powered app.

Core Modules

Name Version Build Status Coverage
maki-queue NPM Package Build Status Coverage Status
maki-mongoose-hooks NPM Package Build Status Coverage Status
maki-service-websockets NPM Package Build Status Coverage Status
maki-forms NPM Package Build Status Coverage Status

Plugins

Name Version Build Status Coverage
maki-sessions NPM Package Build Status Coverage Status
maki-passport-local NPM Package Build Status Coverage Status
maki-passport-github NPM Package Build Status Coverage Status
maki-passport-soundcloud NPM Package Build Status Coverage Status
maki-analytics NPM Package Build Status Coverage Status
maki-forms NPM Package Build Status Coverage Status
maki-types-file NPM Package Build Status Coverage Status
maki-assets NPM Package Build Status Coverage Status
maki-client NPM Package Build Status Coverage Status
maki-cms-local NPM Package Build Status Coverage Status

Documentation

For our documentation, see the docs/ subfolder.

Contributors

Special thanks to the whole Maki community for supporting this project with their presence and more.

Many, many other contributors deserve recognition for their work on the open source projects that have made Maki possible. See package.json for just a few of them.

Spirit

Please feel free to submit changes to this repo via pull requests! We're trying to keep this as general and flexible as possible, so anyone can take the project and run with it.

maki's People

Contributors

lawoftheland avatar martindale avatar melnx avatar overra avatar toriborealis avatar uncharles avatar unusualbob avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

maki's Issues

Patch Only Updates One Record

Currently, Resource.prototype.patch only updates one record – it should be able to update many. There aren't any good tests around this yet, so they should be written.

Private Fields -> Confidential Values / ZKP

Fields on a Resource that are marked private should have some common mechanic for being converted into a Confidential Value or ZKP store. Examples might include an account balance or even a password hash.

Quickstart seemingly doesn't work

Using the following source from the Quickstart section of docs,

var myApp = new require('maki');

myApp.define('Widget', {
  attributes: {
    name: String
  }
});

myApp.start();

I'm given the following error:

myApp.define('Widget', {
      ^
TypeError: Object #<Object> has no method 'define'
    at Object.<anonymous> ([REDACTED]/app.js:3:7)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

Remove Express

We're not using Express for much beyond routing. We should eliminate it in favor of some more tidy, faster microcode.

Standard Format for Representing Resources

We should consider Resources to be serializable contracts to consumers of a Maki application; perhaps we should consider utilizing Protobufs as a high-performance way of exchanging these contracts.

Open to other ideas.

Implement Queues

A common pattern is to utilize a job scheduler for operations that may take an extended period of time. Maki should have this built-in.

Interaction Layer Compositing

Views for a Maki application should compile to a composed UI; a la "Single Page App," updating only components that have changed dependent on state. Maki applications should target all clients (web, mobile, desktop), with the necessary abstraction being behaviors, not UI elements.

Behaviors, not UIs
When we spend time crafting how a menu works in the browser vs. how it works on a mobile browser, or even a different interaction model for an installed desktop application, and furthermore the more difficult mobile application – we're thinking carefully about the optimal experience for the current context of the content type they're interacting with.

Rather than repeating this process several times over for each individual component exposed by your application, we should describe composable interface elements that behave and extend one another differently based on those contexts. If we construct an application with these elements, we can categorically change behaviors across our application by simply changing their parent classes. With an appropriate inheritance model, we can make a convenient set of construction blocks that will form our composition library.

Likely choices:

  • React to manage view updates
  • Semantic UI to categorically describe behaviors (see also react-semantify)
  • Jade to write composable interactions
  • react-jade to tie the loop

An Example:
views/index.jade

extends layouts/default

block header
  menu
    item(href="/") Home
    item(href="/people") People

block content
  video(src="youtube.com?v=asdf1234")

compiles to...

html (loosely):

<!DOCTYPE html>
<html>
<body>
  <div>
    <a href="/">Home</a>
    <a href="/people">People</a>
  </div>
  <div>
    <iframe ...>
  <div>
</body>
</html>

Services

Resources created in Maki should be exposed via an extensible Service framework, accessible over Protocols.

Authentication Middlewares

Authentication should be exposed as middlewares, pluggable to the application and the Resources.

  • BitAuth
  • Mnemonic

maki.use( require('bitauth') )

Define and create Application State

As per #117, we need an Application State layer in the Maki stack.

This is the state of the instanced application being run by an Identity. The state can be any arbitrary tree of values, all of which are observable and evented. You can bind any attribute or sub-tree to an addressable object from the Data Layer which then synchronizes two way using the events from either layer.

Discuss libraries and techniques to use, as well as the rigid interfaces in which it communicates.

Default Layout Used Erroneously

When Resources fall back to their default view, they also use the default layout – the rendering lookup should again start from the top level directory before falling back to the internal Maki version.

./app/views/layouts before node_modules/maki/app/views/layouts. Only fall back if the layout does not exist.

Get Rid of Mongo

Mongo, while a convenient choice for this first iteration of Maki, is not an appropriate datastore on its own. With the Datastore class we should move to implement some sane defaults, and perhaps even offer multiple options.

  • Reheat, for RethinkDB.
  • Osmos, for a common interface to several DB types.
  • JugglingDB, common interface, very similar to Mongoose.

Module

Using maki as a module should be straightforward: var maki = require('maki');

While maki is currently taken on npm, I am satisfied with npm install martindale/maki, which installs the package and makes it available as maki.

The current hurdles include the highly specific directory structure for looking up various components, specifically;

  • app/views

Add cURL Examples to /api

The auto-generated API documentation located at /api (currently enabled by default) should have cURL examples for each resource.

Add Filters, Aliases

Resources should be filterable, allow for aliases of these filters.

Proposal:

maki.define('Play', {
  attributes: {
    state: { type: String , enum: ['queued', 'playing', 'played', 'skipped'], default: 'queued' }
  },
  filters: {
    'playing': { state: 'playing' }
  }
}

...would add a new endpoint at /plays/playing that serves only documents with state "playing", identical to a query of /plays?state=playing.

Not sure if this is a good idea, as it doesn't "feel" pure. Feedback appreciated.

Allow Arbitrary Requirements

Currently, the name of requirements denotes the specific resource that must be queried to collect those components. Instead, if a special field (let's say resource) is provided, that should be the base Resource, and the name should be the supplied local.

Subresource Population Settings Ignored in Updates

When a Resource has a particular attribute configured to be "populated" for specific methods, these rules are not respected when the parent resource emits update events:

For example, from Melody:

var PostSchema = {
  content: { type: String },
  created: { type: Date , default: Date.now },
  _identity: { type: melody.mongoose.SchemaTypes.ObjectId , ref: 'Identity', required: true },
  _author: {
    type: melody.mongoose.SchemaTypes.ObjectId,
    ref: 'Person',
    populate: ['query', 'get']
  }
};

Perhaps handlers for the populate option should be expanded to include the update or patch methods.

Eliminate Deprecated Ciphers

MongoDB by default uses MD5 hashes for GridFS uploads; #68 adds the ability to retrieve a file based on these hashes. MD5 is deprecated, as it has known collisions.

We should switch to SHA2 or SHA3, perhaps even make this change upstream.

Migrations

When Resource definitions change, there should be a simple mechanism for timestamping the change (as deployed) and migrating to (and from) the new definitions.

Maki Site(s?) Should Serve Source Code

If you attempt to git clone a Maki-powered server, it'd be nice if it served its own source code. This should probably be an optional feature, if not a plugin.

Community Page

We need a dedicated list of /people who are members of the community. Perhaps this can also be a demonstration of using external applications to manage resources (snarl + maki?) as an agent.

Pipeline Followed Twice

It seems in some cases, the pipeline for a specific method might be executed twice. For example;

  1. Install Converse, linked on top of the redirect-debug branch
  2. Run Converse with NODE_ENV=debug
  3. Create a post
  4. Create a Comment
  5. Observe two distinct calls to resource.create:
handlers instance: { html: { create: [Function] }, json: { create: [Function] } }
provide, handlers: { html: { create: [Function] }, json: { create: [Function] } }
Trace: specific handler create /comments { hashcash: '2:2:sha256:20150717:005400680069007300200063006f006d006d0065006e0074002000730068006f0075006c00640020006800610076006500200070006f00730074002000600035003500610032003000370062003900640037003300380031006400330036003200370063003900350066003600370060002000610073002000690074007300200060005f0070006f0073007400600020006600690065006c0064002e::mrmqi:64',
  _author: '55a04123a753f10357c45248',
  _post: '55a207b9d7381d3627c95f67',
  content: 'This comment should have post `55a207b9d7381d3627c95f67` as its `_post` field.',
  _id: 55a931b574dbd38708c05891 } function (req, res) {
        console.log('create handler')

        var comment = this;
        req.flash('info', 'Comment created successfully!');
        if (comment._parent) {
          res.status( 303 ).redirect('/comments/' + comment._parent + '#comments' + comment._id );
        } else {
          console.log('suppppp', comment._post);
          res.status( 303 ).redirect('/posts/' + comment._post );
        }

      }
    at /opt/maki/lib/Service/http.js:201:17
    at Array.forEach (native)
    at ServerResponse.res.provide (/opt/maki/lib/Service/http.js:200:31)
    at /opt/maki/lib/Service/http.js:687:28
    at /opt/maki/lib/Resource/index.js:455:12
    at /opt/maki/lib/Resource/index.js:492:14
    at /opt/maki/node_modules/async/lib/async.js:240:13
    at /opt/maki/node_modules/async/lib/async.js:150:25
    at /opt/maki/node_modules/async/lib/async.js:237:17
    at /opt/maki/node_modules/async/lib/async.js:600:34
Trace: specific handler create /comments { hashcash: '2:2:sha256:20150717:005400680069007300200063006f006d006d0065006e0074002000730068006f0075006c00640020006800610076006500200070006f00730074002000600035003500610032003000370062003900640037003300380031006400330036003200370063003900350066003600370060002000610073002000690074007300200060005f0070006f0073007400600020006600690065006c0064002e::mrmqi:64',
  _author: '55a04123a753f10357c45248',
  _post: '55a207b9d7381d3627c95f67',
  content: 'This comment should have post `55a207b9d7381d3627c95f67` as its `_post` field.',
  _id: 55a931b574dbd38708c05891 } function (req, res, next) {
        // send the correct status code to clients expecting HTML (usually browsers)
        res.redirect( 303 , '/' + maki.resources[ r ].collection + '/' + req.locals[ resource.fields.id ] );
      }
    at /opt/maki/lib/Service/http.js:201:17
    at Array.forEach (native)
    at ServerResponse.res.provide (/opt/maki/lib/Service/http.js:200:31)
    at /opt/maki/lib/Service/http.js:687:28
    at /opt/maki/lib/Resource/index.js:455:12
    at /opt/maki/lib/Resource/index.js:492:14
    at /opt/maki/node_modules/async/lib/async.js:240:13
    at /opt/maki/node_modules/async/lib/async.js:150:25
    at /opt/maki/node_modules/async/lib/async.js:237:17
    at /opt/maki/node_modules/async/lib/async.js:600:34

Shared Validators

Validators for resources (and the input forms for those resources) should be shared between server and client.

Query Builder

For query requests, URI parameters should be passed into a "query builder" that constructs a query for the Resource's datastore.

This may lead to a Resource transformation pipeline, as requests come over a Protocol, access a Resource, and may be transformed along the way before being rendered to a View.

Offline Support

When a Maki application loses network connectivity, it should continue to function as completely as possible, then synchronize changes when it resumes connectivity.

See also #21 and #27.

Coverage does not run

Currently, mocha will successfully run the tests, _mocha will also successfully run the tests, but istanbul cover _mocha will fail with the following error:

$ istanbul cover _mocha


  1) "before all" hook

  0 passing (9ms)
  1 failing

  1)  "before all" hook:
     Uncaught Error: connect ECONNREFUSED
      at errnoException (net.js:905:11)
      at Object.afterConnect [as oncomplete] (net.js:896:19)

Static Build

Generating a static build of a Maki application should be possible.

  • Offline Resource Service (PouchDB?)
  • Output bundled HTML + assets to render app locally

Notification Plugin & Hooks

martindale/converse#16 implements a largely functional notification system; this should be isolated into a standalone plugin for Maki. This will likely force us to explore how plugin hooks work (event emitters, etc.).

Authentication & Authorization

Maki should offer an extensible system for authenticating individual operations (client certificates as a reasonable first option), and then authorizing those operations (a capability-based scheme as the first option). Restrictions should be made per resource, per method, and individually per logic within a resource and its methods.

Authentication should take place as the first item in the Resource pipeline, while Authorization should take place as the last item in the pre pipeline.

Crafting Resources is Hard.

Building things on today's Web often requires cutting corners to meet objectives and reduce cost. Building a REST-compliant API might not be in the scope of a project, but without it your application is difficult to extend and grow. Gracefully responding to client capabilities might not be in the scope of a project, but adding this functionality later becomes increasingly expensive.

🍱 Maki should make it easy to start a project in a way that is simpler to manage over time.


Makers building applications with Maki should never be forced to concern themselves with the intricacies of the underlying systems, but should always be permitted to tinker and replace every single component. Once a Maki application has been defined, layering interactions on top of underlying resources should never require a change in the application definition.

Application Definitions are single-form representations of an entire interaction model. These can be deployed to an execution layer, like Fabric (see #21). Generally, they describe a list of Resources (read: "types") and their Behaviors; Validators, Events, Requirements. These behaviors are modeled in per-resource-method pipelines, which flow unidirectionally through the application into the user's view.

Resources can be represented in many ways. Maki is Resource-centric, and hopes to implement a common core for rendering applications to all digital mediums. Our first objective is a complete "edge node" implementation for Fabric, which will serve a compiled HyperText bundle to a standards-based Web browser that implements a pure HTML representation of the Application Definition.

This model will give applications made with Maki the ability to deploy to all platforms from a single definition; mobile, web, desktop, and autonomous agents.

Handler Not Passed

Likely tightly co-mingled with #81. In the second call, the handler reverts to the default handler, instead of using the one provided by the Resource.

Atomic Pipelines

Users shouldn't be required to understand atomicity; instead, the pipeline of behavior for a particular resource should be all-or-nothing.

View Transformers

Views should be transformable into various output formats, so that they can be written once.

Perhaps:

var string = fs.readfileSync('some-template.jade');
var formats = ['html', 'blessed'];

maki.render( string , {
  format: 'blessed'
} , function(err, interface) {
  // compiled blessed interface based on Jade's lexer and parser
});

Requirement Recursion

Currently, the requires attribute on a Resource only climbs one level. There should be some mechanism for recursion, perhaps with a limit.

Specific use cases include hierarchical comment threads (see martindale/converse#18).

All Resources Should Have Corresponding Feeds

In particular, collections should serve either an RSS or Atom feed of the latest published items into that collection. This will require testing various RSS clients for their support of Content Negotiation – i.e., do they correctly send Accept: application/atom+xml in their requests? I can only hope so.

We may also consider feeds to be published for individual entities, perhaps using the Activity Streams specification.

Resource Editing

When viewing a resource, users should not have to deal with the idea of an "edit mode". This is not something that they would be familiar with; it is not a concept in the real world. They just change the thing.

Instead, we should offer an inline-editing interface for all resources that the current user is able to edit. This may be restricted with a "locked" mode for each page to prevent inadvertent edits, but at no point should a URL be dedicated to presenting this view.

Dependency Management

Occasionally [and in some view contexts], a resource will require others to be displayed in a relevant fashion. These dependencies should be managed without overhead, and details on how to retrieve them should be made available to the appropriate views.

Package Audit

We need to perform a package audit, including:

  • cleanup of unused packages
  • license inclusions

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.