Code Monkey home page Code Monkey logo

useless's Introduction

Use Less. Do More.

A cross-platform JavaScript toolbox for writing complex web applications. Currently in pre-release stage, missing some basic documentation and being under heavy development. In near future, it will be split into several loosely coupled NPM modules, for everyone's convenience. Stay tuned.

Installing | Building | Wiki

> npm install useless

Upcoming features

  • Brand new test system based on Promises
  • Nonlinear/asynchronous logging and stack traces (for Promise-based code)
  • Splitting of distinct framework parts to separate projects (finally, useful ones).

Recent updates / changelog

Browser builds

Running example app

> node example.js

If everything's ok, example app will be running at http://localhost:1333. Currently there's not much example code, but it's on the way.

You may want to look into these projects (built upon Useless.js):

  • Skychat — a simple WebRTC paint/chat app.
  • Wyg — a revolutionary WYSIWYG editor (demo).

Macro processor for prototype definitions

How-to & Examples

  • $prototype / $extends
  • Smart property declarations
  • $static methods / properties
  • $memoized properties
  • Tag groups on members ($static: { ... })
  • RTTI
  • Pluggable macros for custom syntax
  • Member aliases
  • $final
  • $traits / a.k.a. mixins / combinatoric-style alternative to inheritance
  • $aspect / Aspect Oriented Programming / declarative method binding
  • $singleton
  • Reflection (can read prototype name and file, via Prototype.$meta)
Vec2 = $prototype ({

    /*  Static property: Vec2.zero
     */
    zero: $static ($property (function () { return new Vec2 (0, 0) })),

    /*  Static method: Vec2.dot (a, b)
     */
    dot: $static (function (a, b) { return a.x * b.x + a.y * b.y }),

    /*  Tag groups for convenience
     */
    $static: {
        unit: $property (function () { return new Vec2 (1, 1) }),
        one:  $alias ('unit') }, // member aliases

    /*  Constructor
     */
    constructor: function (x, y) { this.x = x; this.y = y },

    /*  Instance property (.length)
     */
    length: $property (function () { return Math.sqrt (this.x * this.x + this.y * this.y) }),

    /*  Instance method
     */
    add: function (other) { return new Vec2 (this.x + other.x, this.y + other.y) } })

/*  Inheritance (relies on native JavaScript prototype semantics)
 */
BetterVec2 = $extends (Vec2, { /* ... */ })

Component model

How-to & Examples

  • Binds own methods to this automatically
  • Manages bindable $trigger / $barrier / $observableProperty members
  • Tracks bound components / auto-unbinds upon deinitialization
  • Holds parent-child relationship / handles automatic deinitialization
  • Enables $traits to chain into method calls by overlapping method definitions
  • Enforces configuration contracts ($requires, $defaults)

Functional primitives

Reference

  • Extends underscore.js _ namespace
  • Functional primitives busybox (predicates, operators / higher order stuff)
  • Binding to tail of argument list / flipping argument list
  • Limiting number of arguments (arity)
  • Infix interface (as Array/String/Function extensions)
  • Handy string-processing operators
  > [[1,2], [3,4]].zip (_.seq (_.sum, _.appends ('_foo'), _.quotesWith ('()')))
  < ["(4_foo)", "(6_foo)"]

Abstract map/zip/filter/reduce/find

Reference

Datatype-abstract (works over arrays/objects/scalars):

  > _.map2 ({ one: '1', two: '2' }, _.prepends ('foo_').then (_.appends ('_bar')))
  < { one: "foo_1_bar", two: "foo_2_bar" }

Structure-abstract ('sees through' structure of arbitrary complexity):

   > _.mapMap ({ foo: { bar: 1, baz: [2, 3] } }, _.plus (10))
   < { foo: { bar: 10, baz: [12, 13] } }

Asynchronous primitives

Reference

