Skip to content

Commit

Permalink
Added documentation and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nknapp committed Jul 22, 2016
1 parent f6bf290 commit ccac480
Show file tree
Hide file tree
Showing 12 changed files with 352 additions and 52 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@ npm-debug.log
.directory
._*
*.iml
examples/city
7 changes: 7 additions & 0 deletions .thought/partials/api.md.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{{#if package.main}}
## API-reference

### `require("m-io/fs")`

{{{jsdoc 'fs.js' '#'}}}
{{/if}}
19 changes: 19 additions & 0 deletions .thought/partials/overview.md.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
This package is a replacement for the functions of {{npm 'q-io'}} that I use in my projects. I have use `q-io/fs` a lot since it has functions
like `makeTree`, `listTree` and `removeTree`. Furthermore, its `read` and `write` function work with strings by default, which makes it easier to
read text files.

Sadly, `q-io@1` depends on {{npm 'collections'}}@1, which
[overwrites the function `Array.prototype.find` with an implementation that does not match the ES6-spec](https://github.com/montagejs/collections/issues/139).
This causes problems in {{npm 'jsdoc-parse'}}. This is another example of why [modifying objects you don’t own][zakas dont modify]
is a bad practice.

This problem *could* be solved by using `q-io@2` instead of version 1. This version has [other problems](https://github.com/kriskowal/q-io/pull/155) which were
solved in version 1. It may be a silly feeling, but version 2 of `q-io` vseems not to receive too much care at the moment.

Since I do not use many functions, I have decided to write a drop-in replacement for my own purposes, and this is it: `m-io`.
If you like this and want to provide more methods for your needs, please go ahead and make a PR.




[zakas dont modify]: https://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/
16 changes: 16 additions & 0 deletions .thought/partials/usage.md.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{{#if (exists 'examples/example.js')}}

## Usage

The following example demonstrates how to use this module:

{{{example 'examples/example.js'}}}

This will generate the following output

{{{exec 'node example.js' cwd='examples/'}}}
{{/if}}

After deleting `city/usa`, the `city`-subtree looks liks this:

{{dirTree 'examples' 'city/**'}}
123 changes: 114 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,27 @@
[![Coverage Status](https://img.shields.io/coveralls/nknapp/m-io.svg)](https://coveralls.io/r/nknapp/m-io)


> (Incomplete) replacement for `q-io`
> (Incomplete) replacement for q-io
This package is a replacement for the functions of [q-io](https://npmjs.com/package/q-io) that I use in my projects. I have use `q-io/fs` a lot since it has functions
like `makeTree`, `listTree` and `removeTree`. Furthermore, its `read` and `write` function work with strings by default, which makes it easier to
read text files.

Sadly, `q-io@1` depends on [collections](https://npmjs.com/package/collections)@1, which
[overwrites the function `Array.prototype.find` with an implementation that does not match the ES6-spec](https://github.com/montagejs/collections/issues/139).
This causes problems in [jsdoc-parse](https://npmjs.com/package/jsdoc-parse). This is another example of why [modifying objects you don’t own][zakas dont modify]
is a bad practice.

This problem *could* be solved by using `q-io@2` instead of version 1. This version has [other problems](https://github.com/kriskowal/q-io/pull/155) which were
solved in version 1. It may be a silly feeling, but version 2 of `q-io` vseems not to receive too much care at the moment.

Since I do not use many functions, I have decided to write a drop-in replacement for my own purposes, and this is it: `m-io`.
If you like this and want to provide more methods for your needs, please go ahead and make a PR.




[zakas dont modify]: https://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/
# Installation

```
Expand All @@ -20,24 +38,111 @@ npm install m-io
The following example demonstrates how to use this module:

```js

var FS = require('m-io/fs')

// Create some files
FS.makeTree('city/germany')
.then(() => FS.write('city/germany/darmstadt.md', 'Darmstadt is nice'))
.then(() => FS.makeTree('city/usa'))
.then(() => FS.write('city/usa/new-york.md', 'New York is huge'))
.then(() => FS.makeTree('city/france'))
.then(() => FS.write('city/france/paris.md', 'Olala'))

// List files
.then(() => FS.listTree('city', (filename, stats) => stats.isFile()))
.then((filelist) => console.log('List files:', filelist.sort()))

// List dirs and files
.then(() => FS.listTree('city'))
.then((list) => console.log('List dirs and files:', list.sort()))

// Read file contents
.then(() => FS.read('city/usa/new-york.md'))
.then((nyc) => console.log('Read file contents:', nyc))

// Remove subdir
.then(() => FS.removeTree('city/usa'))
.done(() => console.log('Done'))
```

This will generate the following output

```
List files: [ 'city/france/paris.md',
'city/germany/darmstadt.md',
'city/usa/new-york.md' ]
List dirs and files: [ 'city',
'city/france',
'city/france/paris.md',
'city/germany',
'city/germany/darmstadt.md',
'city/usa',
'city/usa/new-york.md' ]
undefined
Read file contents: New York is huge
Done
```

## API-reference
After deleting `city/usa`, the `city`-subtree looks liks this:

<pre><code>
city
├─┬ france
│ └── paris.md
└─┬ germany
└── darmstadt.md
</code></pre>

## API-reference

### `require("m-io/fs")`

<a name="module_fs"></a>

## fs

* [fs](#module_fs)
* [.listTree(directoryPath, filter)](#module_fs.listTree) ⇒ <code>Promise.&lt;Array.&lt;string&gt;&gt;</code>
* [.makeTree(aPath, [mode])](#module_fs.makeTree)
* [.read(aPath)](#module_fs.read)

<a name="module_fs.listTree"></a>

### .listTree(directoryPath, filter) ⇒ <code>Promise.&lt;Array.&lt;string&gt;&gt;</code>
Custom implementation of [q-io/fs#listTree](http://documentup.com/kriskowal/q-io#listtreepath-guardpath-stat)
to avoid dependency on q-io

**Kind**: static method of <code>[fs](#module_fs)</code>
**Returns**: <code>Promise.&lt;Array.&lt;string&gt;&gt;</code> - a promise for the collector, that is fulfilled after traversal

| Param | Type | Description |
| --- | --- | --- |
| directoryPath | <code>string</code> | the base path |
| filter | <code>function</code> | a function that returns true, false or null to show that a file should be included or ignored and that a directory should be ignored completely (null) |

<a name="module_fs.makeTree"></a>

### .makeTree(aPath, [mode])
Replacement for [q-io/fs#makeTree](http://documentup.com/kriskowal/q-io#maketreepath-mode)

**Kind**: static method of <code>[fs](#module_fs)</code>

| Param | Type | Description |
| --- | --- | --- |
| aPath | <code>string</code> | the directory to be created |
| [mode] | <code>number</code> | (e.g. 0644) |

<a name="module_fs.read"></a>

### .read(aPath)
Replacement for [q-io/fs#read](http://documentup.com/kriskowal/q-io#readpath-options)

<a name="mIo"></a>
**Kind**: static method of <code>[fs](#module_fs)</code>

## mIo()
Describe your module here
| Param |
| --- |
| aPath |

**Kind**: global function
**Access:** public



Expand Down
25 changes: 25 additions & 0 deletions examples/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
var FS = require('../fs')

// Create some files
FS.makeTree('city/germany')
.then(() => FS.write('city/germany/darmstadt.md', 'Darmstadt is nice'))
.then(() => FS.makeTree('city/usa'))
.then(() => FS.write('city/usa/new-york.md', 'New York is huge'))
.then(() => FS.makeTree('city/france'))
.then(() => FS.write('city/france/paris.md', 'Olala'))

// List files
.then(() => FS.listTree('city', (filename, stats) => stats.isFile()))
.then((filelist) => console.log('List files:', filelist.sort()))

// List dirs and files
.then(() => FS.listTree('city'))
.then((list) => console.log('List dirs and files:', list.sort()))

// Read file contents
.then(() => FS.read('city/usa/new-york.md'))
.then((nyc) => console.log('Read file contents:', nyc))

// Remove subdir
.then(() => FS.removeTree('city/usa'))
.done(() => console.log('Done'))
50 changes: 28 additions & 22 deletions fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ var fs = require('fs')
var Q = require('q')
var path = require('path')

/**
*
* @module
*/
module.exports = {
/**
* Custom implementation of [q-io/fs#listTree](http://documentup.com/kriskowal/q-io#listtreepath-guardpath-stat)
* to avoid dependency on q-io
* @param {string} directoryPath the base path
* @param {function(string,fs.Stats):boolean} filter a function that returns true, false or null to show that a file
* @param {function(string,fs.Stats):boolean=} filter a function that returns true, false or null to show that a file
* should be included or ignored and that a directory should be ignored completely (null)
* @returns {Promise<string[]>} a promise for the collector, that is fulfilled after traversal
*/
Expand All @@ -26,7 +30,7 @@ module.exports = {
/**
* Replacement for [q-io/fs#makeTree](http://documentup.com/kriskowal/q-io#maketreepath-mode)
* @param {string} aPath the directory to be created
* @param {number} mode (e.g. 0644)
* @param {number=} mode (e.g. 0644)
*/
makeTree: function makeTree (aPath, mode) {
return Q.nfcall(require('mkdirp'), aPath, {mode: mode})
Expand All @@ -40,6 +44,7 @@ module.exports = {
*/
read: function read (aPath, options) {
const flags = optionsFrom(options).flags
console.log(flags)
return Q.ninvoke(fs, 'readFile', aPath, {
encoding: flags === 'b' ? null : 'utf-8'
})
Expand Down Expand Up @@ -69,13 +74,14 @@ module.exports = {
/**
* Coerce a flag-string `b` into an options-object `{ flags: 'ba' }` if necessary.
* @param optionsOrFlags
* @private
* @returns {*}
*/
function optionsFrom (optionsOrFlags) {
if (!optionsOrFlags) {
return {}
}
if (optionsOrFlags instanceof String) {
if (typeof optionsOrFlags === 'string') {
return {
flags: optionsOrFlags
}
Expand All @@ -89,6 +95,7 @@ function optionsFrom (optionsOrFlags) {
* @param {function(string,fs.Stats):boolean} filter a function that returns true, false or null to show that a file
* should be included or ignored and that a directory should be ignored completely (null)
* @param {string[]} collector array to collect the filenames into
* @private
* @returns {Promise<string[]>} a promise for the collector, that is fulfilled after traversal
*/
function walk (directoryPath, filter, collector) {
Expand All @@ -97,29 +104,28 @@ function walk (directoryPath, filter, collector) {
if (err) {
return defer.reject(err)
}
var filterResult = filter(directoryPath, stat)
// Call filter to get result, "true" if no filter is set
var filterResult = !filter || filter(directoryPath, stat)
if (filterResult) {
collector.push(directoryPath)
}
if (stat.isDirectory()) {
// false/true => iterate directory
if (filterResult !== null) {
fs.readdir(directoryPath, function (err, filenames) {
if (err) {
return defer.reject(err)
}
var paths = filenames.map(function (name) {
return path.join(directoryPath, name)
})
// Walk all files/subdirs
Q.all(paths.map(function (filepath) {
return walk(filepath, filter, collector)
}))
.then(function () {
defer.fulfill(collector)
})
// false/true => iterate directory
if (stat.isDirectory() && filterResult !== null) {
fs.readdir(directoryPath, function (err, filenames) {
if (err) {
return defer.reject(err)
}
var paths = filenames.map(function (name) {
return path.join(directoryPath, name)
})
}
// Walk all files/subdirs
Q.all(paths.map(function (filepath) {
return walk(filepath, filter, collector)
}))
.then(function () {
defer.fulfill(collector)
})
})
} else {
// No recursive call with a file
defer.fulfill(collector)
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "m-io",
"version": "0.0.2",
"description": "(Incomplete) replacement for `q-io`",
"description": "(Incomplete) replacement for q-io",
"repository": {
"type": "git",
"url": "git@github.com:nknapp/m-io.git"
Expand Down Expand Up @@ -33,6 +33,8 @@
"rimraf": "^2.5.3"
},
"devDependencies": {
"chai": "^3.5.0",
"chai-as-promised": "^5.3.0",
"ghooks": "^1.0.3",
"mocha": "^2.3.3",
"thoughtful-release": "^0.3.0"
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/tree/a/b/c.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
c
1 change: 1 addition & 0 deletions test/fixtures/tree/a/bb/cc.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cc
Loading

0 comments on commit ccac480

Please sign in to comment.