Repository: sanctuary-js/sanctuary
Pull request #424 (2017-07-16 14:42:07)

relax constraints on predicates given to S.get, S.gets, and S.parseJson
Commit message:

> These functions facilitate safe interactions with untrusted values. To be maximally useful they should each take a predicate which safely handles any input (even inconsistently typed input).



Patch:
diff --git a/index.js b/index.js
index ae8d499..0226691 100644
--- a/index.js
+++ b/index.js
@@ -3600,7 +3600,7 @@
   }
   S.props = def('props', {}, [$.Array($.String), a, b], props);
 
-  //# get :: (b -> Boolean) -> String -> a -> Maybe c
+  //# get :: (Any -> Boolean) -> String -> a -> Maybe b
   //.
   //. Takes a predicate, a property name, and an object and returns Just the
   //. value of the specified object property if it exists and the value
@@ -3617,6 +3617,12 @@
   //.
   //. > S.get(S.is(Number), 'x', {})
   //. Nothing
+  //.
+  //. > S.get($.test([], $.Array($.Number)), 'x', {x: [1, 2, 3]})
+  //. Just([1, 2, 3])
+  //.
+  //. > S.get($.test([], $.Array($.Number)), 'x', {x: [1, 2, 3, null]})
+  //. Nothing
   //. ```
   function get(pred, key, x) {
     var obj = toObject(x);
@@ -3626,9 +3632,9 @@
     }
     return Nothing;
   }
-  S.get = def('get', {}, [$.Predicate(b), $.String, a, $Maybe(c)], get);
+  S.get = def('get', {}, [$.Predicate($.Any), $.String, a, $Maybe(b)], get);
 
-  //# gets :: (b -> Boolean) -> Array String -> a -> Maybe c
+  //# gets :: (Any -> Boolean) -> Array String -> a -> Maybe b
   //.
   //. Takes a predicate, a property path (an array of property names), and
   //. an object and returns Just the value at the given path if such a path
@@ -3655,7 +3661,7 @@
     }, Just(x), keys));
   }
   S.gets =
-  def('gets', {}, [$.Predicate(b), $.Array($.String), a, $Maybe(c)], gets);
+  def('gets', {}, [$.Predicate($.Any), $.Array($.String), a, $Maybe(b)], gets);
 
   //# keys :: StrMap a -> Array String
   //.
@@ -4020,27 +4026,30 @@
   S.parseInt =
   def('parseInt', {}, [Radix, $.String, $Maybe($.Integer)], parseInt_);
 
-  //# parseJson :: (a -> Boolean) -> String -> Maybe b
+  //# parseJson :: (Any -> Boolean) -> String -> Maybe a
   //.
   //. Takes a predicate and a string which may or may not be valid JSON, and
   //. returns Just the result of applying `JSON.parse` to the string *if* the
   //. result satisfies the predicate; Nothing otherwise.
   //.
   //. ```javascript
-  //. > S.parseJson(S.is(Array), '["foo","bar","baz"]')
-  //. Just(['foo', 'bar', 'baz'])
+  //. > S.parseJson($.test([], $.Array($.Integer)), '[')
+  //. Nothing
   //.
-  //. > S.parseJson(S.is(Array), '[')
+  //. > S.parseJson($.test([], $.Array($.Integer)), '["1","2","3"]')
   //. Nothing
   //.
-  //. > S.parseJson(S.is(Object), '["foo","bar","baz"]')
+  //. > S.parseJson($.test([], $.Array($.Integer)), '[0,1.5,3,4.5]')
   //. Nothing
+  //.
+  //. > S.parseJson($.test([], $.Array($.Integer)), '[1,2,3]')
+  //. Just([1, 2, 3])
   //. ```
   function parseJson(pred, s) {
     return Z.filter(pred, encase(JSON.parse, s));
   }
   S.parseJson =
-  def('parseJson', {}, [$.Predicate(a), $.String, $Maybe(b)], parseJson);
+  def('parseJson', {}, [$.Predicate($.Any), $.String, $Maybe(a)], parseJson);
 
   //. ### RegExp
 


