npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

luc

v0.3.1

Published

a small javascript framework

Downloads

41

Readme

Luc

====

Build Status

Selenium Test Status

What is Luc?

Luc is a lightweight JavaScript framework that is built from the ground up targeting all browsers and Node.js. Luc provides a class system that can add compositions, mixins and statics functionality to any class without messing up the inheritance chain. Luc comes with over 40 utility Array/Object/Function methods along with over 150 Array utility methods that follow the same API and grammar. It also comes with the ability to add EventEmiter and Plugin functionality to any pre existing class. Luc has zero dependencies and currently sits at less than 650 SLOC and it is less than 7.5Kb minified and gzipped.

Node

npm install luc

Browser

Download the latest zip or check out the hosted build files luc, luc-es5-shim. Source maps come packaged with the non minified versions.

Supported Browsers

  • IE8 - latest with tentative support for IE6/7 (Our tests currently pass in them now)
  • FF3 - latest
  • Chrome
  • Opera
  • Safari 5.1 - latest
  • Tentative support for mobile (Our tests pass for the platforms that we are testing)

Examples/Code

For in depth examples and API documentation check out our docs.

Class System

###Simple define Luc.define just takes the passed in config and puts the properties on the prototype and returns a Constructor.

var C = Luc.define({
    a: 1,
    doLog: true,
    logA: function() {
        if (this.doLog) {
            console.log(this.a);
        }
    }
});
var c = new C();
c.logA();
>1
c.a = 45;
c.logA();
>45
c.doLog = false;
c.logA();

new C().logA()
>1

Simple super class

function Counter() {
    this.count = 0;
 };

 Counter.prototype = {
    getCount: function() {
        return this.count;
    },
    increaseCount: function() {
        this.count++;
    }
 }

 var C = Luc.define({
    $super:Counter
});

var c = new C()

c instanceof Counter
>true
c.increaseCount();
c.getCount();
>1
c.count
>1

Call a super's method:

    var C = Luc.define({
        $super:Counter,
        increaseCount: function () {
            this.count += 2;
            this.callSuper();
        }
    });

It can also be done this way:

     var C = Luc.define({
        $super:Counter,
        increaseCount: function () {
            this.count += 2;
            C.$superclass.increaseCount.call(this);
        }
    });
    var c = new C();
    c.increaseCount();
    c.count
    >3

###Compositions

Compositions allow the ability to add functionality to any class without changing or messing up the inheritance chain. They are more powerful than mixins because they don't have to worry about putting a state on classes using them or have to know that they may be a mixin or a standalone class.

    var C = Luc.define({
        $compositions: {
            Constructor: Luc.EventEmitter,
            name: 'emitter',
            methods: 'allMethods'
        }
    });

    var c = new C();

    c.on('hey', function() {
        console.log(arguments);
    });

    c.emit('hey', 1,2,3, 'a');
    >[1, 2, 3, "a"]
    c instanceof Luc.EventEmitter
    >false

###Default Compositions Luc comes with two default composition objects.

##Luc.compositionEnums.EventEmitter

Luc.EventEmitter is preferred as a composition over a mixin because it adds a state "_events" to the this instance when on is called.


    var C = Luc.define({
            $compositions: Luc.compositionEnums.EventEmitter
    });

    var c = new C();

    c.on('hey', function() {
        console.log(arguments);
    });

    c.emit('hey', 1,2,3, 'a');
    >[1, 2, 3, "a"]
    c instanceof Luc.EventEmitter
    >false
    c._events
    >undefined

##Luc.compositionEnums.PluginManager

The PluginManager adds a plugin functionality to any Class. Check out the methods that get added to the instance and more info in the docs

A plugin follows the following life-cycle: plugin is added to the instance -> plugin is created -> plugin init is called with instance -> if needed destroy called by instance -> destroy called on plugin Here is the most basic example using the default plugin.


    var C = Luc.define({
        $compositions: Luc.compositionEnums.PluginManager
    });

    var c = new C({
        plugins: [{
                init: function() {
                    console.log('im getting initted')
                },
                myCoolName: 'cool'
            }
        ]
    });

    >im getting initted

    c.getPlugin({myCoolName: 'coo'}) instanceof Luc.Plugin
    > true

Plugins can be of any class and can be added with addPlugin


    function MyPlugin(){}

    var C = Luc.define({
        $compositions: Luc.compositionEnums.PluginManager
    });

    var c = new C();

    c.addPlugin({Constructor: MyPlugin});
    //getPlugin takes a Constructor or match object
    c.getPlugin(MyPlugin) instanceof MyPlugin
    >true
    c.getPlugin(Luc.Plugin)
    >false

