Git Product home page Git Product logo

jexl's People

Contributors

a-gorbunov avatar bitghostm avatar czosel avatar dependabot[bot] avatar eliekhoury avatar heharkon avatar mediawrbb avatar melodyn avatar nigelzor avatar rehandalal avatar sliverc avatar tomfrost avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jexl's Issues

escape dash

hi. is there any way to escape the dash (minus sign) character ?

jexl
  .eval('foo-bar == "foobar"', {
    "foo-bar": "foobar"
  })
  .then(console.log)
  .catch(console.error);

results false

Dates and times

Does this library handle the comparison of date, and times?
for example

var context = {
    birthdate: "01-01-1975',
    todaysdate: '25-03-2017'
}
jexl.eval('birthdate < todaysdate', context).then(function(res){ });

Add lodash integration to Readme?

Hi, this is a fantastic little library. Thanks for contributing it!

I've been playing with it and seeing if it'll work for us as a safe way of letting users define their own calculations which we can store in the database and apply to their own docs at runtime. Your Transforms feature is fantastic.

Using it, I thought I'd try binding a custom transform to lodash. It turns out that this can be pretty handy, and goes some way towards addressing Issue #25 (at least the example use case) and possibly #43. So I thought I'd share it here, and ask 2 questions:

  • Can you see any security issues with this (e.g. denial of service) ?
  • Would you like me to add this to the Readme ?

Lodash integration

The Transforms feature allows us to bind other 3rd-party libraries, for example Lodash :

const _ = require('lodash');
const jexl = require('jexl');

jexl.addTransform('_', (inputObj, lodashFuncName, ...arguments) => { 
  if(_.hasOwnProperty(lodashFuncName)){ 
    return _[lodashFuncName].apply(this, [inputObj].concat(arguments));
  } 
});

Then you can use many of the lodash functions, for example:

const doc2 = { employees: 
   [ { first: 'Sterling', last: 'Archer', age: 36 },
     { first: 'Malory', last: 'Archer', age: 75 },
     { first: 'Lana', last: 'Kane', age: 33 },
     { first: 'Cyril', last: 'Figgis', age: 45 },
     { first: 'Cheryl', last: 'Tunt', age: 28 } ],
  retireAge: 62 }

jexl.evalSync('employees|_("meanBy","age")', doc2)
// output: 43.4

jexl.evalSync('employees|_("maxBy","age")', doc2)
// output: { first: 'Malory', last: 'Archer', age: 75 }
jexl.evalSync('employees|_("minBy","age")', doc2)
// output: { first: 'Cheryl', last: 'Tunt', age: 28 }
jexl.evalSync('employees|_("map","age")', doc2)
// output: [ 36, 75, 33, 45, 28 ]

// === you can combine them! === 
jexl.evalSync('employees|_("map","age")|_("sum")', doc2)
// output: 217

Support for accented letters in identifiers

Hi! I have the need for "non-standard" accented letters for the identifiers. Would it be possible to include a support for those as well?

I tried it out by extending the regexps for the identifiers and is seems to work. I'll make a pull request for you to consider that.

Thanks,
Henrik.

compare

I believe this happend due to StandardJS suggestions.

version 1.1.4 was comparing string variables and number variables with == and !=

  • '2'==2 is true
  • '2'<=2 is true
  • '2'!=2 is false
  • '2'!=3 is true

version 2.0.1 breaks this by using === and !==

  • '2'==2 is false
  • '2'<=2 is true
  • '2'!=2 is true
  • '2'!=3 is true

When looking at how ``'2'<=2 is true works you should revert back to use `==` and `!=`.

Is it possible to get evaluation result synchronously?

Hi,

I need to evaluate an expression, but I need the result immediately, not wrapped into a promise. The evaluator is executed in a synchronous context and I cannot switch to async pattern. All used variables are ready, no promises.

Is this possible with the current version?

If not, are you planning to add this feature? How difficult is it to implement in current architecture, if I decide to add it by myself?

Thank you.

eval is safe

what is the difference between eval from Jexl, compared to eval from javascript?
Second question, is it really safe?

Character escaping with `\\` in a string

This issue was reported by one of my users: advanced-rest-client/arc-electron#102

The user put the following text into the input:

{ 
  "regex1": "^([\\w][\\w\\s\\-]*[\\w]|[\\w])$", 
  "regex2": "^\\-?[1-9][0-9]*(\\.[0-9]+)?$", 
  "testParam3": "${placeholder}" 
}

