Skip to content

Commit a4f37bc

Browse files
committed
added getter/setter for record id to workaround string coersion, override TableSchemaModel:_getPrimaryKey to return un-coerced id, added optimitized filter() and find() to RecordCollection
1 parent b873405 commit a4f37bc

File tree

9 files changed

+198
-113
lines changed

9 files changed

+198
-113
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "redux-db-extras",
3-
"version": "1.0.33",
3+
"version": "1.0.34",
44
"description": "Collections, selectors, and top-level record fields for redux-db",
55
"main": "src/index.js",
66
"scripts": {

src/ModelFactory.js

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
import { DefaultModelFactory } from 'redux-db'
2-
import TableModel from './TableModel'
32
import RecordModel from './RecordModel'
3+
import TableModel from './TableModel'
4+
import TableSchemaModel from './TableSchemaModel'
45

56
export default class ModelFactory extends DefaultModelFactory {
67
getRecordBaseClass(schema) {
78
return RecordModel
89
}
910

10-
newTableModel(session, state, schema) {
11-
return new TableModel(session, state, schema)
11+
newTableSchema(db, name, schema) {
12+
return new TableSchemaModel(db, name, schema)
1213
}
1314

14-
newRecordValueField(schema, record) {
15-
const field = super.newRecordField(schema, record)
16-
return field && field.value
15+
newTableModel(session, state, schema) {
16+
return new TableModel(session, state, schema)
1717
}
1818

19-
_createRecordModel(schema) {
19+
createRecordModel(schema) {
2020
if (this._recordClass[schema.name])
2121
return this._recordClass[schema.name]
2222
else {
@@ -38,22 +38,27 @@ export default class ModelFactory extends DefaultModelFactory {
3838
Object.defineProperty(ExtendedRecordModel.prototype, name, {
3939
get() {
4040
// TODO: Improve the instance cache mechanism. Invalidate when the field value changes..
41-
return cache ? (this.__fields[name] || (this.__fields[name] = factory(field, this))) : factory(field, this)
41+
return cache ? (this._fields[name] || (this._fields[name] = factory(field, this))) : factory(field, this)
4242
}
4343
})
4444
}
4545

46-
schema.fields.forEach(f => (f.isForeignKey || !f.isPrimaryKey) && defineAttributeProperty(f.propName, f, f.references ? this.newRecordField : this.newRecordValueField.bind(this)))
47-
schema.relations.forEach(f => f.relationName && defineRelationProperty(f.relationName, f, f.unique ? this.newRecordRelation.bind(this) : this.newRecordSet.bind(this), !f.unique))
46+
schema.fields.forEach(f => (f.isForeignKey || !f.isPrimaryKey) && defineAttributeProperty(f.propName, f, f.references ? this._newRecordField : this._newRecordValueField.bind(this)))
47+
schema.relations.forEach(f => f.relationName && defineRelationProperty(f.relationName, f, f.unique ? this._newRecordRelation.bind(this) : this._newRecordSet.bind(this), !f.unique))
4848

4949
return this._recordClass[schema.name] = ExtendedRecordModel
5050
}
5151
}
52+
53+
_newRecordValueField(schema, record) {
54+
const field = this._newRecordField(schema, record)
55+
return field && field.value
56+
}
5257
}
5358

5459
function createRecordModelClass(BaseClass) {
5560
return class ExtendedRecordModel extends BaseClass {
56-
__fields = {}
61+
_fields = {}
5762
}
5863
}
5964

src/RecordCollection.js

Lines changed: 66 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,35 @@
1+
import sharedMethods from './sharedMethods'
2+
import isEqual from 'lodash/isEqual'
3+
4+
const EMPTY_OBJECT = {}
5+
const EMPTY_ARRAY = []
6+
17
export default class RecordCollection {
28
constructor(table, key) {
39
this.table = table
10+
this.schema = table.schema
411
this.key = key
12+
this.state = (table.state.collections || {})[key] || {}
513
}
614

715
get ids() {
8-
return this._getProps().ids || []
16+
return this.state.ids || EMPTY_ARRAY
917
}
1018

1119
get length() {
1220
return this.ids.length
1321
}
1422

1523
get meta() {
16-
return this._getProps().meta || {}
24+
return this.state.meta || EMPTY_OBJECT
1725
}
1826

1927
exists() {
20-
return !!this._getProps().ids
28+
return !!this.state.ids
2129
}
2230

2331
all() {
24-
const { factory } = this.table.schema.db
25-
return this.ids.map(id => factory.newRecordModel(id, this.table))
32+
return this.ids.map(id => this.table.getOrDefault(id))
2633
}
2734

2835
value() {
@@ -33,54 +40,55 @@ export default class RecordCollection {
3340
return this.all().map(callback)
3441
}
3542

36-
filter(callback) {
37-
return this.all().filter(callback)
38-
}
39-
4043
set(ids, meta) {
41-
const props = { ids }
44+
if (ids == null) throw new Error('Invalid ids')
45+
this._setProp('ids', ids)
4246
if (arguments.length > 1) {
43-
props.meta = meta
47+
this.setMeta(meta)
4448
}
45-
return this._setProps(props)
49+
return this
4650
}
4751

4852
setMeta(meta) {
49-
return this._setProps({ meta })
53+
return this._setProp('meta', meta)
5054
}
5155

5256
updateMeta(meta) {
53-
return this.setMeta({ ...this.meta, ...meta })
57+
return this._setProp('meta', { ...this.state.meta, ...meta })
5458
}
5559

5660
insert(ids, index) {
61+
if (ids == null) throw new Error('Invalid ids')
5762
Array.isArray(ids) || (ids = [ids])
58-
const currentIds = this.ids.slice()
5963

60-
// Remove duplicate ids from the collection before insertion.
61-
if (currentIds.length) {
62-
ids.forEach((id, i) => {
63-
const idx = currentIds.indexOf(id)
64+
if (index == null) {
65+
const currentIds = this.ids.filter(id => ids.indexOf(id) === -1)
66+
return this.set(currentIds.concat(ids))
67+
} else {
68+
const currentIds = this.ids.slice()
6469

65-
if (idx > -1) {
66-
currentIds.splice(idx, 1)
70+
// Remove duplicate ids from the collection before insertion.
71+
if (currentIds.length) {
72+
ids.forEach(id => {
73+
const idx = currentIds.indexOf(id)
6774

68-
if (idx < index) {
69-
index--
75+
if (idx > -1) {
76+
currentIds.splice(idx, 1)
77+
78+
if (idx < index) {
79+
index--
80+
}
7081
}
71-
}
72-
})
73-
}
82+
})
83+
}
7484

75-
if (index == null) {
76-
return this.set(currentIds.concat(ids))
77-
} else {
7885
currentIds.splice(index, 0, ...ids)
7986
return this.set(currentIds)
8087
}
8188
}
8289

8390
insertUnique(ids, index) {
91+
if (ids == null) throw new Error('Invalid ids')
8492
Array.isArray(ids) || (ids = [ids])
8593
const currentIds = this.ids.slice()
8694

@@ -89,12 +97,16 @@ export default class RecordCollection {
8997
ids = ids.filter(id => currentIds.indexOf(id) === -1)
9098
}
9199

92-
if (index == null) {
93-
return this.set(currentIds.concat(ids))
94-
} else {
95-
currentIds.splice(index, 0, ...ids)
96-
return this.set(currentIds)
100+
if (ids.length) {
101+
if (index == null) {
102+
this.set(currentIds.concat(ids))
103+
} else {
104+
currentIds.splice(index, 0, ...ids)
105+
this.set(currentIds)
106+
}
97107
}
108+
109+
return this
98110
}
99111

100112
remove(ids) {
@@ -108,46 +120,49 @@ export default class RecordCollection {
108120
}
109121
})
110122

111-
return this.set(updatedIds)
123+
if (ids.length !== updatedIds.length) {
124+
this.set(updatedIds)
125+
}
126+
127+
return this
112128
}
113129

114130
clear() {
115131
return this.set([], {})
116132
}
117133

118134
delete() {
119-
const { state } = this.table
120-
let { collections } = state
135+
const tableState = this.table.state
136+
let { collections } = tableState
121137

122138
if (collections && collections[this.key]) {
123139
collections = { ...collections }
124140
delete collections[this.key]
125141

126-
this.table.state = { ...state, collections }
142+
this.state = {}
143+
this.table.state = { ...tableState, collections }
127144
this.table.dirty = true
128145
}
129146
}
130147

131-
_getProps() {
132-
return (this.table.state.collections || {})[this.key] || {}
133-
}
148+
_setProp(key, value) {
149+
if (isEqual(this.state[key], value)) return
150+
const tableState = this.table.state
134151

135-
_setProps(props) {
136-
const { state } = this.table
137-
const { collections = {} } = state
152+
this.state = { ...this.state, [key]: value }
138153

139154
this.table.state = {
140-
...state,
155+
...tableState,
141156
collections: {
142-
...collections,
143-
[this.key]: {
144-
...collections[this.key],
145-
...props
146-
}
157+
...tableState.collections,
158+
[this.key]: this.state
147159
}
148160
}
149161

150162
this.table.dirty = true
151-
return this
152163
}
153164
}
165+
166+
RecordCollection.prototype.find = sharedMethods.find
167+
RecordCollection.prototype.filter = sharedMethods.filter
168+

src/RecordModel.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
import { RecordModel as DefaultRecordModel } from 'redux-db'
22

33
export default class RecordModel extends DefaultRecordModel {
4+
get id() {
5+
if (this.table.schema._primaryKeyFields.length === 1) {
6+
return this.table.state.byId[this._id][this.table.schema._primaryKeyFields[0].propName]
7+
}
8+
return this._id
9+
}
10+
11+
set id(value) {
12+
this._id = value
13+
}
14+
415
equals(record) {
516
return record && record.value === this.value
617
}
718

819
idEquals(record) {
9-
return record && record.id === this.id
20+
return record && record._id === this._id
1021
}
1122
}

src/SelectorFactory.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ export default class SelectorFactory {
8484
)
8585
}
8686

87+
collectionIds(tableName, defaultKey = DEFAULT_COLLECTION_KEY) {
88+
return this.create(
89+
tableName,
90+
(state, key = defaultKey) => key,
91+
(table, key) => table.collection(key).ids
92+
)
93+
}
94+
8795
_resolveTableNames(tableNames) {
8896
if (Array.isArray(tableNames)) {
8997
return tableNames

0 commit comments

Comments
 (0)