Plugins can also be destroyed individually or all of them at once.


    var C = Luc.define({
        $compositions: Luc.compositionEnums.PluginManager
    });

    var c = new C({
        plugins: [{
            init: function() {
                console.log('im getting initted ' + this.name)
            },
            destroy: function() {
                console.log('destroyed : ' + this.name)
            },
            name: '1'
        },{
            init: function() {
                console.log('im getting initted ' + this.name)
            },
            destroy: function() {
                console.log('destroyed : ' + this.name)
            },
            name: '2'
        }]
    });

    >im getting initted 1
    >im getting initted 2

    c.destroyPlugin({name: '1'});
    >destroyed : 1
    >Plugin {init: function, destroy: function, name: "1", owner: Object, init: function…}

    c.destroyPlugin({name: '1'});
    >false

    c.destroyAllPlugins();
    >destroyed : 2

###Mixins

Mixins are a way to add functionality to a class that should not add state to the instance unknowingly. Mixins can be either objects or Constructors.

    function Logger() {}
    Logger.prototype.log = function() {
        console.log(arguments)
    }

    var C = Luc.define({
        $mixins: [Logger, {
            warn: function() {
                console.warn(arguments)
            }
        }]
    });

    var c = new C();

    c.log(1,2)
    >[1,2]

    c.warn(3,4)
    >[3,4]

###Statics Statics are good for defining default configs.

var C = Luc.define({
        $statics: {
            number: 1
        }
    });

    var c = new C();
    c.number
    >undefined
    C.number
    >1

Using statics prevent subclasses and instances from unknowingly modifying all instances.

    var C = Luc.define({
        cfg: {
            a: 1
        }
    });

    var c = new C();
    c.cfg.a
    >1
    c.cfg.a = 5
    new C().cfg.a
    >5

###$class Every class defined with Luc.define will get a reference to the instance's own constructor.

    var C = Luc.define()
    var c = new C()
    c.$class === C
    >true

There are some really good use cases to have a reference to it's own constructor. Add functionality to an instance in a simple and generic way:

    var C = Luc.define({
        add: function(a,b) {
            return a + b;
        }
    });

    //Luc.Base applies first 
    //arg to the instance

    var c = new C({
        add: function(a,b,c) {
            return this.$class.prototype.add.call(this, a,b) + c;
        }
    });

    c.add(1,2,3)
    >6
    new C().add(1,2,3)
    >3

Or have a simple generic clone method :

    var C = Luc.define({
        clone: function() {
            var myOwnProps = {};
            Luc.Object.each(this, function(key, value) {
                myOwnProps[key] = value;
            });

            return new this.$class(myOwnProps);
        }
    });

    var c = new C({a:1,b:2,c:3});
    c.d = 4;
    var clone = c.clone();

    clone === c
    >false

    clone.a
    >1
    clone.b
    >2
    clone.c
    >3
    clone.d
    >4

###Luc.Base Luc.Base is the default class of for define. It is a simple class that by default applies the first argument to the instance and then calls Luc.Base.init, init is just an emptyFn which is meant to be overwritten by the defining class.

var b = new Luc.Base({
    a: 1,
    init: function() {
        console.log('hey')
    }
})
b.a
>hey
>1

We found that most of our classes do this so we made it the default. Having a config object as the first and only param keeps a clean api as well.

var C = Luc.define({
    init: function() {
        Luc.Array.each(this.items, this.logItems)
    },

    logItems: function(item) {
        console.log(item);
    }
});

var c = new C({items: [1,2,3]});
>1
>2
>3
var d = new C({items: 'A'});
>'A'
var e = new C();

If you don't like the applying of the config to the instance it can always be "disabled"

var NoApply = Luc.define({
    beforeInit: function() {

    },
    init: function() {
        Luc.Array.each(this.items, this.logItems)
    },

    logItems: function(item) {
        console.log(item);
    }
});

var c = new NoApply({items: [1,2,3]});

Array, Object, Function and other utility functions

Luc.is*

###Luc.isFalsy Return true if the object is falsy but not zero.

    Luc.isFalsy(false)
    >true
    Luc.isFalsy(0)
    >false

###Luc.isEmpty Return true if the object is empty. {}, [], '',false, null, undefined, NaN are all treated as empty.

Luc.isEmpty(true)
>false
Luc.isEmpty([])
>true

The native js type methods work as you think they should.

Luc.isObject([])
>false
Luc.isArray([])
>true

