UNPKG

6.06 kBJavaScriptView Raw
1var hide = require('./hideProperty')
2var async = require('./async')
3var extend = require('./extend')
4
5var Row = module.exports = function Row(data, table) {
6 var self = this
7 if (table && table.Row && typeof table.Row === 'function') {
8 var model = function () {}
9 var rowPrototype = extend({}, Row.prototype)
10 model.prototype = new table.Row(data, table)
11 extend(model.prototype, rowPrototype)
12 var newRow = new model(data, table)
13 newRow.initialize(data, table)
14 return newRow
15 }
16 if (!(self instanceof Row)) {
17 return new Row(data, table)
18 }
19 self.initialize(data, table)
20}
21
22
23Row.prototype.initialize = function (data, table) {
24 var self = this
25 data = data || {}
26
27 if (self._table || !table) return
28
29 hide(self, '_data')
30 hide(self, '_table')
31 hide(self, '_db')
32
33 // take a snapshot of the data so we only save() what is changed
34 self._data = self._data || {}
35 self._table = table
36 self._db = table.db
37
38 Object.getOwnPropertyNames(data).forEach(function (prop) {
39 var desc = Object.getOwnPropertyDescriptor(data, prop)
40 Object.defineProperty(self, prop, desc)
41 })
42}
43
44
45Row.prototype.dump = function() {
46 console.log(this._table.name + '(' + this.getPrimaryKey() + '):', this);
47}
48
49
50Row.prototype.getForeignRows = function(foreignRowsName, cb) {
51 var self = this
52 var db = self._table.db
53 var parts = foreignRowsName.split(':')
54 var fkName = parts[0]
55 var table = parts[1]
56 var fk = db[table].fk[fkName]
57 var where = {}
58 fk.columns.forEach(function (col, i) {
59 where[col] = self[fk.foreignColumns[i]]
60 })
61 db[table].find({
62 where: where
63 }, cb)
64}
65
66
67Row.prototype.getForeignRowsName = function(fkName) {
68 var self = this
69 var db = self._table.db
70 if (self._table.fk[fkName]) {
71 return false
72 }
73 var colExists = !!self._table.columns.filter(function (col) {
74 return col.name === fkName
75 }).length
76 if (colExists) {
77 return false
78 }
79 var foreignRowsName
80 var parts = fkName.split(':')
81 var table = parts[0]
82 if (parts.length > 1) {
83 foreignRowsName = parts[0]
84 table = parts[1]
85 }
86 if (!db[table]) {
87 return false
88 }
89 var fk = db[table].fk
90 if (foreignRowsName) {
91 if (fk[foreignRowsName].foreignTable === self._table.name) {
92 return fkName
93 }
94 throw new Error('The foreign key specified is not linked to this row.')
95 }
96 var found = 0
97 Object.keys(fk).forEach(function (name) {
98 if (fk[name].foreignTable === self._table.name) {
99 found++
100 foreignRowsName = name + ':' + table
101 }
102 })
103 if (found > 1) {
104 throw new Error("hydrate('" + table + "') is ambiguous. Try something like '" + fkName + "'")
105 }
106 return foreignRowsName || false
107}
108
109
110Row.prototype.hydrate = function(fkNames, cb) {
111 var self = this
112 var db = self._table.db
113 cb = cb || db._promiseResolver()
114 if (!isArray(fkNames)) {
115 fkNames = [fkNames]
116 }
117 async.each(fkNames, function (fkName, done) {
118 var foreignRowsName = self.getForeignRowsName(fkName)
119 if (foreignRowsName) {
120 self.getForeignRows(foreignRowsName, function (err, rows) {
121 if (err) return done(err)
122 self[fkName] = rows
123 done(null)
124 })
125 return
126 }
127 if (!self._table.fk || !self._table.fk[fkName]) {
128 return cb(new Error(self._table.name + '.' + fkName + ' is not hydratable.'))
129 }
130 var fk = self._table.fk[fkName]
131 var property = fk.constraintName
132 var fkTable = fk.foreignTable
133 var fkPk = {}
134 fk.columns.forEach(function(column, i) {
135 fkPk[fk.foreignColumns[i]] = self[column]
136 })
137 var invalidPk = false
138 Object.keys(fkPk).forEach(function(prop) {
139 var val = fkPk[prop]
140 if (val === null || typeof val === 'undefined') {
141 invalidPk = true
142 }
143 })
144 if (invalidPk) return done(null)
145 self._table.db[fkTable].get(fkPk, function(err, obj) {
146 if (err && !err.notFound) return done(err)
147 self[fkName] = obj
148 done(null)
149 })
150 }, cb)
151 return cb.promise ? cb.promise : this
152}
153
154
155Row.prototype.getPrimaryKey = function() {
156 var self = this
157 var pk = {}
158 if (!self._table.primaryKey) return pk
159 self._table.primaryKey.forEach(function(field) {
160 pk[field] = self[field]
161 })
162 return pk
163}
164
165
166Row.prototype.getCacheKey = function() {
167 var self = this
168 var pk = self.getPrimaryKey()
169 var cacheKey = ''
170 Object.keys(pk).forEach(function(k) {
171 var val = pk[k]
172 if (cacheKey) cacheKey = cacheKey + ','
173 cacheKey = cacheKey + val
174 })
175 cacheKey = self._table.name + ':' + cacheKey
176 return cacheKey
177}
178
179
180Row.prototype.save = require('./save')
181
182
183Row.prototype.set = function(data) {
184 var self = this
185 Object.keys(data).forEach(function(field) {
186 self._data[field] = self[field]
187 self[field] = data[field]
188 })
189}
190
191
192Row.prototype.update = function(data, cb) {
193 var self = this
194 var db = self._table.db
195 cb = cb || db._promiseResolver()
196 Object.keys(data).forEach(function(key) {
197 self[key] = data[key]
198 })
199 self.save(cb)
200 return cb.promise ? cb.promise : this
201}
202
203Row.prototype.delete = function (opts, cb) {
204 var self = this
205 var table = self._table
206 var db = table.db
207 var iq = db._platform.identifierQuoteChar
208 opts = opts || {}
209 if (typeof opts === 'function') {
210 cb = opts
211 opts = {}
212 }
213 cb = cb || db._promiseResolver()
214 var pk = self.getPrimaryKey()
215 var where = table.getPrimaryKeyWhereClause(pk)
216 var cacheKey = self.getCacheKey()
217 var sql = [
218 'DELETE FROM ' + iq + table.name + iq,
219 'WHERE ' + where
220 ]
221 db.execute(sql, function (err) {
222 if (err) return cb(err)
223 // invalidate memoization
224 self._table.invalidateMemo(pk)
225 // delete self
226 Object.keys(self).forEach(function (key) {
227 delete self[key]
228 })
229 // invalidate the cache
230 var cache = db._opts.cache
231 if (!cache || typeof cache.set !== 'function') {
232 return cb(null)
233 }
234 cache.set(cacheKey, null, function(err) {
235 if (err) return cb(err)
236 cb(null)
237 })
238 })
239 return cb.promise ? cb.promise : this
240}
241
242function isArray(obj) {
243 return Object.prototype.toString.call(obj) === '[object Array]'
244}