Skip to content

Commit

Permalink
Release v1.1.0 (#21)
Browse files Browse the repository at this point in the history
- feat(set): add 'default' mode.
  • Loading branch information
msimerson authored May 3, 2024
1 parent c29fce9 commit 17c1c27
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 113 deletions.
15 changes: 15 additions & 0 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
engines:
eslint:
enabled: true
channel: 'eslint-8'
config:
config: '.eslintrc.yaml'

checks:
method-complexity:
config:
threshold: 10

ratings:
paths:
- '**.js'
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name: CI

on:
push:
pull_request:

env:
CI: true
Expand All @@ -21,3 +22,7 @@ jobs:
windows:
needs: [lint]
uses: haraka/.github/.github/workflows/windows.yml@master

macos:
needs: [lint]
uses: haraka/.github/.github/workflows/macos.yml@master
2 changes: 1 addition & 1 deletion .release
Submodule .release updated 7 files
+3 −0 CHANGELOG.md
+4 −3 README.md
+8 −0 base.sh
+82 −0 contributors.js
+1 −1 finish.sh
+36 −5 start.sh
+29 −17 submit.sh
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).

### Unreleased

### [1.1.0] - 2024-05-03

- set: added 'default' mode. See docs. #

### [1.0.7] - 2024-04-08

- use `[files]` in package.json. Delete .npmignore.
Expand Down Expand Up @@ -42,3 +46,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
[1.0.4]: https://github.com/haraka/haraka-notes/releases/tag/1.0.4
[1.0.6]: https://github.com/haraka/haraka-notes/releases/tag/1.0.6
[1.0.7]: https://github.com/haraka/haraka-notes/releases/tag/v1.0.7
[1.1.0]: https://github.com/haraka/haraka-notes/releases/tag/v1.1.0
8 changes: 8 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Contributors

This handcrafted artisinal software is brought to you by:

| <img height="80" src="https://avatars.githubusercontent.com/u/261635?v=4"><br><a href="https://github.com/msimerson">msimerson</a> (<a href="https://github.com/haraka/haraka-notes/commits?author=msimerson">23</a>)|
| :---: |