You can see that we don't have an isNull isUndefined or isNaN. We prefer to use:

obj === null
obj === undefined
isNaN(obj)

Luc.Object

###Luc.Object.apply / Luc.apply Apply the properties from fromObject to the toObject. fromObject will overwrite any shared keys. It can also be used as a simple shallow clone.

var to = {a:1, c:1}, from = {a:2, b:2}
Luc.Object.apply(to, from)
>Object {a: 2, c: 1, b: 2}
to === to
>true
var clone = Luc.Object.apply({}, from)
>undefined
clone
>Object {a: 2, b: 2}
clone === from
>false

No null checks are needed.

Luc.apply(undefined, {a:1})
>{a:1}
Luc.apply({a: 1})
>{a:1}

###Luc.Object.mix / Luc.mix Similar to Luc.Object.apply except that the fromObject will NOT overwrite the keys of the toObject if they are defined.

Luc.mix({a:1,b:2}, {a:3,b:4,c:5})
>{a: 1, b: 2, c: 5}

No null checks are needed.

Luc.mix(undefined, {a:1})
>{a:1}
Luc.mix({a: 1})
>{a:1}

###Luc.Object.each Iterate over an objects properties as key value "pairs" with the passed in function.

var thisArg = {val:'c'};
Luc.Object.each({
    u: 'L'
}, function(key, value) {
    console.log(value + key + this.val)
}, thisArg)
>"Luc"

###Luc.Object.filter Return key value pairs from the object if the filterFn returns a truthy value.

Luc.Object.filter({
    a: false,
    b: true,
    c: false
}, function(key, value) {
    return key === 'a' || value
})
>[{key: 'a', value: false}, {key: 'b', value: true}]

Luc.Object.filter({
    a: false,
    b: true,
    c: false
}, function(key, value) {
    return key === 'a' || value
}, undefined, {
    keys: true
})
>['a', 'b']

Luc.Array

Luc is optionally packaged with es5 shim so you can write es5 code in non es5 browsers. It comes with your favorite Array methods such as Array.forEach, Array.filter, Array.some, Array.every Array.reduceRight ..

###Luc.Array.toArray Take an object and turn it into an array if it isn't one.

    Luc.Array.toArray()
    >[]
    Luc.Array.toArray(null)
    >[]
    Luc.Array.toArray(1)
    >[1]
    Luc.Array.toArray([1,2])
    >[1, 2]

###Luc.Array.forEach Runs an Array.forEach after calling Luc.Array.toArray on the item. It is very useful for setting up flexible API's that can handle none one or many.

    Luc.Array.each(this.items, function(item) {
        this._addItem(item);
    });

    vs.

    if(Array.isArray(this.items)){
        this.items.forEach(function(item) {
            this._addItem(item);
        })
    }
    else if(this.items !== undefined) {
        this._addItem(this.items);
    }

###Luc.Array.pluck Flatten out an array of objects based of their value for the passed in key. This also takes account for null/undefined values.

Luc.Array.pluck([undefined, {a:'1', b:2}, {b:3}, {b:4}], 'b')
>[undefined, 2, 3, 4]

###Luc.Array.insert Insert or append the second array/arguments into the first array/arguments. This method does not alter the passed in array/arguments.

Luc.Array.insert([0,4], [1,2,3], 1);
>[0, 1, 2, 3, 4]
Luc.Array.insert([0,4], [1,2,3], true);
>[0, 4, 1, 2, 3]
Luc.Array.insert([0,4], [1,2,3], 0);
>[1, 2, 3, 0, 4]

###Luc.Array.last Return the last item of the array

var myLongArrayNameForThingsThatIWantToKeepTrackOf = [1,2,3]

Luc.Array.last(myLongArrayNameForThingsThatIWantToKeepTrackOf);
vs.
myLongArrayNameForThingsThatIWantToKeepTrackOf[myLongArrayNameForThingsThatIWantToKeepTrackOf.length -1]

###Luc.Array.removeAtIndex Remove an item from the passed in arr from the index.

var arr = [1,2,3];
Luc.Array.removeAtIndex(arr, 1);
>2
arr;
>[1,3]

###Luc.Array.fromIndex Return the items in between the passed in index and the end of the array.

Luc.Array.fromIndex([1,2,3,4,5], 1)
>[2, 3, 4, 5]

###Iterator and Matching functions