Test patch:
diff --git a/test/get.js b/test/get.js
index f29a25d..a929196 100644
--- a/test/get.js
+++ b/test/get.js
@@ -2,6 +2,8 @@
 
 var vm = require('vm');
 
+var $ = require('sanctuary-def');
+
 var S = require('..');
 
 var eq = require('./internal/eq');
@@ -11,7 +13,7 @@ test('get', function() {
 
   eq(typeof S.get, 'function');
   eq(S.get.length, 3);
-  eq(S.get.toString(), 'get :: (b -> Boolean) -> String -> a -> Maybe c');
+  eq(S.get.toString(), 'get :: (Any -> Boolean) -> String -> a -> Maybe b');
 
   eq(S.get(S.is(Number), 'x', {x: 0, y: 42}), S.Just(0));
   eq(S.get(S.is(Number), 'y', {x: 0, y: 42}), S.Just(42));
@@ -25,4 +27,7 @@ test('get', function() {
   eq(S.get(S.K(true), 'valueOf', null), S.Nothing);
   eq(S.get(S.K(true), 'valueOf', undefined), S.Nothing);
 
+  eq(S.get($.test([], $.Array($.Number)), 'x', {x: [1, 2]}), S.Just([1, 2]));
+  eq(S.get($.test([], $.Array($.Number)), 'x', {x: [1, 2, null]}), S.Nothing);
+
 });
diff --git a/test/gets.js b/test/gets.js
index 5123f28..26e1dbf 100644
--- a/test/gets.js
+++ b/test/gets.js
@@ -2,6 +2,8 @@
 
 var vm = require('vm');
 
+var $ = require('sanctuary-def');
+
 var S = require('..');
 
 var eq = require('./internal/eq');
@@ -11,7 +13,7 @@ test('gets', function() {
 
   eq(typeof S.gets, 'function');
   eq(S.gets.length, 3);
-  eq(S.gets.toString(), 'gets :: (b -> Boolean) -> Array String -> a -> Maybe c');
+  eq(S.gets.toString(), 'gets :: (Any -> Boolean) -> Array String -> a -> Maybe b');
 
   eq(S.gets(S.is(Number), ['x'], {x: {z: 0}, y: 42}), S.Nothing);
   eq(S.gets(S.is(Number), ['y'], {x: {z: 0}, y: 42}), S.Just(42));
@@ -27,4 +29,7 @@ test('gets', function() {
   eq(S.gets(S.K(true), ['valueOf'], null), S.Nothing);
   eq(S.gets(S.K(true), ['valueOf'], undefined), S.Nothing);
 
+  eq(S.gets($.test([], $.Array($.Number)), ['x'], {x: [1, 2]}), S.Just([1, 2]));
+  eq(S.gets($.test([], $.Array($.Number)), ['x'], {x: [1, 2, null]}), S.Nothing);
+
 });
diff --git a/test/parseJson.js b/test/parseJson.js
index 13e401c..2bbddaa 100644
--- a/test/parseJson.js
+++ b/test/parseJson.js
@@ -1,5 +1,7 @@
 'use strict';
 
+var $ = require('sanctuary-def');
+
 var S = require('..');
 
 var eq = require('./internal/eq');
@@ -9,10 +11,13 @@ test('parseJson', function() {
 
   eq(typeof S.parseJson, 'function');
   eq(S.parseJson.length, 2);
-  eq(S.parseJson.toString(), 'parseJson :: (a -> Boolean) -> String -> Maybe b');
+  eq(S.parseJson.toString(), 'parseJson :: (Any -> Boolean) -> String -> Maybe a');
 
   eq(S.parseJson(S.is(Object), '[Invalid JSON]'), S.Nothing);
   eq(S.parseJson(S.is(Array), '{"foo":"bar"}'), S.Nothing);
   eq(S.parseJson(S.is(Array), '["foo","bar"]'), S.Just(['foo', 'bar']));
 
+  eq(S.parseJson($.test([], $.Array($.Number)), '[1,2]'), S.Just([1, 2]));
+  eq(S.parseJson($.test([], $.Array($.Number)), '[1,2,null]'), S.Nothing);
+
 });