<sub>this file is maintained by [.release](https://github.com/msimerson/.release)</sub>
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,23 @@

Notes are objects that exist on Haraka connections and transactions. Prior to the release of [haraka-notes](https://github.com/haraka/haraka-notes), notes was just an empty object. Now notes is an empty object with two functions:

### set (path, value)
## Usage

Sets a note at a dot delimited path to the specified value. The path can be any number of levels deep and any missing objects in the path are [autovivified](https://en.wikipedia.org/wiki/Autovivification). Perl refugees, contain yourselves.
```js
const Notes = require('haraka-notes')
const myNote = new Notes()

myNote.set('some.path', 'a value') // { some: {path: 'a value'}}
myNote.get('some.path') // 'a value'
```

### set (path, value, [onlyIfUndefined])

Sets a note at a dot delimited path to the specified value. The path can be any number of levels deep and any missing objects in the path are [autovivified](https://en.wikipedia.org/wiki/Autovivification). Perl afficianados, contain yourselves.

#### set default

If the third set argument is any truthy value, then the property is only set if the current value is undefined. This is useful for applying default values.

```js
connection.transaction.notes.set('queue.wants', 'smtp_forward')
Expand Down
76 changes: 40 additions & 36 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,54 +1,58 @@
class Notes {
constructor(notes) {
if (notes && typeof notes === 'object') {
Object.assign(this, notes)
}

Object.defineProperty(this, 'set', {
configurable: false,
enumerable: false,
writable: false,
value: assignPathValue.bind(this),
})

Object.defineProperty(this, 'get', {
configurable: false,
enumerable: false,
writable: false,
value: getPathValue.bind(this),
})
constructor(notes) {
if (notes && typeof notes === 'object') {
Object.assign(this, notes)
}

Object.defineProperty(this, 'set', {
configurable: false,
enumerable: false,
writable: false,
value: assignPathValue.bind(this),
})

Object.defineProperty(this, 'get', {
configurable: false,
enumerable: false,
writable: false,
value: getPathValue.bind(this),
})
}
}

module.exports = Notes

function getSegments(path) {
// a dot.delimited.path
if (typeof path === 'string') return path.split('.')
// a dot.delimited.path
if (typeof path === 'string') return path.split('.')

// ['one', 'two', 'thr.ee']
if (Array.isArray(path)) return path
// ['one', 'two', 'thr.ee']
if (Array.isArray(path)) return path
}

function assignPathValue(path, value) {
if (path === undefined || value === undefined) return
function assignPathValue(path, value, onlyWhenUndefined) {
if (path === undefined || value === undefined) return

const segments = getSegments(path)
let dest = this
const segments = getSegments(path)
let dest = this

while (segments.length > 1) {
while (segments.length > 1) {
// create any missing paths
if (!dest[segments[0]]) dest[segments[0]] = {}
// set dest one path segment deeper
dest = dest[segments.shift()]
}
if (!dest[segments[0]]) dest[segments[0]] = {}
// set dest one path segment deeper
dest = dest[segments.shift()]
}
if (onlyWhenUndefined) {
if (dest[segments[0]] === undefined) dest[segments[0]] = value
} else {
dest[segments[0]] = value
}
}

function getPathValue(path) {
if (!path) return
const segments = getSegments(path)
return segments.reduce((prev, curr) => {
return prev ? prev[curr] : undefined
}, this)
if (!path) return
const segments = getSegments(path)
return segments.reduce((prev, curr) => {
return prev ? prev[curr] : undefined
}, this)
}
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "haraka-notes",
"version": "1.0.7",
"version": "1.1.0",
"description": "Haraka Notes",
"main": "index.js",
"files": [
Expand All @@ -13,8 +13,8 @@
"prettier": "npx prettier . --check",
"prettier:fix": "npx prettier . --write --log-level=warn",
"test": "npx mocha@10",
"versions": "npx @msimerson/dependency-version-checker check",
"versions:fix": "npx @msimerson/dependency-version-checker update && npm run prettier:fix"
"versions": "npx dependency-version-checker check",
"versions:fix": "npx dependency-version-checker update && npm run prettier:fix"
},
"repository": {
"type": "git",
Expand Down
140 changes: 69 additions & 71 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,91 +3,89 @@ const assert = require('assert')
const Notes = require('../index')

describe('notes', () => {
beforeEach((done) => {
this.notes = new Notes()
done()
})
beforeEach((done) => {
this.notes = new Notes()
done()
})

it('exports an object', (done) => {
// console.log(this.notes)
assert.ok(typeof this.notes === 'object')
done()
})
it('exports an object', () => {
assert.ok(typeof this.notes === 'object')
})

const functionList = ['get', 'set']
const functionList = ['get', 'set']

functionList.forEach((fn) => {
it(`has ${fn}()`, (done) => {
assert.equal(typeof this.notes[fn], 'function')
done()
})
for (const fn of functionList) {
it(`has ${fn}()`, () => {
assert.equal(typeof this.notes[fn], 'function')
})
}

functionList.forEach((fn) => {
it(`ignores attempts to redefine ${fn}`, (done) => {
this.notes[fn] = 'turd'
this.notes[fn]('turd')
done()
})
for (const fn of functionList) {
it(`ignores attempts to redefine ${fn}`, () => {
this.notes[fn] = 'turd'
this.notes[fn]('turd')
})
}

it('sets a top level value', (done) => {
this.notes.set('foo', 'bar')
// console.log(this.notes)
assert.equal(this.notes.foo, 'bar')
done()
})
it('sets a top level value', () => {
this.notes.set('foo', 'bar')
assert.equal(this.notes.foo, 'bar')
})

it('can set a false value', (done) => {
this.notes.set('boolean', false)
assert.equal(this.notes.boolean, false)
done()
})
it('can set a false value', () => {
this.notes.set('boolean', false)
assert.equal(this.notes.boolean, false)
})

it('gets a top level value', (done) => {
this.notes.set('foo', 'bar')
assert.equal(this.notes.get('foo'), 'bar')
done()
})
it('gets a top level value', () => {
this.notes.set('foo', 'bar')
assert.equal(this.notes.get('foo'), 'bar')
})

it('sets/gets a second level value', (done) => {
this.notes.set('seg1.seg2', 'bar')
assert.equal(this.notes.seg1.seg2, 'bar')
assert.equal(this.notes.get('seg1.seg2'), 'bar')
done()
})
it('sets/gets a second level value', () => {
this.notes.set('seg1.seg2', 'bar')
assert.equal(this.notes.seg1.seg2, 'bar')
assert.equal(this.notes.get('seg1.seg2'), 'bar')
})

it('sets/gets a three level value', (done) => {
this.notes.set('one.two.three', 'floor')
assert.equal(this.notes.one.two.three, 'floor')
assert.equal(this.notes.get('one.two.three'), 'floor')
done()
})
it('sets/gets a three level value', () => {
this.notes.set('one.two.three', 'floor')
assert.equal(this.notes.one.two.three, 'floor')
assert.equal(this.notes.get('one.two.three'), 'floor')
})

it('supports array syntax', (done) => {
this.notes.set(['one', 'two', 'three'], 'floor')
assert.equal(this.notes.one.two.three, 'floor')
assert.equal(this.notes.get(['one', 'two', 'three']), 'floor')
done()
})
it('supports array syntax', () => {
this.notes.set(['one', 'two', 'three'], 'floor')
assert.equal(this.notes.one.two.three, 'floor')
assert.equal(this.notes.get(['one', 'two', 'three']), 'floor')
})

it('array syntax tolerates dots', (done) => {
this.notes.set(['one', 'two', 'three.four'], 'floor')
assert.equal(this.notes.one.two['three.four'], 'floor')
assert.equal(this.notes.get(['one', 'two', 'three.four']), 'floor')
done()
})
it('array syntax tolerates dots', () => {
this.notes.set(['one', 'two', 'three.four'], 'floor')
assert.equal(this.notes.one.two['three.four'], 'floor')
assert.equal(this.notes.get(['one', 'two', 'three.four']), 'floor')
})

it('sets default sets a property', () => {
this.notes.set(['one', 'two'], 'tree', true)
assert.equal(this.notes.one.two, 'tree')
})

it('set default does NOT change defined property', () => {
this.notes.set('one.two', 'tree', true)
this.notes.set('one.two', 'three', true)
assert.equal(this.notes.one.two, 'tree')
})
})

describe('notes + object', () => {
it('assigns instantiation object', (done) => {
const passIn = {
one: true,
two: 'false',
three: 'floor',
}
this.notes = this.notes = new Notes(passIn)
assert.deepEqual(this.notes, passIn)
done()
})
it('assigns instantiation object', () => {
const passIn = {
one: true,
two: 'false',
three: 'floor',
}
this.notes = this.notes = new Notes(passIn)
assert.deepEqual(this.notes, passIn)
})
})

0 comments on commit 17c1c27

Please sign in to comment.