The result of parsing it with Jexl is:

{ 
  "regex1": "^([\w][\\w\\s\\-]*[\\w]|[\\w])$", 
  "regex2": "^\-?[1-9][0-9]*(\\.[0-9]+)?$", 
  "testParam3": "someValueFromContext" 
}

The problem is with \\ characters in 1st and 2nd lines. They got truncated but only the first occurance. At first I thought it's my implementation but after tracing the execution it gets truncated when calling Jexl API.

Is there something I can do to prevent this behavior? Or can it be fixed in Jexl?
Thanks

With addUnaryOp, evalSync evaluation differs from asynchronous eval

I used addUnaryOp to return the length of a filtered array :

jexl.addUnaryOp("length", array => { if (Array.isArray(array)) { return array.length; } return 0; });

When I build an expression like :

length objects[.myProperty in ["A","B"]]

Let's say I got this object to evaluate:

{ "objects": [ {"myProperty":"A"},{"myProperty":"B"},{"myProperty":"C"} ] }

The eval method will return 2 as result, but evalSync will evaluates the same expression to 3. It seems like the filtering part on the array vs my custom "length" are not executed with the same operation priorities depending if I use eval or evalSync.

Floating point innaccuracy when adding two numbers

When calculating the addition of two numbers

jexl.eval(that.formula, that.context, function (err, res) {
     console.log(res);
});

where that.formula is
a+b

when the context is

{
a: 1.2,
b: 1.22
}

the result is 2.42

when the context is

{
a: 1.2,
b: 1.222
}

the result is 2.4219999999999997

and when I increase b by one more decimal place

{
a: 1.2,
b: 1.2222
}

the result is 2.4222

cannot read property 'length' of undefined

Hi, there! @TomFrost
thanks for your wonderful project (!!!!), which gives me a great favour in my projects!
but i got a problem today ...
when i eval 'length' of an empty array, error was thrown.
An example here:
const emptyArrayLength = jexl.eval('names.length', {names:[]}); // throw error: cannot read property 'length' of undefined

Shouldn't jexl return 0 ?

how to make a function call in expression

H Tom,

Thanks a ton for such a great library!!!

I am trying to do something similar to one given below.

const context = {
name: { first: 'Sterling', last: 'Archer' }
}

jexl.eval('name.first.toUpperCase()', context)

My use case is , i get the expression from some other source dynamically. I have few prototype methods that i need to execute as part of expression. Is there a way to do this ?

Right now, above snippet returns an error while trying to call toUpperCase on string .

Thank you.

Path after array filter results single entry?

Hey,

Considering this:

const jexl = require('jexl');

var context = {
    name: {first: 'Sterling', last: 'Archer'},
    assoc: [
        {first: 'Lana', last: 'Kane'},
        {first: 'Lana', last: 'Kane1'},
        {first: 'Cyril', last: 'Figgis'},
        {first: 'Pam', last: 'Poovey'}
    ],
    age: 36
};

const exp = 'assoc["Lana" in .last]';

Evaluating this expression results Kane, I was expecting the filter to result two objects and then extract from both the last name: ["Kane", "Kane1"]

(btw, without data extraction after filter it does result two objects)

Thoughts?

Thanks.

null values cause error

Since version 2.2.0. Null values in context are treated differently.

Example:

const jexl = require("jexl");
const context = {
  data: {
    author: null
  }
};

const test = jexl.evalSync('data.author != null && data.author.id == "1"', context);
console.log(test);

In v2.1.1 variable test is set to false as it should be.
In v2.2.1 an error is thrown Error: Error: TypeError: Cannot read property 'id' of null. And null check like so: data.author != null && does not help.

Weirdly enough if author in context is set to undefined, it works as expected by returning false.

Shouldn't null values be handled in similar way to undefined values?

Multiline expressions

Wanted to say thank you for this incredible library. My use case is for multiline expressions

Can we please replace the lexer line for whitespace with this?

This line of code appears to be responsible for the regex that tokenizes whitespace.

//whitespace
'[\\r\\n\\t ]+',

I genuinely have no idea how to do testing or such to verify the changes, and I could use some help with contributing a pull request for this library.

Problem when parsing expressions with dotted Identifiers and multiple BinaryExpressions

There seems to be a priority problem when multiple binary expressions are used in a row involving dotted identifiers.

