API Docs for: 0.2.1
Show:

File: addon/utils/attr.js

/**
  @module ember-jsonapi-resources
  @submodule utils
  @main attr
**/

import Ember from 'ember';
import { isBlank, isDasherized, isType } from 'ember-jsonapi-resources/utils/is';

/**
  Utility helper to setup a computed property for a resource attribute, imported and
  exported with the resource submodule.

  An `attr` of the resource is a computed property to the actual attribute in an
  `attributes` hash on the `resource` (model) instance. Using `attr()` supports
  any type, and an optional `type` (String) argument can be used to enforce
  setting and getting with a specific type. `'string'`, `'number'`, `'boolean'`,
  `'date'`, `'object'`, and `'array'` are all valid types for attributes.

  Use `attr()`, with optional type argument, to compose your model attributes, e.g:

  ```js
  import Ember from 'ember';
  import Resource from 'ember-jsonapi-resources/models/resource';
  import { attr, toOne, toMany } from 'ember-jsonapi-resources/models/resource';

  export default Resource.extend({
    type: 'articles',
    service: Ember.inject.service('articles'),

    title: attr('string'),
    published: attr('date'),
    tags: attr('array'),
    footnotes: attr('object'),
    revisions: attr()
    version: attr('number'),
    "is-approved": attr('boolean')
  });
  ```

  @method attr
  @for Resource
  @final
  @param {String} [type] an optional param for the type of property, i.e. `string`,
    `number`, `boolean`, `date`, `object`, or `array`
  @param {Boolean} [mutable=true] optional param, defaults to `true` if not passed
  @return {Object} computed property
*/
export default function attr(type = 'any', mutable = true) {
  const _mutable = mutable;
  if (type !== 'any' && !isBlank(type)) {
    assertValidTypeOption(type);
  }
  return Ember.computed('attributes', {
    get: function (key) {
      assertDasherizedAttr(key);
      let value = this.get('attributes.' + key);
      if (!isBlank(value)) {
        assertType.call(this, key, value);
      }
      return value;
    },

    set: function (key, value) {
      const lastValue = this.get('attributes.' + key);
      if (!_mutable) {
        return immutableValue(key, value, lastValue);
      }
      if (value === lastValue) { return value; }
      assertType.call(this, key, value);
      this.set('attributes.' + key, value);
      if (!this.get('isNew')) {
        this._attributes[key] = this._attributes[key] || {};
        this._attributes[key].changed = value;
        this._attributes[key].previous = lastValue;
        const service = this.get('service');
        if (service) {
          service.trigger('attributeChanged', this);
        }
      }
      return this.get('attributes.' + key);
    }
  }).meta({type: type, mutable: mutable});
}

function assertValidTypeOption(type) {
  if (type === 'any') { return; }
  let allowed = 'string number boolean date object array';
  let msg = 'Allowed types are: ' + allowed + ' however ' + type + ' was given instead.';
  Ember.assert(msg, allowed.split(' ').indexOf(type) > -1);
}

function assertDasherizedAttr(name) {
  try {
    let attrName = Ember.String.dasherize(name);
    let msg = "Attributes are recommended to use dasherized names, e.g `'"+ attrName +"': attr()`";
    msg += ", instead of `"+ name +": attr()`";
    Ember.assert(msg, isDasherized(name));
  } catch(e) {
    Ember.Logger.warn(e.message);
  }
}

function assertType(key, value) {
  let meta = this.constructor.metaForProperty(key);
  if (meta && meta.type && meta.type !== 'any') {
    let msg = this.toString() + '#' + key + ' is expected to be a ' + meta.type;
    Ember.assert(msg, isType(meta.type, value));
  }
}

function immutableValue(key, value, lastValue) {
  let msg = [
    this.toString(), '#', key, ' is not mutable set was called with ',
    '`', value, '`', ' but is previous set to `', lastValue, '`'
  ];
  Ember.Logger.warn(msg.join(''));
  return lastValue;
}