Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
sdgluck committed Mar 27, 2017
2 parents e025d0b + a066030 commit bc7e2f8
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 29 deletions.
22 changes: 22 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Copyright (c) 2017 Sam Gluck <sdgluck@gmail.com>

MIT License

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
55 changes: 43 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<p><h1 align="center">mewt</h1></p>

<p align="center">:seedling: Immutability in under a kilobyte</p>
<p align="center">Immutability in under one kilobyte</p>

<p align="center">Made with ❤ at <a href="http://www.twitter.com/outlandish">@outlandish</a></p>

Expand All @@ -14,7 +14,11 @@

<hr/>

:cookie: Under 1kb unminified. Zero dependencies.
:seedling: Under 1kb (unminified), tiny API, zero dependencies.

:+1: Makes all native array methods immutable operations.

:v: Two simple methods `$set` and `$unset` for objects and arrays.

:point_right: Built for Node ES2015 environments. Use a bundler, transpiler, and Proxy polyfill as required.

Expand Down Expand Up @@ -42,45 +46,72 @@ var immutable = require('mewt')

## Usage

Create an immutable instance from a JavaScript array or object:
Create an immutable instance from a JavaScript array or object.

Both objects and arrays have the `$set` and `$unset` methods.

```js
let immutableArray = immutable([])
let immutableObect = immutable({})
const immutableArray = immutable([])
const immutableObect = immutable({})

immutableArray[0] = 'Van Morrison' //=> Error "array is immutable"
immutableObect.name = 'Van Morrison' //=> Error "object is immutable"
```

### Array

Use `$set` and `$unset` to create new array with applied change.

Use all array instance methods as usual, however those that would normally return a single
non-array value (pop, push, shift, unshift) will return an array containing the value and a new array
(see part 2 in example below).

```js
const arr = immutable([])

// all array instance methods are available
// 1. all array instance methods are available
const arr1 = arr.concat('bubble')

console.log(arr) //=> ['bubble']
console.log(arr1) //=> ['bubble']
console.log(arr1 === arr) //=> false

// methods with non-array return value also return new
// array via destructuring (push, pop, shift, unshift)
// 2. methods with non-array return value (push, pop, shift, unshift)
// also return new array, accessible via destructuring
const [val, arr2] = arr1.pop()

console.log(val) //=> 'bubble'
console.log(arr2) //=> []
console.log(arr2 === arr1) //=> false

// 3. use $set and $unset to get new array with changes
const arr3 = arr2.$set(0, 'Iggy Pop')

console.log(arr3) //=> ['Iggy Pop']
console.log(arr3 === arr2) //=> false
```

### Object

Use `$set` and `$unset` to create new object with applied change.

```js
const obj = immutable({})

// properties are added/updated using `$set`
const obj1 = obj.set('album', 'Hunky Dory')
// 1. properties are added/updated using `$set`
const obj1 = obj.$set('album', 'Hunky Dory')

console.log(obj1) //=> {album: 'Hunky Dory'}
console.log(obj1 === obj) //=> false

// properties are deleted using `$unset`
// 2. properties are deleted using `$unset`
const obj2 = obj1.$unset('album')

console.log(obj2) //=> {}
console.log(obj2 === obj1) //=> false
```

## Contributing

All pull requests and issues welcome!

If you're not sure how, check out the [great video tutorials on egghead.io](http://bit.ly/2aVzthz)!
31 changes: 19 additions & 12 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
/** Create immutable instance of array or object.
* @returns {Array|Object} */
/** @returns {Array|Object} */
module.exports = function mewt (target) {
let current = target
, isArr = Array.isArray(target)
let isA = Array.isArray(target)
, multiRet = 'push pop shift unshift'
, clone = v => isArr ? [].concat(v) : Object.assign({}, v)
, clone = (v, soft) => (v = isA ? [].concat(v) : Object.assign({}, v), soft ? v : mewt(v))

let override = fn => (...args) => {
let res = current[fn](...args)
current = clone(current)
return multiRet.includes(fn) ? [res, current] : current
let override = prop => (...args) => {
let res = target[prop](...args)
return multiRet.includes(prop) ? [res, clone(target)] : clone(res)
}

if (!isArr && typeof target !== 'object')
let newObj, api = {
$set: (prop, val) => (newObj = clone(target, true), newObj[prop] = val, newObj),
$unset: prop => (newObj = clone(target, true), delete newObj[prop], newObj)
}

if (!isA && typeof target !== 'object')
throw new Error('mewt accepts array or object')

return new Proxy(target, {
set: (_, prop, val) => (current = clone(current), current[prop] = val, true),
get: (_, prop) => current[prop] && (current.hasOwnProperty(prop) ? current[prop] : override(prop))
set: () => {
throw new Error(`${isA ? 'array' : 'object'} is immutable`)
},
get: (_, prop) => {
if (api[prop]) return api[prop]
return target[prop] && (target.hasOwnProperty(prop) ? target[prop] : override(prop))
}
})
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "mewt",
"version": "1.0.0",
"description": "Immutability in under a kilobyte",
"description": "Immutability in under one kilobyte",
"main": "index.js",
"scripts": {
"test": "node test"
Expand Down
65 changes: 61 additions & 4 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,36 @@ test('array', (t) => {
const n = mewt(a)
t.notEqual(a, n)
}
{
// throws on mutation
const a = mewt([])
t.throws(() => a[0] = 'Lodger', /immutable/)
}
{
// has $set & $unset
const a = mewt([])
t.equal(typeof a.$set, 'function')
t.equal(typeof a.$unset, 'function')
}
{
// get own property
const a = mewt([''])
t.equal(a[0], '')
}
{
// $set
const a = mewt([])
const n = a.$set(0, '')
t.deepEqual(n, [''])
t.notEqual(a, n)
}
{
// $unset
const a = mewt([''])
const n = a.$unset(0)
t.deepEqual(n, [])
t.notEqual(a, n)
}
{
// copyWithin
const a = mewt([1, 2])
Expand Down Expand Up @@ -76,7 +106,7 @@ test('array', (t) => {
// sort
const a = mewt(['a', 'b', 'c'])
const n = a.splice(0, 1)
t.deepEqual(n, ['b', 'c'])
t.deepEqual(n, ['a'])
t.notEqual(a, n)
}
{
Expand All @@ -98,9 +128,36 @@ test('object', (t) => {
t.notEqual(o, n)
}
{
//
const o = {}
const n = mewt(o)
// throws on mutation
const o = mewt({})
t.throws(() => o.track = 'The Promise', /immutable/)
}
{
// has $set & $unset
const o = mewt({})
t.equal(typeof o.$set, 'function')
t.equal(typeof o.$unset, 'function')
}
{
// get own property
const o = mewt({album: 'Aladdin Sane'})
t.equal(o.album, 'Aladdin Sane')
}
{
// $set
const o = mewt({})
const n = o.$set('album', 'Hours')
console.log(o.album)
t.equal(o.album, undefined)
t.equal(n.album, 'Hours')
t.notEqual(o, n)
}
{
// $unset
const o = mewt({album: 'Heroes'})
const n = o.$unset('album')
t.equal(o.album, 'Heroes')
t.equal(n.album, undefined)
t.notEqual(o, n)
}
t.end()
Expand Down

0 comments on commit bc7e2f8

Please sign in to comment.