z-pattern-matching / z

Pattern Matching for Javascript
https://z-pattern-matching.github.io/
Apache License 2.0
1.72k stars 50 forks source link

Does not work if match value comes from a variable #37

Closed francisbrito closed 6 years ago

francisbrito commented 6 years ago

e.g:

#!/usr/bin/env node
const { matches } = require('z')

const FILTERS = {
  CATEGORY: 'CATEGORY',
  PRICE: 'PRICE',
  COLOR: 'COLOR',
  ONLY_NEW: 'ONLY_NEW',
  ONLY_IN_DISCOUNT: 'ONLY_IN_DISCOUNT'
}

const _30DaysAgo = Date.now() - 1000 * 60 * 60 * 24 * 30
const convertOptionsToQuery = filterOptions => matches(filterOptions.by)(
  (b = FILTERS.CATEGORY) => ({ category: filterOptions.value }),
  (b = FILTERS.COLOR) => ({ colors: filterOptions.value }),
  (b = FILTERS.PRICE) => ({ price: { $gte: filterOptions.value[0], $lte: filterOptions.value[1] } }),
  (b = FILTERS.ONLY_NEW) => ({ createdAt: { $gte: new Date(_30DaysAgo) } }),
  (b = FILTERS.ONLY_IN_DISCOUNT) => ({ isInDiscount: true }),
  (b) => ({})
)

convertOptionsToQuery({ by: 'CATEGORY', value: 'shirts' })

// ReferenceError: FILTERS is not defined

Strangely enough, it only happens if the code is ran as a file, if it is ran in the REPL it works.

This error seems to be related to js-function-reflector.

leonardiwagner commented 6 years ago

@francisbrito This code won't work because FILTERS is not present in z matches scope. That's why we can only make a pattern with explicit values. It's actually strange how it works on REPL, I'm going to spend some time to check this.

If someone has a idea how could inside matches read variables outside its scope, it would be a great feature! Honestly I don't know any solution but create an argument to to pass the FILTERS variable explicitly into z scope, but I think it would be to harsh to be usable.

leonardiwagner commented 6 years ago

Hello @francisbrito, this feature is now available on z@1.0.4 , now you can call z with apply or call and then set the context, examples:

    const FILTERS = {
      CATEGORY: 'CATEGORY',
      PRICE: 'PRICE',
      COLOR: 'COLOR',
      ONLY_NEW: 'ONLY_NEW',
      ONLY_IN_DISCOUNT: 'ONLY_IN_DISCOUNT'
    }

    const result = matches(FILTERS.COLOR).call(this,
      (x = FILTERS.CATEGORY) => false,
      (x = FILTERS.COLOR) => true,
      (x = FILTERS.ONLY_NEW) => false
    )

If the variable to match isn't on this, you can also set manually , eg.:

    const FILTERS = {
      CATEGORY: 'CATEGORY',
      PRICE: 'PRICE',
    }

    const SORT = {
      ASC: 'ASC',
      DESC: 'DESC',
    }

    const result = matches('something').call({ FILTERS, SORT },
      (x = FILTERS.CATEGORY) => false,
      (x = SORT.ASC) => false
    )

This was possible due a modification on js-function-reflector library

francisbrito commented 6 years ago

Glad hearing so. Thank you very much!