Skip to content

Instantly share code, notes, and snippets.

@jmdobry jmdobry/Resource.js
Last active Nov 5, 2015

Embed
What would you like to do?
js-data v3 decorators
/**
* Usage:
*
* @belongsTo(User, {
* localKey: 'myUserId'
* })
* class Post extends JSData.Resource {...}
*
* @belongsTo(User)
* @belongsTo(Post, {
* localField: '_post'
* })
* class Comment extends JSData.Resource {...}
*/
function belongsTo(Resource, options = {}) {
return function (target) {
let localField = options.localField || Resource.name.toLowerCase()
let localKey = options.localKey || Resource.name.toLowerCase() + '_id'
let descriptor = {
get() {
return Resource.get(this[localKey])
},
set(parent) {
this[localKey] = parent[Resource.idAttribute]
},
enumerable: 'enumerable' in options ? options.enumerable : false
}
if (options.get) {
let originalGet = descriptor.get
descriptor.get = function () {
return options.get(target, Resource, this, (...args) => orig.apply(this, args))
}
}
if (options.set) {
let originalSet = descriptor.set
descriptor.set = function (value) {
return options.set(target, Resource, this, value, (...args) => orig.apply(this, args))
}
}
Object.defineProperty(target.prototype, localField, descriptor)
return target
};
}
/**
* For when a user is writing in ES5 or doesn't have decorator support.
*
* For "extend" implementation see https://gist.github.com/jmdobry/519a375009cd744b8348#file-resource-js
*/
var User = JSData.Resource.extend({
name: 'user',
schema: {
first: {},
last: {},
role: {
value: 'dev'
},
name: {
get() { return `${this.first} ${this.last}` },
set(value) {
let parts = value.split(' ')
this.first = parts[0]
this.last = parts[1]
return this
}
}
}
})
var Post = JSData.Resource.extend({
name: 'post'
})
Post.belongsTo(User, {
localKey: 'myUserId'
})
// AND/OR
var store = new JSData.DS()
var User = store.defineResource({...})
function basicIndex(target) {
target.$index = {}
target.$collection = []
}
/**
* Usage:
*
* @configure({
* idAttribute: '_id'
* })
* class User extends JSData.Resource {...}
*/
function configure(options = {}) {
return function (target) {
for (var key in options) {
if (options.hasOwnProperty(key)) {
// TODO: Make deep copy
target[key] = options[key]
}
}
// See See https://gist.github.com/jmdobry/519a375009cd744b8348#file-schema-js
schema({
[target.idAttribute]: {}
})(target)
};
}
@basicIndex
// Apply defaults
@configure({
idAttribute: 'id'
})
class Resource {
constructor(props) {
configure(props)(this)
}
// Static methods
static createInstance(props = {}) {
let Constructor = this
return props instanceof Constructor ? props : new Constructor(props)
},
static inject(props = {}) {
let Constructor = this
if (Array.isArray(props)) {
props = props.map(this.createInstance)
} else {
props = [this.createInstance(props)]
}
return props.map(function (instance) {
let id = instance[this.idAttribute]
if (!this.$index[id]) {
this.$collection.push(instance)
}
return this.$index[id] = instance
}, this)
}
static get(id) {
return this.$index[id]
}
/**
* Usage:
*
* Post.belongsTo(User, {
* localKey: 'myUserId'
* })
*
* Comment.belongsTo(User)
* Comment.belongsTo(Post, {
* localField: '_post'
* })
*/
static belongsTo(Resource, options) {
// See https://gist.github.com/jmdobry/519a375009cd744b8348#file-belongsto-js
return belongsTo(Resource, options)(this)
}
/**
* Usage:
*
* var User = JSData.Resource.extend({...}, {...})
*/
static extend(props, classProps) {
// TODO: should mimic behavior of "class User extends JSData.Resource {...}"
}
}
/**
* Usage:
*
* @schema({
* first: {},
* last: {},
* role: {
* value: 'dev'
* },
* // computed property
* name: {
* get() { return `${this.first} ${this.last}` },
* set(value) {
* let parts = value.split(' ')
* this.first = parts[0]
* this.last = parts[1]
* return this
* }
* }
* })
* class User extends JSData.Resource {...}
*
* let user = new User()
* user.role // "dev"
* user.name = 'John Anderson'
* user.first // "John"
* user.last // "Anderson"
* user.first = "Bill"
* user.name // "Bill Anderson"
*/
function schema(schema) {
return function (target) {
for (var key in schema) {
let descriptor = {
enumerable: 'enumerable' in schema[key] ? schema[key] : true,
writable: 'writable' in schema[key] ? schema[key] : false,
configurable: 'configurable' in schema[key] ? schema[key] : false
}
if (schema[key].value) {
descriptor.value = schema[key].value
} else {
if (schema[key].get) {
descriptor.get = schema[key].get
}
if (schema[key].set) {
descriptor.set = schema[key].set
}
}
Object.defineProperty(target.prototype, key, descriptor)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.