Use chained method calls with Ramda
Sometimes you are working with an API, that chains method together. For example $('.foo').child('.bar').attr('class')
The example is where Firebase returns storageRef
object, that has a .child(filename)
method.
firebase.storage().ref().child('foo').putString('bar')
In imperative style you would have this function:
const createFileRef = (id, sref) => {
var filename = createFileName(id)
return sref.child(filename)
}
With Ramda this would look like this:
const createFileRef = R.useWith(
R.invoker(1, 'child'),
[
createFileName,
R.identity
]
)
Or uncurry the compose
function:
const createFileRef = R.uncurryN(
2,
R.compose(
R.invoker(1, 'child'),
createFileName
)
)
Is the fp code better? If you add a Maybe
wrapper, then maybe yes...
Just for comparison, the functional code:
const createFileName = R.concat(R.__, '/index.html')
const createFileRef = R.uncurryN(
2,
R.compose(
R.invoker(1, 'child'),
createFileName
)
)
const putContents = R.flip(R.invoker(1, 'putString'))
const storeContents = R.uncurryN(
3,
R.compose(
putContents,
createFileRef
)
)
Is the equivalent of:
const createFileName = R.concat(R.__, '/index.html')
const storeContents = (fileid, storageRef, contents) => storageRef.child(createFileName(fileid)).putString(contents)
I'm not sure, that the functional style code would be any better (more readable or maintainable)
One argument could be that if you look at the individual elements of the program, those are easy to understand:
R.compose(putContents, createFileRef)
The rest (uncurryN
) is just boilerplate so that you can have a classical style call signature: storeContents('myfile', storageRef, 'my contents')
. In most cases, if you are using functional programming, you may not end up calling storeContents
with all three arguments at the same time. Instead you would "build up" your call through several steps or pass the arguments as a tuple.
By the way the Jest mock setup for this:
const firebase = require('firebase')
jest.mock('firebase')
const mockPutString = jest.fn()
mockPutString.mockReturnValue(Promise.resolve('inserted'))
const mockChildRef = jest.fn()
mockChildRef.mockReturnValue({
putString: mockPutString
})
const storageRef = {
child: mockChildRef
}