var jexl = require('jexl');
var context = {name: {first: 'Malory', last: 'Archer'}};
var expr = 'name.first == "Malory" && name.last == "Archer"';
jexl.eval(expr, context).catch(function(err) { console.error(err.stack); });
TypeError: Cannot read property 'type' of undefined
    at /home/mythmon/scratch/jexl-spike/node_modules/jexl/lib/evaluator/Evaluator.js:53:22
    at process._tickDomainCallback (internal/process/next_tick.js:129:7)

Digging around in the internals a bit, I got this AST from the parser:

var Lexer = require('jexl/lib/Lexer');
var Parser = require('jexl/lib/parser/Parser');
var grammar = require('jexl/lib/grammar').elements;
var tokens = new Lexer(grammar).tokenize(expr);
var parser = new Parser(grammar);
parser.addTokens(tokens);
var ast = parser.complete();
JSON.stringify(ast, null, 2);
{
  "type": "BinaryExpression",
  "operator": "==",
  "left": {
    "type": "Identifier",
    "value": "last",
    "from": {
      "type": "Identifier",
      "value": "name",
      "from": {
        "type": "BinaryExpression",
        "operator": "&&",
        "left": {
          "type": "BinaryExpression",
          "operator": "==",
          "left": {
            "type": "Identifier",
            "value": "first",
            "from": {
              "type": "Identifier",
              "value": "name"
            }
          },
          "right": {
            "type": "Literal",
            "value": "Malory"
          }
        }
      }
    }
  },
  "right": {
    "type": "Literal",
    "value": "Archer"
  }
}

That looks unbalanced to me. Specifically left.from.left is missing a corresponding left.from.right. This problem doesn't show up when using undotted identifiers.

Support for referencing individual properties in a collection

This may already be supported, but if so, I couldn't find the syntax in the documentation. Currently, Jexl supports filtering using the data[.name == 'Austin'].id syntax. I'd like to be able to basically map an array, so that we can return a subset of properties.

Here are two examples of what I'm trying to accomplish:

Sample internal map functionality:

const jexl = require('jexl');

// It would be great if this would return:
// [{ properties: { id: "abc" } }, { properties: { id: "def" } }]
const result = jexl.evalSync("data[.properties]", {
  data: [
    {
     name: 'John',
      properties: {
        id: 'abc'
      }
    },
    {
      name: 'Jane',
      properties: {
        id: 'def'
      }
    }
  ]
});

Sample mapping of sub properties:

const jexl = require('jexl');

// [{ id: "abc" }, { id: "def" }]
const result = jexl.evalSync("data[.properties.id]", {
  data: [
    {
     name: 'John',
      properties: {
        id: 'abc'
      }
    },
    {
      name: 'Jane',
      properties: {
        id: 'def'
      }
    }
  ]
});

Thoughts @TomFrost?

Ready to use frontend script

In the original project, there was a "ready to use" frontend script, in the form of a minified js file :

For the frontend, drop dist/jexl.min.js into your project and include it on your page with:
<script src="path/to/jexl.min.js"></script>

I would be a really appreciated gesture, since not everyone uses node.js (or even likes it) and since it is pretty heavy to install/configure, even more since the "trendy tools" change every month or so...

Would it be possible for you to generate a "standalone" javascript file (minified or not) to be used in a frontend only context?

Defining functions?

Is there any way to define functions in Jexl? Thank you for the hard work!

Shortcuts for logic expressions?

Many if not most programming languages implement shortcuts for the logic operators or and and:

  • For or, if the left hand parameter is true, the right hand parameter is not evaluated as the result is already determined to be true
  • For and, if the left hand parameter is false, the right hand parameter is not evaluated, as the result is already determined to be false

This allows for some checking to see if for example a calculation even makes sense (think avoiding division by zero for example): foo != 0 && 3 / foo < 10).

