简体   繁体   中英

How do I refactor this composed function with Ramda.js?

i am writing a small utility using ramda and data.task that reads image files out of a directory and outputs their size. I got it working like so:

const getImagePath = assetsPath => item => `${assetsPath}${item}`

function readImages(path) {
  return new Task(function(reject, resolve) {
    fs.readdir(path, (err, images) => {
      if (err) reject(err)
      else resolve(images)
    })
  })
}

const withPath = path => task => {
  return task.map(function(images) {
    return images.map(getImagePath(path))
  })
}

function getSize(task) {
  return task.map(function(images) {
    return images.map(sizeOf)
  })
}

const getImageSize = dirPath => compose(getSize, withPath(dirPath), readImages)

The problem is with the withPath function that adds the correct image path to the image file name but forces my api to pass in the directoryName twice: once for reading the files and second time for reading the path. This means I have to call the getImageSize function like so:

const portfolioPath = `${__dirname}/assets/`

getImageSize(portfolioPath)(portfolioPath).fork(
  function(error) {
    throw error
  },
  function(data) {
    console.log(data)
  }
)

Is there any way to pass the dirname as a parameter only once? I want the api to work like this:

getImageSize(portfolioPath).fork(
  function(error) {
    throw error
  },
  function(data) {
    console.log(data)
  }
)

You shouldn't be building paths manually like that

One of Node's better APIs is the Path module – I would recommend that your readImages wrapper is made a generic readdir wrapper, and instead resolve an Array of path.resolve 'd file paths

const readdir = dir =>
  new Task ((reject, resolve) =>
    fs.readdir (dir, (err, files) =>
      err
        ? reject (err)
        : resolve (files.map (f => path.resolve (dir, f)))


const getImagesSizes = dir =>
  readdir (dir) .map (R.map (sizeOf))

Wrapping the Node continuation-passing style APIs just to return a Task gets to be a bother, doesn't it?

const taskify = f => (...args) =>
  Task ((reject, resolve) =>
    f (...args, (err, x) =>
      err ? reject (err) : resolve (x)))

const readdir = (dir, ...args) =>
  taskify (fs.readdir) (dir, ...args)
    .map (R.map (f => path.resolve (dir, f)))

const getImagesSizes = dir =>
  readdir (dir) .map (R.map (sizeOf))

You should probably also take care to file out file paths that are directories – unless your sizeOf implementation handles that

I managed to solve this by passing the Task resolution a single object like so:

function readImages(path) {
  return new Task(function(reject, resolve) {
    fs.readdir(path, (err, images) => {
      if (err) reject(err)
      else resolve({ images, path })
    })
  })
}

const withPath = task => {
  return task.map(function({ images, path }) {
    return images.map(getImagePath(path))
  })
}

...and then destructing it out of the task payload and now my compose function looks like this:

module.exports = (function getImageSize(dirPath) {
  return compose(getSize, withPath, readImages)
})()

And my api call looks like this:

getImageSize(portfolioPath).fork(
  function(error) {
    throw error
  },
  function(data) {
    console.log(data)
  }
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM