<repo_name>ursm/ember-decorators<file_sep>/tests/unit/inject-test.js
import Ember from 'ember';
import { moduleFor } from 'ember-qunit';
import { test } from 'qunit';

import { controller } from 'ember-decorators/controller';
import { service } from 'ember-decorators/service';

moduleFor('Inject Decorators', {
  integration: true
});

test('service decorator works without service name', function(assert) {
  const FooService = Ember.Object.extend();

  this.register('service:foo', FooService);

  this.register('class:baz', Ember.Object.extend({
    @service foo: null
  }));

  const baz = this.container.lookup('class:baz');

  assert.ok(baz.get('foo') instanceof FooService, 'service injected correctly');
});

test('service decorator works with service name', function(assert) {
  const FooService = Ember.Object.extend();

  this.register('service:foo', FooService);

  this.register('class:baz', Ember.Object.extend({
    @service('foo') fooService: null
  }));

  const baz = this.container.lookup('class:baz');

  assert.ok(baz.get('fooService') instanceof FooService, 'service injected correctly');
});

test('controller decorator works without controller name', function(assert) {
  const FooController = Ember.Controller.extend();

  this.register('controller:foo', FooController);

  this.register('class:baz', Ember.Controller.extend({
    @controller foo: null
  }));

  const baz = this.container.lookup('class:baz');

  assert.ok(baz.get('foo') instanceof FooController, 'service injected correctly');
});

test('controller decorator works with controller name', function(assert) {
  const FooController = Ember.Controller.extend();

  this.register('controller:foo', FooController);

  this.register('class:baz', Ember.Controller.extend({
    @controller('foo') foo: null
  }));

  const baz = this.container.lookup('class:baz');

  assert.ok(baz.get('foo') instanceof FooController, 'service injected correctly');
});
<file_sep>/addon/object/index.js
import Ember from 'ember';
import computedMacro from 'ember-macro-helpers/computed';

import extractValue from '../utils/extract-value';
import {
  decorator,
  decoratorWithParams
} from '../utils/decorator-wrappers';

import {
  decoratorWithRequiredParams
} from '../utils/decorator-macros';

/**
 * Decorator that turns the target function into an Action
 *
 * Adds an `actions` object to the target object and creates a passthrough
 * function that calls the original. This means the function still exists
 * on the original object, and can be used directly.
 *
 * Before:
 *
 * ```js
 * export default Ember.Component.extend({
 *   actions: {
 *     foo() {
 *       // do something
 *     }
 *   }
 * });
 * ```
 *
 * After:
 *
 * ```js
 * import { action } from 'ember-decorators/object';
 *
 * export default MyComponent extends Ember.Component {
 *   @action
 *   foo() {
 *     // do something
 *   }
 * }
 * ```
 *
 * @function
 */
export const action = decorator(function(target, key, desc) {
  const value = extractValue(desc);

  if (typeof value !== 'function') {
    throw new Error('The @action decorator must be applied to functions');
  }

  if (!target.hasOwnProperty('actions')) {
    let parentActions = target.actions;
    target.actions = parentActions ? Object.create(parentActions) : {};
  }

  target.actions[key] = value;

  return value;
});

/**
 * Decorator that turns a function into a computed property.
 *
 * In your application where you would normally have:
 *
 * ```javascript
 * foo: Ember.computed('someKey', 'otherKey', function() {
 *   var someKey = this.get('someKey');
 *   var otherKey = this.get('otherKey');
 *
 *   // Do Stuff
 * })
 * ```
 *
 * You replace with this:
 *
 * ```javascript
 * import computed from 'ember-decorators/object';
 *
 * // ..... <snip> .....
 * @computed('someKey', 'otherKey')
 * foo(someKey, otherKey) {
 *   // Do Stuff
 * }
 * ```
 *
 * #### Without Dependent Keys
 *
 * ```javascript
 * foo: Ember.computed(function() {
 *   // Do Stuff
 * })
 * ```
 *
 * You replace with this:
 *
 * ```javascript
 * import computed from 'ember-decorators/object';
 *
 * // ..... <snip> .....
 * @computed
 * foo() {
 *   // Do Stuff
 * }
 * ```
 *
 * #### "Real World"
 *
 * ```javascript
 * import Ember from 'ember';
 * import computed from 'ember-decorators/object';
 *
 * export default Ember.Component.extend({
 *   @computed('first', 'last')
 *   name(first, last) {
 *     return `${first} ${last}`;
 *   }
 * });
 * ```
 *
 *
 * #### "Real World get/set syntax"
 *
 * ```javascript
 * import Ember from 'ember';
 * import computed from 'ember-decorators/object';
 *
 * export default Ember.Component.extend({
 *   @computed('first', 'last')
 *   name: {
 *     get(first, last) {
 *       return `${first} ${last}`;
 *     },
 *
 *     set(value, first, last) {
 *       // ...
 *     }
 *   }
 * });
 * ```
 *
 * @function
 * @param {...String} propertyNames - List of property keys this computed is dependent on
 */
export const computed = decoratorWithParams(function(target, key, desc, params) {
  if (!desc.writable) {
    throw new Error('ember-decorators does not support using getters and setters');
  }

  let value = extractValue(desc);
  return computedMacro(...params, value);
});

/**
 * Decorator that wraps [Ember.observer](https://emberjs.com/api/#method_observer)
 *
 * Triggers the target function when the dependent properties have changed
 *
 * ```javascript
 * import Ember from 'ember';
 * import { observes } from 'ember-decorators/object';
 *
 * export default Ember.Component.extend({
 *   @observes('foo')
 *   bar() {
 *     //...
 *   }
 * });
 * ```
 *
 * @function
 * @param {...String} eventNames - Names of the events that trigger the function
 */
export const observes = decoratorWithRequiredParams(Ember.observer, 'Cannot `observe` without property names');

/**
 * Decorator that modifies a computed property to be read only.
 *
 * Usage:
 *
 * ```javascript
 * import Ember from 'ember';
 * import { computed, readOnly } from 'ember-decorators/object';
 *
 * export default Ember.Component.extend({
 *   @readOnly
 *   @computed('first', 'last')
 *   name(first, last) {
 *     return `${first} ${last}`;
 *   }
 * });
 * ```
 *
 * @function
 */
export const readOnly = decorator(function(target, name, desc) {
  var value = extractValue(desc);
  return value.readOnly();
});
