vadimdemedes / mongorito

🍹 MongoDB ODM for Node.js apps based on Redux
1.38k stars 90 forks source link

How to intercept & change set behavior? #180

Open zaaack opened 7 years ago

zaaack commented 7 years ago

I want to intercept get & set action to make the field value different in using & store, here are my code:

import { ActionTypes, Model } from 'mongorito'

// const defaults = (val, defaults) => typeof val === 'undefined' ? defaults : val

export function mapFields(mappings) {
    const mapKeys = Object.keys(mappings)
    return () => store => next => action => {
        // action GET
        if (action.type === ActionTypes.GET) {
            let { fields } = store.getState()
            const { key } = action
            const { get: getter } = key ? mappings[key] : {}
            // current key has getter and not null
            if (getter) {
                const val = fields[key]
                return getter(val)
                // get all fields
            } else if (!key) {
                fields = { ...fields }
                return mapKeys.reduce((fields, key) => {
                    const val = fields[key]
                    fields[key] = mappings[key].get(val)
                    return fields
                }, fields)
            }
        // action set
        } else if (action.type === ActionTypes.SET) {
            const { fields } = action
            // set each field key with setter
            for (let key in fields) {
                const { set: setter } = mappings[key] || {}
                if (setter) {
                    const val = fields[key]
                    fields[key] = setter(val)
                }
            }
        }

        return next(action)
    }
}

class Users extends Model {

}

db.register(Users)

const Gender = {
    MALE: 1,
    FEMALE: 2,
    UNKNOWN: 3,
}

const GenderReverse = {
    1: 'MALE',
    2: 'FEMALE',
    3: 'UNKNOWN',
}

Users.use(mapFields({
    gender: {
        get: g => GenderReverse[g],
        set: g => Gender[g],
    },
}))

Then I reallize SET action is not only used by outside, but also inside when query from the db (in the constructor), so I couldn't tell which action is triggered by user input or query from the db.

vadimdemedes commented 7 years ago

Could you reformat your code or post it elsewhere? It's hard to follow it, because the indentation is broken and there's no spacing.

zaaack commented 7 years ago

@vadimdemedes Sorry, added some annotations and a demo, hope it would be more clear.

vadimdemedes commented 7 years ago

Then I reallize SET action is not only used by outside, but also inside when query from the db (in the constructor), so I couldn't tell which action is triggered by user input or query from the db.

This is a good point, perhaps we'll need to introduce a specific action to dispatch on model initialization, like SEED or INITIAL_SET. @zaaack Any ideas for a better name?

zaaack commented 7 years ago

@vadimdemedes I find out the feature I need is actual the virtual field feature in mongoose, and if I using another field name that doesn't exist in database, the problem solved.

zaaack commented 7 years ago

But to solve the problem that there isn't a way to intercept query and persistence, I think we should let the models query from database using another action to initial, like INTERNAL_SET, not just in constructor, since this could still be called by user.