Continuation-passing style (_.cps.xxx) versions of underscore.js primitives:

   function searchRemoteFilesForText (text, then) {
      _.cps.find (['file1.txt', 'file2.txt', 'file3.txt'], function (name, return_) {
          $.get (name, function (fileText) {
              return_ (fileText.indexOf (text) >= 0) }) }, then) }
   log (_.map ([1,2,3],     _.constant ('stub'))      // prints ['stub','stub','stub']
    _.cps.map ([1,2,3], _.cps.constant ('stub'), log) // prints ['stub','stub','stub']
   cachedReadFile = _.cps.memoize (_.tails ($.get, 'text'))
   cachedReadFile ('/useless.js', log) // prints contents of /useless.js

Sequential composition of asynchronous operations:

  _.cps.sequence (doRoutine, waitUntilAssertionsComplete, done) ()

Task pooling (parallel map/reduce with limit on maximum concurrently running tasks):

  _.mapReduce (array, {
                  maxConcurrency: 10,
                  next: function (item, index, next, skip, memo) { ... },
                  complete: function (memo) { ... })

_.interlocked (puts a function under concurrency lock)

  readFilesSequentially = _.interlocked (function (releaseLock, file, done) {
                                         $.get (file, done.then (releaseLock), 'text') })

  readFilesSequentially ('file1.txt', log)
  readFilesSequentially ('file2.txt', log) // waits until file1.txt is read

Multicast model for method calls with simple functional I/O

Reference

  • _.trigger, _.triggerOnce / one-to-many broadcast
  • _.barrier / synchronization primitive
  • _.observable / state change notifications

Raw API (same for every mentioned primitive):

var mouseMoved = _.trigger ()

/*  Binding
 */
mouseMoved (function (x, y) { }) // bind
mouseMoved (someCallback)        // bind another
mouseMoved.once (someCallback)   // bind with 'once' semantics (auto-unbinds itself upon calling)

/*  Calling
 */
mouseMove (12, 33)               // call

/*  Unbinding
 */
mouseMove.off (someCallback)     // unbinds specific listener
mouseMove.off ()                 // unbinds everything
_.off (someCallback)             // unbinds callback from everything it's bound to

Using $component:

Compo = $component ({

    didLayout:     $trigger (),
    layoutReady:   $barrier (),             // it's like jQueryish $(document).ready
    value:         $observableProperty (),  // for property change notifications
    
    init: function () {
        doSomeUselessAsyncJob (function () {
           this.layoutReady () }) }, // signals that layout is ready

    doLayout: function () {
        this.didLayout () } })       // simply call to perform multicast
compo = new Compo ()

compo.didLayout (function () {
    /*  Gets called whenether layout has rebuilt */ })

compo.layoutReady (function () {
    /*  Postpones until DOM is ready.
        If already, calls immediately (like $(document).ready) */ })

compo.valueChange (function (value, oldValue) {
    /*  Gets called whenether property has assigned distinct value */ })

compo.value = 10 // simply assign a value to notify listeners
compo.value = 10 // won't trigger, as not changed

Bindable methods for ad-hoc code injection

Raw API:

_.onAfter   (Player.prototype, 'move', function (x, y) { /* this will execute after move calls */ })
_.onBefore  (Player.prototype, 'move', function (x, y) { /* this will execute before */ })
_.intercept (Player.prototype, 'move', function (x, y, originalMethod) {
    originalMethod.call (this, x, y) })

Using $component + 'once' semantics:

Button = $component ({
    layout: $bindable (function () { /* ... */ }) })
    
button = new Button ()
button.layout.onceBefore (function () { log ("I'm called before next layout()") })
button.layout ()
button.layout () // won't print anything

Using $aspect:

AddsLoggingToButton = $aspect (Button, {

    beforeCreate: function () { log.green ('Button is about to be created') },
    afterDestroy: function () { log.red   ('Button is now destroyed') } })

Adds CORS proxy to existing XMLHttpRequest prototype:

XMLHttpRequestWithCORS = $aspect (XMLHttpRequest, {
    open: function (method, path, async, impl) {
                return impl.call (this, method, (!path.contains ('cors.io') &&
                                                 !path.contains (window.location.host))
                                                    ? ('http://cors.io/?u=' + path) : path, async) } })

Math utility for front-end works

Reference

Working with ranges:

    _.lerp  (t, min, max)  // linear interpolation between min and max
    _.clamp (n, min, max)  // clips if out of range
    
    /*  Projects from one range to another (super useful in widgets implementation)
     */
    _.rescale (t, [fromMin, fromMax], [toMin, toMax], { clamp: true })

Vector math (Vec2, Transform, BBox, Bezier, intersections):

   var offsetVec = this.anchor.sub (this.center).normal.perp.scale (
                       Bezier.cubic1D (
                           Vec2.dot (direction.normal, upVector), 0, 1.22, 0, 1.9))
   var where = this.bodyBBox.nearestPointTo (this.anchor, this.borderRadius)
   domElement.css (BBox.fromPoints (pts).grow (20).offset (position.inverse).css)

Platform Abstraction Layer

/*  A cross-platform alias for the object representing global namespace.
    Use instead of `window` for cross-platform code.
 */
$global.foo = 5 

/*  Implements `alert` for NodeJS platform.
 */
alert ('foo')

/*  Working with uncaught exceptions
 */
_.withUncaughtExceptionHandler (function (e) { throw e /* re-throw */ }, // adds handler to chain
                                function (done) {
                                    ...
                                    done () })                           // removes handler from chain
                                    
/*  Platform detection
 */
Platform = $singleton ({ $property: {
    
    engine: ... // 'browser' / 'node'
    system: ... // 'iOS' / 'Android' / undefined for everything else
    device: ... // 'iPad' / 'iPhone' / undefined for everything else
    touch:  ... // true for touch-enabled devices

    Browser: ... // true in browser
    NodeJS:  ... // true in Node
    iPad:    ... // true on iPad,
    iPhone:  ... // true on iPhone,
    iOS:     ... // true on any iOS device } })

Error handling

node.js stacktrace

Panic.js demo

  • Cross-platform uncaught exception handling (works around incomplete 'onerror' impl. in Safari).
  • Uncaught exceptions pass through network API calls
  • Client displays server's exceptions as if it was single environment
  • Complete API for it's internals
  • Strips third party calls (clean mode)
  • Fetches source code (local/remote)
  • Nice output
    • Console mode (replaces default Node.js exception printer)
    • GUI mode (a pop-up dialog with expandable source lines)

Test framework

How-to & Examples

assertion demo

assertion demo

  • Tests before code
  • Tests as documentantion
  • Rich library of assertions
  • Asynchronous / nested assertions
  • Intercepts global log, displaying it in running assertion context
  • Custom assertions
  • Humane error reporting
  • Browser-side support (see demo: youtube.com/watch?v=IWLE8omFnQw)

Logging

Reference / examples

log demo log demo

  • Platform-independent
  • Color output (even in WebInspector)
  • Shows code location
  • Configurable object printer
  • Table layout formatting
  • Hookable/interceptable

Server app framework

Example:

require ('useless')

UselessApp = $component ({

	api: function () { return {
		'/':           this.file ('./static/index.html'),
		'hello-world': this.helloWorld } },

	helloWorld: function (context) { context.success ('Hello world!') },

	$traits: [

		require ('useless/server/exceptions'),
		require ('useless/server/tests'),
		require ('useless/server/deploy'),
		require ('useless/server/api'),
		require ('useless/server/io'),
		require ('useless/server/http') ],

	init: function (then) {
	    then ()
		log.ok ('App started') } })

module.exports = { init: function () { return new UselessApp () } }

Following are $traits defined at useless/server:

  • api.js URL routing
  • args.js command line arguments parsing
  • config.js handles config.json and default parameters
  • deploy.js self-deployment protocol (automatic builds)
  • devtools.js APIs for Git / source code access (developer mode)
  • exceptions.js custom exception printer + error handling for requests
  • history.js journal for DB operation
  • http.js request serving basics
  • io.js basic I/O for requests
  • supervisor.js auto-restart on source code change
  • templating.js basic templating (via underscore)
  • tests.js self-tests on startup
  • uploads.js file uploads
  • uptime.js uptime tracking / restart()
  • websocket.js WebSocket utility (peer tracking / auth / multicast)

And more..

Installing

From NPM

Type npm install useless in root directory of your project.

From Git

  1. Go to node_modules subfolder of your project
  2. Run git clone https://github.com/xpl/useless.git, go to useless folder
  3. Run npm install to install dependencies
  4. Optionally, run node build.js to test if everything's ok

As monolithic pre-built script

Go to build folder and pick useless.js. For minified version (with unit tests stripped) pick useless.min.js. This one is ready to be used in production setup.

This version includes only the platform-independent part of the library, not including any of the ./client features. If you need any of these, you can either link them them in browser code separately, or make custom build file with the additional files included (see instructions below).

Building

Build command:

node build.js <header-file-1> <header-file-2> ... <header-file-N> <output-folder> [no-compress] [no-stripped]

For generic build, run node build.js ./useless.js ./build, or simply

> node build.js

It will generate ./build/useless.js by substituting $include directives found in header file. Produced result will undergo stripping of tests and comments, and then finally compiled using Google Closure Compiler, outputting minified result to ./build/useless.min.js

Custom build

To make reduced/extended distribution (with some submodules disabled or enabled), you can create your own version of default header file, commenting out unneeded $include directives or including additional ones.

There exists ./useless.micro.js as an example of reduced build. Running node build.js ./useless.micro.js ./build will produce ./build/useless.micro.min.js as output.

Integrated build

Applications that are based on top of useless/server can easily enable automatic rebuilds feature by adding following $traits to main application component:

$traits: [        
        require ('useless/server/tests'),
        require ('useless/server/deploy'),
        require ('useless/server/supervisor')

This will add test & build phase to app startup sequence, aborting if something went wrong and re-starting if source code has changed.

Default settings:

buildScriptPaths: [process.cwd (), $uselessPath],
buildScripts: ['useless.js', 'useless.micro.js', 'useless.devtools.js'],
buildPath: $uselessPath + 'build/',

useless's People

Contributors

bitdeli-chef avatar xpl avatar

Watchers

 avatar  avatar

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.