As I understand the code (https://github.com/TomFrost/Jexl/blob/master/lib/evaluator/handlers.js#L27), this is currently not implemented, causing every "branch" of an expression being evaluated, even if it's not needed to calculate a result.

Could we consider implementing this change?

transform names beginning with 'in'

Adding a transform with a name beginning with 'in' causes the Lexer not to recognize the transform name as a token. For example,


jexl.addTransform('indexOf', function(val, i) {
  return val.indexOf(i);
});

jexl.eval("[1,2,3] | indexOf(1)", {}).then(function(res) {
  console.log(res); // expecting 0
});

fails because indexOf is tokenized as 'in' and 'dexOf'.

cannot match variable starting with $

var jexl = require('jexl')

var context = {
    $a: 1
}
jexl.eval('$a', context)

$a is a legal variable name in Javascript.

I found it is the problem of delimiter regex.

Could you fix it? Thx

Eval returns length of an empty string as undefined

var jexl = require("jexl");
jexl.eval("name.length", {name: ""}).then(console.log);
// undefined
jexl.eval("name.length == 0", {name: ""}).then(console.log);
// false
jexl.eval("name.length == undefined", {name: ""}).then(console.log);
// true

Support for Function Calls

Does this library support function calls?

For example, given the following context
{
number1: 3,
number2: 5,
number3: 4
}

Can i use something like the following?
min(number1, number2, number3)

Allow evaluating from precompiled AST object

Problem

Currently, Jexl supports the ability to precompile by saving the Expression object in memory. Example:

const jexl = require('jexl')

const compiled = jexl.compile('name')
compiled.evalSync({ name: 'John' })

I am using AWS Lambda, so the processes are short lived. I precompile my Jexl code and store the private expression._ast object for later evaluation. My current hack is basically:

const jexl = require('jexl');

const ast = ...
const expression = '...'

const jexlExpression = new Expression(
  jexl._getLang(),
  expression
);

jexlExpression._ast = ast;
jexlExpression.evalSync({ ... });

Proposal

We should consider exposing a public API for evaluating from a provided AST object.

Here is a possible API:

Fetching AST

We could change Expression.prototype._getAst to Expression.prototype.getAst:

const ast = jexl.compile(...).getAst();

Evaluating AST

jexl.evalSyncFromAst(expression, ast, context)
jexl.evalFromAst(expression, ast, context)

Thoughts @TomFrost? I would be happy to work on this change if we can come up with an API.

Strange Behavior with number as string as the needle

Terrible title aside, here's some weirdness. Is it a bug? Hard to say, but it is odd.

'use strict';
var jexl = require('jexl'),
    Bluebird = require('bluebird'),
    haystack = {
        foo: 'bar',
        something: 'else'
    };


function jexEval(needle) {
    return jexl.eval(needle, haystack)
        .catch((err) => {
            return `Error: ${err.message}`;
        });
}

Bluebird.props({
    '51.5': jexEval('51.5'),
    51: jexEval(51),
    'foo': jexEval('foo'),
    'hello': jexEval('hello')
})
.then((res) => {
    console.log(res);
//{ '51': 'Error: str.split is not a function',
//  '51.5': 51.5,
//  foo: 'bar',
//  hello: undefined }
});

Tested with jexl 1.1.2/3 on node 4.4.3

Provide option to use synchronized version of jexl

It would be better if I can choose either async or sync evaluation of jexl by option/ config. For example, I want to evaluate a json object with special syntax "${...}", eg. ${"2016-4-15"|toDate} where toDate is custom transform I added to jexl. I would grab the context between ${ and } by regular expression. The final object derived would be a Date object.
I would like to do this on React Component where the above would be one of the props, as the React Component lifecycle function is not async, I would like to do it and convert the props to state and use the state for the first time rendering.
I think it is just example case, there are many cases we may want to use Sync instead of Async.
Maybe can follow fs, jexl.evalSync and jexl.eval?

Collection filtering on identifiers gives empty result

Hi,

thank you for this awesome library :)

I just noticed that:

const context = {
  items: [
    {id: 123, title: 'Hello world'},
    {id: 456, title: 'Good bye world'}
  ],
  my: {
    itemId: 123
  }
}

// The following will resolve with an empty array:
jexl.eval('items[.id == my.itemId]', context).then(console.log)

// While this will give the correct subset:
jexl.eval('items[.id == (my.itemId)]', context).then(console.log)

interceptor

Hi,
can we have feature to intercept the expression before evaluating it, so we can do custom operations on it?

thanks in advanced.

Array filter with number keys fails with (literal) unexpected

The expression "value[.9supPjNuM4LbbXHXC != 't'].firstName" fails with