All remove* / find* methods follow the same api. *All functions will return an array of removed or found items. The items will be added to the array in the order they are found. *First functions will return the first item and stop iterating after that, if none is found false is returned. remove* functions will directly change the passed in array. *Not functions only do the following actions if the comparison is not true. All remove* / find* take the following api: array, objectToCompareOrIterator, compareConfigOrThisArg for example:

//most common use case
Luc.Array.findFirst([1,2,3, {}], {});
>Object {}

//pass in optional config for a strict === comparison
Luc.Array.findFirst([1,2,3,{}], {}, {type: 'strict'});
>false

//pass in an iterator and thisArg
Luc.Array.findFirst([1,2,3,{}], function(val, index, array){
    return val === 3 || this.num === val;
}, {num: 1});
>1

//you can see remove modifies the passed in array.
var arr = [1,2,{a:1},1, {a:1}];
Luc.Array.removeFirst(arr, {a:1})
>{a:1}
arr;
>[1, 2, 1, {a:1}]
Luc.Array.removeLast(arr, 1)
>1
arr;
>[1,2, {a:1}]


Luc.Array.findAll([1,2,3, {a:1,b:2}], function() {return true;})
> [1,2,3, {a:1,b:2}]
//show how not works with an iterator
Luc.Array.findAllNot([1,2,3, {a:1,b:2}], function() {return true;})
>[]

The Array functions are also combined with the Luc.is* functions. There are over 150 matching functions. Almost every public method of Luc.is* is available it uses the following grammar Luc.Array["methodName""isMethodName"] These functions have a consistent api that should make sense what they do from the function name. Docs

//compact like function
Luc.Array.findAllNotFalsy([false, true, null, undefined, 0, '', [], [1]])
  > [true, 0, [], [1]]

    //Or remove all empty items
    var arr = ['', 0 , [], {a:1}, true, {}, [1]]
    Luc.Array.removeAllEmpty(arr)
    >['', [], {}]
    arr
    >[0, {a:1}, true, [1]]


  Luc.Array.findFirstNotString([1,2,3,'5'])
  >1
  var arr = [1,2,3,'5'];
  Luc.Array.removeAllNotString(arr);
  >[1,2,3]
  arr
  >["5"]

As of right now there are two function sets which differ from the is api.

InstanceOf

Luc.Array.findAllInstanceOf([1,2, new Date(), {}, []], Object)
>[date, {}, []]
>Luc.Array.findAllNotInstanceOf([1,2, new Date(), {}, []], Object)
[1, 2]

In

Luc.Array.findAllIn([1,2,3], [1,2])
>[1, 2]
Luc.Array.findFirstIn([1,2,3], [1,2])
>1

//defaults to loose comparison
Luc.Array.findAllIn([1,2,3, {a:1, b:2}], [1,{a:1}])
> [1, {a:1,b:2}]

Luc.Array.findAllIn([1,2,3, {a:1, b:2}], [1,{a:1}], {type: 'deep'})
>[1]

Luc.Function

Most of these functions follow the same api: function or function[], relevant args ... with an optional config to Luc.Function.createAutmenter as the last argument.

###Luc.Function.createAugmenter

Augment the passed in function's thisArg and or arguments object based on the passed in config. Read the docs to understand all of the config options.

function fn() {
    console.log(this)
    console.log(arguments)
}

//Luc.Array.insert([4], [1,2,3], 0)
Luc.Function.createAugmenter(fn, {
    thisArg: {configedThisArg: true},
    args: [1,2,3],
    index:0
})(4)

>Object {configedThisArg: true}
>[1, 2, 3, 4]

//Luc.Array.insert([1,2,3], [4], 0)
Luc.Function.createAugmenter(fn, {
    thisArg: {configedThisArg: true},
    args: [1,2,3],
    index:0,
    argumentsFirst:false
})(4)

>Object {configedThisArg: true}
>[4, 1, 2, 3]

Luc.Array.insert([4], [1,2,3],  true)
var f = Luc.Function.createAugmenter(fn, {
    args: [1,2,3],
    index: true
});

f.apply({config: false}, [4])

>Object {config: false}
>[4, 1, 2, 3]

###Luc.Function.createThrottled Create a throttled function from the passed in function that will only get called once the number of milliseconds have been exceeded. Read the docs to understand all of the config options.

var logArgs  = function() {
    console.log(arguments)
};

var a = Luc.Function.createThrottled(logArgs, 1);

for(var i = 0; i < 100; ++i) {
    a(1,2,3);
}

setTimeout(function() {
    a(1)
}, 100)
setTimeout(function() {
    a(2)
}, 400)

>[1, 2, 3]
>[1]
>[2]

