Convert to DSL for Ransack
JSONをRansack用DSLに変換するライブラリ
by bc_rikko
HTML
<pre id="app"></pre>
<!--
JSONをRansack用DSLに変換するライブラリ
## 変換元
[
{ id: 1, name: 'rikko' },
{ age: [20, 30, 40] }
]
## 変換後の構造
q: {
g: {
m: 'or',
'0' => {
id_eq: 1,
name_eq: 'rikko'
},
'1' => {
g: {
m: 'or',
'0' => { age_eq: 20 },
'1' => { age_eq: 30 },
'2' => { age_eq: 40 }
}
}
}
}
## 変換後のクエリ
{
q[g[m]]: 'or',
q[g[0][id_eq]]: 1,
q[g[0][name_eq]]: 'rikko'
q[g[1][g[m]]]: 'or',
q[g[1][g[0][age_eq]]]: 20,
q[g[1][g[1][age_eq]]]: 30,
q[g[1][g[2][age_eq]]]: 40,
}
※ _in を使わないのは、連想配列だと同じキーで異なる値を設定できないため
※ 本来は q[g[age_in]][]: 20, q[g[age_in]][]: 30, q[g[age_in]][]: 40と書く
-->
JavaScript
// include: lodash 2.2.1
const data = [
{ id: 1, name: 'rikko' },
{ age: [20, 30, 40], email: 'test@example.com' }
];
class Ransack {
constructor (data) {
this.data = data
}
/**
* 連想配列を配列に変換
* ex. [{ id: 1, name: 'rikko' }, { age: [20, 30, 40 ]} ]
* → [ [{key: 'id', val: 1}, {key: 'name', val: 'rikko'}], {key: 'age', val: [20,30,40]} ]
* @param target
* @return [[{key: *, val: *}, {key: *, val: *}], {key: *, val: *}]
**/
_toArray (target) {
const r = [];
target.forEach(a => {
const _r = [];
for (let b in a) {
_r.push({ key: b, val: a[b] });
}
r.push(_r);
});
return r;
}
/**
* 配列をDSLに変換
* @param target
* @return DSL
**/
_format (target, isRecursion) {
const r = {};
target.forEach((a, i) => {
if (isRecursion) {
r['[m]'] = 'or';
} else {
r['g[m]'] = 'or';
}
_.forEach(a, (b, j) => {
if (b.val && _.isArray(b.val)) {
const _t = [];
const data = b.val.map(c => {
return { key: b.key, val: c};
});
_t.push(data);
const _r = this._format(_t, true);
_.forEach(_r, (v, k) => {
r[`g[${i}]${k}`] = v;
});
} else {
const key = isRecursion ? `[g[${j}][${b.key}_eq]]` : `g[${i}][${b.key}_eq]`;
r[key] = b.val;
}
});
});
return r;
}
/**
* 連想配列をRansackDSLに変換
* ※検索条件の配列指定は1つのみ(順序は関係ない) this._formatの実装問題
* ex. [{key: id, val: 10}] → {q[g[m]]: 'or', q[g[0][id_eq]]: 10}
* @param _toArray
* @return {q[g[m]]: 'or', q[g[0][*_eq]]: *}
**/
convertToDSL () {
if (_.size(this.data) === 0) { return {}; }
const a = this._toArray(this.data);
const f = this._format(a);
const r = {};
for (let a in f) {
r[`q[${a}]`] = f[a];
}
return r;
}
}
const app = document.getElementById('app');
const d = (new Ransack(data)).convertToDSL();
app.innerText =...