UNPKG

memory-orm

Version:
302 lines (301 loc) 8.12 kB
'use strict' Object.defineProperty(exports, '__esModule', { value: true }) exports.Rule = void 0 const tslib_1 = require('tslib') const get_1 = tslib_1.__importDefault(require('lodash/get')) const uniq_1 = tslib_1.__importDefault(require('lodash/uniq')) const property_1 = tslib_1.__importDefault(require('lodash/property')) const snakeCase_1 = tslib_1.__importDefault(require('lodash/snakeCase')) const Mem = tslib_1.__importStar(require('./userdata')) const model_1 = require('./model') const list_1 = require('./list') const set_1 = require('./set') const map_1 = require('./map') const query_1 = require('./query') const finder_1 = require('./finder') const mem_1 = require('./mem') function rename(base) { base = snakeCase_1.default(base).replace(/s$/, '') const name = Mem.Name[base] if (name) { return name } const list = `${base}s` const o = (Mem.Name[list] = Mem.Name[base] = mem_1.PureObject()) o.base = base o.list = list o.id = `${base}_id` o.ids = `${base}_ids` o.relations = [] o.deploys = [] o.depends = [] return o } function method({ prototype }, key, o) { Object.defineProperty(prototype, key, o) } class Rule { constructor( base, { model = class model extends model_1.Model {}, list = class list extends list_1.List {}, set = class set extends set_1.Set {}, map = class map extends map_1.Map {}, scope, scope_without_cache, schema, deploy, } = {} ) { this.$name = rename(base) this.state = mem_1.State.base(this.$name.list) this.all = query_1.Query.build(this.state) this.all.$sort['_reduce.list'] = {} this.all._cache = {} this.all._finder = new finder_1.Finder() this.depend_on(this.$name.list) this.model = model this.list = list this.set = set this.map = map if (scope_without_cache) { this.scope_without_cache(scope_without_cache) } if (scope) { this.scope(scope) } if (deploy) { this.deploy(deploy) } if (schema) { this.schema(schema) } } schema(cb) { cb.call(this) this.model.$name = this.list.$name = this.set.$name = this.map.$name = this.$name this.all._finder.join(this) Mem.Set[this.$name.base] = new this.set(this) Mem.Query[this.$name.list] = this.all Mem.Finder[this.$name.list] = this.all._finder return this } key_by(keys) { const get = (() => { if (undefined === keys) { return function () { return this._id } } if (keys instanceof Function) { return keys } if (keys instanceof String) { return property_1.default(keys) } if (keys instanceof Array) { return property_1.default(keys) } throw new Error(`unimplemented ${keys}`) })() method(this.model, 'id', { enumerable: true, get, }) } deploy(cb) { this.$name.deploys.push(cb) } depend_on(parent) { Mem.Name[parent].depends.push(parent) } scope_without_cache(cb) { const cmd = cb(this.all) for (const key in cmd) { this.all[key] = cmd[key] } } scope(cb) { const cmd = cb(this.all) for (const key in cmd) { this.use_cache(key, cmd[key]) } } property(type, o) { Object.defineProperties(this[type].prototype, o) } default_scope(scope) { this.all._copy(scope(this.all)) const base = mem_1.State.base(this.$name.list) base.$sort = this.all.$sort } shuffle() { this.default_scope((all) => all.shuffle()) } sort(...sort) { this.default_scope((all) => all.sort(...sort)) } order(keys, order) { this.default_scope((all) => all.order(keys, order)) } relation_to_one(key, target, ik, else_id) { this.$name.relations.push(key) method(this.model, key, { enumerable: true, get() { const id = get_1.default(this, ik) return Mem.Query[target].find(id, else_id) }, }) } relation_to_many(key, target, ik, cmd, qk) { const { all } = this this.use_cache(key, (id) => Mem.Query[target].distinct(false)[cmd]({ [qk]: id })) this.$name.relations.push(key) method(this.model, key, { enumerable: true, get() { return all[key](this[ik]) }, }) } relation_tree(key, ik) { const { all } = this this.use_cache(key, (id, n) => { if (n) { const q = all.where({ [ik]: id }) return all[key](q.ids, n - 1) } else { return all.where({ id }) } }) this.$name.relations.push(key) method(this.model, key, { enumerable: true, value(n) { return all[key]([this.id], n) }, }) } relation_graph(key, ik) { const { all } = this this.use_cache(key, (id, n) => { const q = all.where({ id }) if (n) { const ids = [] for (const a of q.pluck(ik)) { if (a != null) { for (let k of a) { if (k != null) { ids.push(k) } } } } return all[key](uniq_1.default(ids), n - 1) } else { return q } }) this.$name.relations.push(key) method(this.model, key, { enumerable: true, value(n) { return all[key]([this.id], n) }, }) } use_cache(key, val) { if (val instanceof Function) { this.all[key] = (...args) => { const name = `${key}:${JSON.stringify(args)}` return this.all._cache[name] || (this.all._cache[name] = val(...args)) } } else { this.all[key] = val } } path(keys, option = { key: 'id' }) { const { key } = option const { base, list } = this.$name let tail_key = keys[keys.length - 1] if ('*' === tail_key) { this.belongs_to(base) this.has_many(list) keys.pop() tail_key = keys[keys.length - 1] } for (const key of keys) { this.belongs_to(key) } this.deploy(({ o, reduce }) => { if ('string' !== typeof o[key]) { throw new Error(`id [${o[key]}] must be string.`) } const subids = o[key].split('-') o[`${key}x`] = subids[subids.length - 1] for (let idx = 0; idx < keys.length; idx++) { const sub_key = keys[idx] o[`${sub_key}_id`] = subids.slice(0, idx + 1).join('-') } // '*' support. if (base && keys.length + 1 < subids.length) { o[`${base}_id`] = subids.slice(0, -1).join('-') } reduce(key, { navi: subids }) }) const { all } = this const pk = `${tail_key}_id` method(this.model, 'siblings', { get() { return all.where({ [pk]: this[pk] }) }, }) } belongs_to(to, option = {}) { const name = rename(to) const { key = name.id, target = name.list, miss } = option this.relation_to_one(name.base, target, key, miss) } habtm(to, option = {}) { const name = rename(to) if (option.reverse) { const { key = this.$name.ids, target = to } = option this.relation_to_many(name.list, target, 'id', 'in', key) } else { const { key = name.ids, target = name.list } = option this.relation_to_many(name.list, target, key, 'where', 'id') } } has_many(to, option = {}) { const name = rename(to) const { key = this.$name.id, target = name.list } = option this.relation_to_many(name.list, target, 'id', 'where', key) } tree(option = {}) { const fk = this.$name.id this.relation_tree('nodes', fk) this.belongs_to(this.$name.base, option) Object.defineProperties(this.all, { leaf: { get() { const not_leaf = uniq_1.default(this.pluck(fk)) return this.where((o) => !not_leaf.includes(o.id)) }, }, }) } graph(option = {}) { const { directed, cost } = option const ik = this.$name.ids this.relation_to_many(this.$name.list, this.$name.list, ik, 'where', 'id') this.relation_graph('path', ik) if (!directed) { return true // todo } return false } } exports.Rule = Rule //# sourceMappingURL=rule.js.map