###Luc.Function.createRelayer Return a functions that runs the passed in functions the result of each function will be the the call args for the next function. The value of the last function return will be returned. Read the docs to understand all of the config options.

Luc.Function.createRelayer([
    function(str) {
        return str + 'b'
    },
    function(str) {
        return str + 'c'
    },
    function(str) {
        return str + 'd'
    }
])('a')

>"abcd"

###Luc.Function.createSequence Return a function that runs the passed in functions and returns the result of the last function called. Read the docs to understand all of the config options.

Luc.Function.createSequence([
    function() {
        console.log(1)
    },
    function() {
        console.log(2)
    },
    function() {
        console.log(3)
        console.log('finished logging')
        return 4;
    }
])()
>1
>2
>3
>finished logging
>4

###Luc.Function.createSequenceIf Return a function that runs the passed in functions if one of the functions returns false the rest of the functions won't run and false will be returned. Read the docs to understand all of the config options.

Luc.Function.createSequenceIf([
    function() {
        console.log(1)
    },
    function() {
        console.log(2)
    },
    function() {
        console.log(3)
        console.log('finished logging')
        return 4;
    }, function() {
        return false;
    }, function() {
        console.log('i cant log')
    }
])()

>1
>2
>3
>finished logging
>false

###Luc.Function.createDeferred Defer a function's execution for the passed in milliseconds. Read the docs to understand all of the config options.

###Luc.Function.emptyFn / Luc.emptyFn A reusable empty function

###Luc.Function.abstractFn / Luc.abstractFn A function that throws an error when called. Useful when defining abstract like classes

Luc.compare

Return true if the values are equal to each other. By default a deep comparison is done on arrays, dates and objects and a strict comparison is done on other types. Pass in 'shallow' for a shallow comparison, 'deep' (default) for a deep comparison 'strict' for a strict === comparison for all objects or 'loose' for a loose comparison on objects. A loose comparison will compare the keys and values of val1 to val2 and does not check if keys from val2 are equal to the keys in val1.

Luc.compare('1', 1)
>false
Luc.compare({a: 1}, {a: 1})
>true
Luc.compare({a: 1, b: {}}, {a: 1, b: {} }, {type:'shallow'})
>false
Luc.compare({a: 1, b: {}}, {a: 1, b: {} }, {type: 'deep'})
>true
Luc.compare({a: 1, b: {}}, {a: 1, b: {} }, {type: 'strict'})
>false
Luc.compare({a: 1}, {a:1,b:1})
>false
Luc.compare({a: 1}, {a:1,b:1}, {type: 'loose'})
>true
Luc.compare({a: 1}, {a:1,b:1}, {type: 'loose'})
>true
Luc.compare([{a: 1}], [{a:1,b:1}], {type: 'loose'})
>true
Luc.compare([{a: 1}, {}], [{a:1,b:1}], {type: 'loose'})
>false
Luc.compare([{a: 1}, {}], [{a:1,b:1}, {}], {type: 'loose'})
>true
Luc.compare([{a:1,b:1}], [{a: 1}], {type: 'loose'})
>false

Luc.id

Return a unique id. A prefix is an optional argument

 Luc.id()
    >"luc-0"
    Luc.id()
    >"luc-1"
    Luc.id('my-prefix')
    >"my-prefix0"
    Luc.id('')
    >"0"

Where does Luc sit now?

Luc is now in its first official release. It still sits at version 0.* and follows the http://semver.org/ versioning spec. We want to get input on what the community thinks about the API and functionality Luc provides. Luc will officially release an unchanging API after taking account for everyone's input. Once input has been gathered a 1.* version will be released.

The future

Right now Luc provides the the building blocks for small and large scale applications. It does not have any dom or html building functionality. We want to add this functionality as a submodule down the road. For the most part Luc's core API should be pretty set now. Any functionality that we want to add down the road will be done through submodules.

Issues/Discussion

Log issues with the appropriate tag. For discussions about the API or documentation use the discussion tag. Please read the known caveats of es5-shim.

FAQ

  • How does Luc support Node and IE6?
    • Luc uses es5-shim and browserify to write pure node code with all of its es5 goodness and it just works on all browsers.
  • Can I run the tests and see code coverage?
    • Feel free to run the tests in your favorite or least favorite browser. Coverage
  • Why do some tests show failing in testling
    • Not sure we are talking to them for support. There are currently only two failure but they are timing out or all the tests pass but they show as failed.
  • How do you pronounce Luc?
    • It is pronounced like the name Luke
  • What is Luc named after?