public
Description: Fluent programming (chained method calls) for JavaScript.
Homepage: http://osteele.com/sources/javascript/fluently/
Clone URL: git://github.com/osteele/fluently.git
fluently / fluently.js
100644 95 lines (90 sloc) 3.424 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/* Copyright 2007-2008 by Oliver Steele. Released under the MIT License. */
 
var Fluently = {
    // returns a new object initialized by `fn`. `fn` is passed a single
    // argument `define` which defines chainable methods on the object.
    make:function(fn) {
        var host = {},
            finalizers = [],
            modifiers;
 
        define.alias = define.synonym = function(target, source) {
            if (arguments.length < 2)
                host[target] = host;
            else
                path(target).set(host[source]);
        }
        define.option = function(name) {
            modifiers[name] = false;
            defineSetter(name, modifiers, name, true);
        }
        define.option.dictionary = function(object) {
            modifiers = object;
        }
 
        fn(define);
        for (var i = 0; i < finalizers.length; i++)
            finalizers[i]();
        return host;
 
        function define(name, fn) {
            if (arguments.length > 1) {
                path(name).set(function() {
                    var value = fn.apply(this, arguments);
                    return typeof value == 'undefined' ? host : value;
                });
            } else {
                return {
                    sets:function(object, propertyName, value) {
                        if (arguments.length > 2)
                            defineSetter(name, object, propertyName, value);
                        return {
                            to:function(value) {
                                defineSetter(name, object, propertyName, value);
                            }
                        }
                    }
                }
            }
        }
        function path(name) {
            var target = host;
            if (name.indexOf('.') >= 0) {
                var components = name.split('.');
                while (components.length > 1) {
                    var step = components.shift();
                    target = target[step] = target[step] || {};
                }
                name = components[0];
            }
            return {target:target, name:name,
                    get:function() {return target[name]},
                    set:function(value) {target[name] = value}}
        }
        function defineSetter(methodName, object, propertyName, value) {
            host[methodName] = makeSetter(function() {
                object[propertyName]=value;
            });
        }
        // returns an object that is like host, except that all of its functions
        // call `before` first
        function makeSetter(before) {
            var modifier = {};
            finalizers.push(function() {
                for (var propertyName in host) {
                    var value = host[propertyName];
                    if (value == host)
                        value = modifier;
                    else if (typeof value == 'function')
                        value = adviseBefore(value, before);
                    modifier[propertyName] = value
                }
            });
            return modifier;
        }
        // returns a function equivalent to `fn`, except that `before`
        // is called first
        function adviseBefore(fn, before) {
            return function() {
                before.apply(this, arguments);
                return fn.apply(this, arguments);
            }
        }
    }
};