Error: Token 9 (literal) unexpected in expression: value[.9

Therefore I cannot filter my array of objects which each have a key '9supPjNuM4LbbXHXC', ie:

{
  9supPjNuM4LbbXHXC: 'abc123',
  firstName: 'bob'
}

Filtering primitive arrays

How does one filter primitive arrays. ie,

var array = ['a','b','c']
jexl.eval("array[? == 'b']|tranform")

How do you reference the element rather than a property of the element?

Doesn't work on Explorer 11

Importing jexl in react app
"import jexl from 'jexl';"
returns error for Explorer 11

"jexl": "^2.1.1",
"react": "^16.8.6",

Capture

Does not handle evaluating when data properties actually include dots

First, I'd like to say how much I appreciate all of the hard work on this module. It's so awesome!

I ran into a case where the data I am passing to be evaluated actually includes dots in the properties.

Example:

const jexl = require('jexl');

const evaluated = jexl.evalSync('my.name', {
  'my.name': 'Austin'
});

console.log(evaluated); // undefined

Is this something we should try to support? I haven't dug into the code much yet, but I could help work on making this change if it is something that we think is possible without too much effort.

How to use this with a loader that uses AMD API?

I found that including jexl.min.js after including Dojo, jexl overwrites the Dojo require with is own, rendering all subsequent require statements of Dojo inoperable.

I also tried to use as a module, but the loader does not recognize it as a module. Any suggestions?

null vs undefined handling

I'm looking at using Jexl for templates, and I want to distingush between two cases:

  1. The expression referred to an attribute that was not there
  2. The expression attempted to access an attribute on a null value
  1. indicates that the expression was incorrectly given, and I'd like to report an error identifying the issue.
  2. indicates that the piece of data is legitmately missing and not an issue with the template

The problem is that I get back undefined in either case, so I can't tell them apart.

What I'd like is: 1) returns undefined but 2) returns null. All that is neccessary for that to happen is to change the Identifier handler to return null when the from evaluates to null instead of returning undefined.

But, I get that perhaps not everyone wants the same semantics as me. But perhaps, given the customizability you already have in your language, you'd be amenable to a PR adding the option to customize the logic on this point?

Otherwise, it looks like I could get the functionality I want by a convoluted process of compiling the expression, rewriting the AST on the returned expression object, then evaluating that. But... that's really ugly. Or I could fork the whole project and modify that one piece, but I'd rather not if I don't have to.

Version 2.0

Dear @TomFrost and community,

i recently stumbled upon this library, as i was searching for a javascript'y solution for JEXL known from Java (e.g., http://commons.apache.org/proper/commons-jexl/ )

I must admit - this package looks awesome and i really like the approach. However, before using this package in my own project, i would like to ask some questions, which may be crucial for the application i am building:

  1. How far away is version 2.0 of this package? I found respective branch (and PR) on GitHub. Yet, the master and 2.0 branch are last updated in September 2017. I also found a comment (from October '17), where you stated that you are approx 95% finish with the 2.0..

  2. Is there support for "common" functions, like min / max / avg / sum / count / ... in rules?

  3. Is it possible to "extend" the functionality to add own "functions" (cf. Question 2)? For example, i want to have a checkIfXpplies(a, b, c, d) function that may contain a more sophisticated logic.

Thank you very much for your time and effort on this project. I know, that providing open-source software (and its maintanance) may be quite frustrating - but keep the good work up! This package is really awesome and i can really see its use in various applications ๐Ÿ‘

Cheers from Germany

No unary plus?

Why is there only one unary operator (!)? Unary plus is a common way to convert to number (like +[] === 0).

How can I use a unary operator?

I did not find examples of its use in the readme. Is it possible to build "not in" conditions?

const context = {
  peoples: [
    { first: 'Lana', last: 'Kane', hobby: ['guitar', 'flowers'] },
    { first: 'Cyril', last: 'Figgis', hobby: ['computer', 'cats'] },
    { first: 'Pam', last: 'Poovey', hobby: ['poetry', 'dogs'] }
  ]
}

jexl.eval('!(peoples["dogs" in .hobby])', context);
/* result:
{ first: 'Lana', last: 'Kane', hobby: ['guitar', 'flowers'] },
{ first: 'Cyril', last: 'Figgis', hobby: ['computer', 'cats'] },
*/

Add an ES5 build

Hi @TomFrost

I've been running into some issues with running Jexl 2.0 in older browsers that don't support ES6 yet (specifically IE 11). Many libraries are publishing transpiled (and often also bundled) code to npm. Would you be interested in providing something like that for Jexl as well?

I might be able to open a PR for this, but I figured I'd ask first if you're interested at all ๐Ÿ˜‰

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.