A collection of Ember computed macros

README.md

ember-awesome-macros

Greenkeeper badge npm version Build Status dependencies Status devDependencies Status Ember Observer Score Ember Version

A collection of Ember computed macros. All the macros are composable, meaning you can nest them to your heart's content, like so:

result: conditional(and(not('value1'), 'value2'), sum('value3', 1), collect('value4', toUpper('value5'))) // lisp much?

Arguments to macros can be:

  • A string, in which case it references the name of a property whose value is used for the calculation
  • A non-string value, in which case it is used as such
  • A raw macro, which lets you use a string as the value without referencing a property
  • Another macro

Examples of these different usages:

source1: 'my value',
source2: 2

value1: equal('source1', 'source2'), // false
value2: equal('source2', 2), // true
value3: equal('source1', raw('my value')) // true
value4: equal('source2', sum(1, 1)) // true

Three essential primitive macros are re-exported from a different addon: ember-macro-helpers

  • computed makes composing macros easier
  • raw allows you to escape string literals to be used in macros
  • writable makes setting macros possible
import computed from 'ember-awesome-macros/computed';
import raw from 'ember-awesome-macros/raw';
import writable from 'ember-awesome-macros/writable';
// or
import { computed, raw, writable } from 'ember-awesome-macros';

If you have any opinions or want a new macro added, just ask! Or feel free to submit a pull request.

Introduction Video

Ember Awesome Macros

Installation

ember install ember-awesome-macros

Usage

import nameOfMacro from 'ember-awesome-macros/name-of-macro';
// or
import { nameOfMacro } from 'ember-awesome-macros';

Macro list

Array
Boolean
Comparison
Number
Object
Math
Promise
String

Custom macros

See [https://github.com/kellyselden/ember-macro-helpers#custom-macros].

Details

add

alias for sum

and

same as Ember.computed.and, but allows composing

source1: false,
source2: true,
source3: false,
value1: and('source1', 'source2', 'source3'), // false
value2: and(not('source1'), 'source2', not('source3')) // true
array.any

wraps Ember.Array.any, allows composing

array: Ember.A([1, 2]),
value1: array.any('array', val => val === 2), // true
value2: array.any('array', val => val === 3) // false
array.compact

wraps Ember.Array.compact, allows composing

array: Ember.A([1, 2, null]),
value: array.compact('array') // [1, 2]
array.concat

wraps Array.prototype.concat(), allows composing

array1: Ember.A([1, 2]),
array2: Ember.A([3, 4]),
string: '3,4',
example: array.concat('array1', 'array2'), // [1, 2, 3, 4]
composingExample: array.concat('array1', split('string', raw(','))) // [1, 2, 3, 4]
array.every

wraps Ember.Array.every, allows composing

array: Ember.A([1, 1]),
value1: array.every('array', val => val === 1), // true
value2: array.every('array', val => val === 2) // false
array.filterBy

wraps Ember.Array.filterBy, allows composing

array: Ember.A([{ test: 1 }, { test: 2 }]),
key: 'test',
referenceValue: 1,
value1: array.filterBy('array', 'key', 2), // [{ test: 2 }]
value2: array.filterBy('array', raw('test'), 'referenceValue') // [{ test: 1 }]
array.filter

wraps Ember.Array.filter, allows composing

array: Ember.A([{ test: 1 }, { test: 2 }]),
value: array.filter('array', item => item.test === 2) // [{ test: 2 }]
array.findBy

wraps Ember.Array.findBy, allows composing

array: Ember.A([{ test: 1 }, { test: 2 }]),
key: 'test',
referenceValue: 1,
value1: array.findBy('array', 'key', 2), // { test: 2 }
value2: array.findBy('array', raw('test'), 'referenceValue') // { test: 1 }
array.find

wraps Ember.Array.find, allows composing

array: Ember.A([{ test: 1 }, { test: 2 }]),
value: array.find('array', item => item.test === 2) // { test: 2 }
array.first

get the first item of an array

array: ['1', '2'],
string: '1, 2',
example: array.first('array'), // '1'
composingExample: array.first(split('string', raw(', '))) // '1'
array.groupBy

allows to group an array of objects by key with an optional comparator

array: Ember.A([{ test: 1, name: 'foo'}, { test: 2, name: 'foo' }, { test: 1, name: 'bar' }]),
key: 'test',
value: array.groupBy('array', 'key')
/*
  [
    {
      key: 'test',
      value: 1,
      items: [{ test: 1 , name: 'foo' }, { test: 1 , name: 'bar' }]
    },
    {
      key: 'test',
      value: 2,
      items: [{ test: 2 , name: 'foo' }]
    }
  ]
*/

The comparator is required if grouping by a key representing a complex object like Date

// Given two different objects that represent the same info
// today1 = new Date();
// today2 = new Date();
// randomDate = new Date(2017, 1, 1);
array: Ember.A([
  { test: 1 , date: today1 },
  { test: 2 , date: today2 },
  { test: 1 , date: randomDate }
]),
key: 'date'

// without comparator
value1: array.groupBy('array', 'key'),
/*
  [
    {
      key: 'date',
      value: today1,
      items: [{ test: 1 , date: today1 }]
    },
    {
      key: 'date',
      value: today2,
      items: [{ test: 2 , date: today2 }]
    },
    {
      key: 'date',
      value: randomDate,
      items: [{ test: 1 , date: randomDate }]
    }
  ]
*/

// with comparator
value2: array.groupBy('array', 'key', (groupValue, currentValue) => {
  return groupValue.getTime() === currentValue.getTime();
})
/*
  [
    {
      key: 'date',
      value: today1,
      items: [{ test: 1 , date: today1 }, { test: 2 , date: today2 }]
    },
    {
      key: 'date',
      value: randomDate,
      items: [{ test: 1 , date: randomDate }]
    }
  ]
*/
array.includes

implements Array.prototype.includes(), allows composing

array: Ember.A(['my value 1', 'my value 2']),
source1: 'my value 2',
source2: 'my value 3',
value1: array.includes('array', 'source1'), // true
value2: array.includes('array', 'source2'), // false
value3: array.includes(collect(raw('my value 1'), raw('my value 2')), raw('my value 1')) // true
array.indexOf

wraps Array.prototype.indexOf(), allows composing

array: [2, 5, 9, 2],
referenceValue: 9,
value1: array.indexOf('array', 2), // 0
value2: array.indexOf('array', 2, 2), // 3
value3: array.indexOf('array', 'referenceValue') // 2
array.invoke

wraps Ember.Array.invoke(), allows composing

array: [{
  foo(arg) {
    return 'bar-' + arg;
  }
}],
value1: array.invoke('array', 'foo', raw('baz')), // ['bar-baz']
value2: array.invoke('array', 'foo', 'arg'), // ['bar-hello']
value3: array.invoke('array', 'foo'), // ['bar-']
arg: 'hello'
array.isAny

wraps Ember.Enumerable.isAny, allows composing

array: Ember.A([{ test: 1 }, { test: 2 }]),
key: 'test',
value1: 2,
value2: 3,
result1: array.isAny('array', 'key', 'value1'), // true
result2: array.isAny('array', 'key', 'value2') // false
array.isEvery

wraps Ember.Enumerable.isEvery, allows composing

array1: Ember.A([{ test: 1 }, { test: 1 }]),
key: 'test',
value1: 1,
value2: 2,
result1: array.isEvery('array', 'key', 'value1'), // true
result2: array.isEvery('array', 'key', 'value2') // false
array.join

wraps Array.prototype.join(), allows composing

array: Ember.A(['1', '2']),
separator: ', ',
value1: array.join('values', 'separator'), // '1, 2'
value2: array.join(collect(raw('1'), raw('2')), raw(', ')) // '1, 2'
array.lastIndexOf

wraps Array.prototype.lastIndexOf(), allows composing

array: [2, 5, 9, 2],
referenceValue: 9,
value1: array.lastIndexOf('array', 2), // 3
value2: array.lastIndexOf('array', 2, 2), // 0
value3: array.lastIndexOf('array', 'referenceValue') // 2
array.last

get the last item of an array

array: ['1', '2'],
string: '1, 2',
example: array.last('array'), // '2'
composingExample: array.last(split('string', raw(', '))) // '2'
array.length

wraps Array.prototype.length, allows composing

array: Ember.A([1, 2]),
string: '1,2',
example: array.length('array'), // 2
composingExample: array.length(split('string', raw(','))) // 2
array.mapBy

wraps Ember.Array.mapBy, allows composing

array: Ember.A([{ test: 1 }, { test: 2 }]),
key: 'test',
value: array.mapBy('array', 'key') // [1, 2]
array.map

wraps Ember.Array.map, allows composing

array: Ember.A([{ test: 1 }, { test: 2 }]),
value: array.map('array', item => item.test) // [1, 2]
array.objectAt

wraps Ember.Array.objectAt, allows composing

array: Ember.A(['my value']),
source1: 0,
source2: 1,
value1: array.objectAt('array', 'source1'), // 'my value'
value2: array.objectAt('array', 'source2'), // undefined
value3: array.objectAt(collect(raw('my value 1')), raw(0)) // 'my value'
array.reduce

wraps Array.prototype.reduce(), allows composing

Your initial value must be a factory function if you plan to mutate it in the reduce, otherwise it would continually mutate the same object every recalculation. You can still pass an object, just be careful not to mutate it.

array: ['one', 'two'],
value1: array.reduce(
  'array',
  (obj, cur, i) => {
    obj[cur] = i;
    return obj;
  },
  // initial value is a factory function because we mutate the object
  () => ({})
), // { one: 0, two: 1 }

string: 'one, two',
value2: array.reduce(
  split('string', raw(', ')),
  (arr, cur, i) => arr.concat(cur, i),
  //initial value is an array because it is not mutated
  []
) // ['one', 0, 'two', 1]
array.rejectBy

wraps Ember.Array.rejectBy, allows composing

array: Ember.A([{ test: 1 }, { test: 2 }]),
key: 'test',
value: array.rejectBy('array', 'key', 2) // [{ test: 1 }]
array.reverse

wraps Array.prototype.reverse() (calls slice() first as to not mutate), allows composing

array: [1, 2, 3],
value1: array.reverse('array'), // [3, 2, 1]
value2: array.reverse(array.reverse('array')) // [1, 2, 3]
array.slice

wraps Array.prototype.slice(), allows composing

array: [1, 2, 3],
value1: array.slice('array', 1), // [2, 3]
value2: array.slice('array', difference('array.length', 1)) // [3]
array.sort

combines the functionality of both Array.prototype.sort() and Ember.computed.sort

array1: Ember.A(['xyz', 'abc']),
array2: Ember.A([{ key: 'abc' }, { key: 'xyz' }]),
value1: array.sort('array1'), // ['abc', 'xyz']
value2: array.sort('array2', ['key:desc']), // [{ key: 'xyz' }, { key: 'abc' }]
value3: array.sort('array2', (a, b) => a.key < b.key), // [{ key: 'xyz' }, { key: 'abc' }]
array.uniqBy

wraps Ember.Array.uniqBy, allows composing

array: Ember.A([{ test: 1 }, { test: 2 }, { test: 2 }]),
key: 'test',
value: array.uniqBy('array', 'key') // [{ test: 1 }, { test: 2 }]
array.uniq

wraps Ember.Array.uniq, allows composing

array: Ember.A([1, 2, 2]),
value: array.uniq('array') // [1, 2]
array.without

wraps Ember.Enumerable.without, allows composing

array: Ember.A([1, 2, 3]),
referenceValue: 3,
value1: array.without('array', 'referenceValue'), // [1, 2]
value2: array.without('array', 2), // [1, 3]
value3: array.without('array', array.objectAt(1)) // [1, 3]
bool

same as Ember.computed.bool, but allows composing

source1: null,
source2: 'my value 1',
source3: { source: 'source3' },
value1: bool('source1'), // false
value2: bool('source2'), // true
value3: bool('source3'), // true
collect

same as Ember.computed.collect, but allows composing

source1: 'my value 1',
source2: 'my value 2',
value: collect('source1', collect('source2')) // ['my value 1', ['my value 2']]
conditional

implements the ternary operator, allows composing

condition1: true,
condition2: false,
expr1: 'my value 1',
expr2: 'my value 2',
value1: conditional('condition1', 'expr1', 'expr2'), // 'my value 1'
value2: conditional('condition2', 'expr1', 'expr2'), // 'my value 2'
value3: conditional(or('condition1', 'condition2'), raw('my value 1'), raw('my value 2')) // 'my value 1'
defaultTrue

true if source is undefined

source1: undefined,
source2: false,
source3: 'my value',
value1: defaultTrue('source1'), // true
value2: defaultTrue('source2'), // false
value3: defaultTrue('source3') // 'my value'
difference

subtracts numbers

source1: 3,
source2: 2,
source3: 1,
value1: difference('source1', 'source2', 'source3'), // 0
value2: difference('source1', collect('source2', 'source3')) // 2
divide

alias for quotient

eq

alias for equal

equal

like Ember.computed.equal, but uses dependent properties on both sides. allows N number of arguments.

source1: 'my value',
source2: 'my other value',
source3: 'my value',
value1: equal('source1', 'source2'), // false
value2: equal('source1', 'source3'), // true
value3: equal('source1', 'source2', 'source3') // false
getBy

get a variable property name from an object

key: 'modelProperty',
model: {
  modelProperty: 'my value'
},
value1: getBy('model', 'key'), // 'my value'
value2: getBy('model', raw('modelProperty')) // 'my value'
gt

like Ember.computed.gt, but uses dependent properties on both sides and allows composing

source1: 1,
source2: 2,
source3: 1,
value1: gt('source1', 'source2'), // false
value2: gt('source1', 'source3'), // false
value3: gt('source2', 'source3') // true
gte

like Ember.computed.gte, but uses dependent properties on both sides and allows composing

source1: 1,
source2: 2,
source3: 1,
value1: gte('source1', 'source2'), // false
value2: gte('source1', 'source3'), // true
value3: gte('source2', 'source3') // true
hash

build a hash out of computed properties, allows composing

source1: 'my value 1',
source2: 'my value 2',

value1: hash({
  prop1: 'source1',
  prop2: hash({
    prop: 'source2'
  })
}), // { prop1: 'my value 1', prop2: { prop: 'my value 2' } }

// you can also build the hash using property key names
value2: hash('source1', 'source2'), // { source1: 'my value 1', source2: 'my value 2' }

// or you can mix and match, the result will be merged
value3: hash('source1', { prop2: 'source2' }) // { source1: 'my value 1', prop2: 'my value 2' }
isEmpty

wraps Ember.isEmpty

sourceString1: '',
sourceString2: foobar,

sourceArray1: [],
sourceArray2: [1, 2, 3],

sourceObject1: {},
sourceObject2: { size: 0 },

valueString1: isEmpty('sourceString1'), // true
valueString2: isEmpty('sourceString2'), // false

valueArray1: isEmpty('sourceArray1'), // true
valueArray2: isEmpty('sourceArray2'), // false

valueObject1: isEmpty('sourceObject1'), // false
valueObject2: isEmpty('sourceObject2'), // true

valueObject1: isEmpty(collect(1, 2)), // false

valueObject1: isEmpty([]), // true
instanceOf

wraps instanceof operator

key1: {},
key2: false,
key3: '',
value1: instanceOf('key1', Object), // true
value2: instanceOf(or('key2', 'key3'), String) // true
lt

like Ember.computed.lt, but uses dependent properties on both sides and allows composing

source1: 1,
source2: 2,
source3: 1,
value1: lt('source1', 'source2'), // true
value2: lt('source1', 'source3'), // false
value3: lt('source2', 'source3') // false
lte

like Ember.computed.lte, but uses dependent properties on both sides and allows composing

source1: 1,
source2: 2,
source3: 1,
value1: lte('source1', 'source2'), // true
value2: lte('source1', 'source3'), // true
value3: lte('source2', 'source3') // false
math

exposes all Math functions

source1: 2.2,
source2: 2.7,
value1: math.ceil('source1'), // 3
value2: math.floor(sum('source1', 'source2')) // 4
mod

the modulus operator

number1: 123,
number2: 45,
example: mod('number1', 'number2'), // 33
composingExample: mod(sum('number1', 'number2'), 39) // 12
multiply

alias for product

nand

applies logical NAND operation

sourceTrue: true,
sourceFalse: false,

value1: nand('sourceFalse', 'sourceFalse', 'sourceFalse'), // true
value2: nand('sourceFalse', 'sourceTrue', 'sourceFalse'), // true
value3: nand('sourceTrue', 'sourceTrue', 'sourceTrue') // false
neq

alias for notEqual

nor

applies logical NOR operation

sourceTrue: true,
sourceFalse: false,

value1: nor('sourceFalse', 'sourceFalse', 'sourceFalse'), // true
value2: nor('sourceFalse', 'sourceTrue', 'sourceFalse'), // false
value3: nor('sourceTrue', 'sourceTrue', 'sourceTrue') // false
not

same as Ember.computed.not, but allows composing

source1: true,
source2: false,
value1: not('source1'), // false
value2: not(and('source1', 'source2')) // true
notEmpty

negation of isEmpty

sourceString1: '',
sourceString2: foobar,

sourceArray1: [],
sourceArray2: [1, 2, 3],

sourceObject1: {},
sourceObject2: { size: 0 },

valueString1: notEmpty('sourceString1'), // false
valueString2: notEmpty('sourceString2'), // true

valueArray1: notEmpty('sourceArray1'), // false
valueArray2: notEmpty('sourceArray2'), // true

valueObject1: notEmpty('sourceObject1'), // true
valueObject2: notEmpty('sourceObject2'), // false

valueObject1: notEmpty(collect(1, 2)), // true

valueObject1: notEmpty([]) // false
notEqual

the inverse of equal, allows composing

source1: 'my value',
source2: 'my other value',
source3: 'my value',
value1: notEqual('source1', 'source2'), // true
value2: notEqual('source1', 'source3'), // false
value3: notEqual('source1', 'source2', 'source3') // true
number

wraps Number, allows composing

prop: true,
example: number('prop'), // 1
composingExample: sum(collect(8, number('prop'))) // 9
or

same as Ember.computed.or, but allows composing

source1: true,
source2: false,
source3: true,
value1: or('source1', 'source2', 'source3'), // true
value2: or(not('source1'), 'source2', not('source3')) // false
parseFloat

wraps parseFloat, allows composing

string1: '12.34',
string2: '12',
string3: '34',
example: parseFloat('string1'), // 12.34
composingExample: parseFloat(tag`${'string2'}.${'string3'}`) // 12.34
parseInt

wraps parseInt, allows composing

string: '123',
example: parseInt('string'), // 123
composingExample: parseInt(substr('string', 1), 8) // 19
product

multiplies numbers

source1: 1,
source2: 2,
source3: 3,
value1: product('source1', 'source2', 'source3'), // 6
value2: product('source1', collect('source2', 'source3')) // 6
promise.all

combines promises using RSVP.all

promise1: computed(function() {
  return RSVP.resolve('value1');
}),
promise2: computed(function() {
  return RSVP.resolve('value2');
}),
promise: promise.all('promise1', 'promise2') // resolves to ['value1', 'value2']
promise.array

wraps a promise in the equivalent of DS.PromiseArray (ArrayProxy and PromiseProxyMixin)

productsPromise: computed(function() {
  return this.store.findAll('product');
}),
products: promise.array('productsPromise')

can also wrap a computed property

products: promise.array(computed(function() {
  return this.store.findAll('product');
}))
promise.hash

combines promises using RSVP.hash

promise1: computed(function() {
  return RSVP.resolve('value1');
}),
promise2: computed(function() {
  return RSVP.resolve('value2');
}),
promise: promise.hash('promise1', 'promise2') // resolves to { promise1: 'value1', promise2: 'value2' }
promise.object

wraps a promise in the equivalent of DS.PromiseObject (ObjectProxy and PromiseProxyMixin)

productPromise: computed(function() {
  return this.store.findRecord('product', 1);
}),
product: promise.object('productPromise')

can also wrap a computed property

product: promise.object(computed(function() {
  return this.store.findRecord('product', 1);
}))
promise.resolve

wraps a value in an RSVP.resolve

key1: 'my value',
promise1: promise.resolve('key1'), // a resolved promise if you need it

key2: computed(function() {
  return this.store.findRecord('user');
}),
promise2: promise.resolve(conditional('someBool', 'key1', 'key2')) // resolve an object if you don't know if it is a promise or not
promise.then

calls .then() on a promise and then .get on the result

key: 'firstName',
userPromise: computed(function() {
  return this.store.findRecord('user'); // { firstName: 'Mary' }
}),

firstName: promise.then('userPromise', 'key') // 'Mary'
quotient

divides numbers

source1: 3,
source2: 2,
source3: 1,
value1: quotient('source1', 'source2', 'source3'), // 1.5
value2: quotient('source1', collect('source2', 'source3')) // 1.5
string.camelize

wraps Ember.String.camelize, allows composing

originalValue: 'test-string',
newValue: string.camelize('originalValue') // 'testString'
string.capitalize

wraps Ember.String.capitalize, allows composing

originalValue: 'test string',
newValue: string.capitalize('originalValue') // 'Test string'
string.classify

wraps Ember.String.classify, allows composing

originalValue: 'test string',
newValue: string.classify('originalValue') // 'TestString'
string.dasherize

wraps Ember.String.dasherize, allows composing

originalValue: 'TestString',
newValue: string.dasherize('originalValue') // 'test-string'
string.decamelize

wraps Ember.String.decamelize, allows composing

originalValue: 'TestString',
newValue: string.decamelize('originalValue') // 'test_string'
string.escapeExpression

wraps Ember.Handlebars.Utils.escapeExpression, allows composing

originalValue: '<input>',
newValue: string.escapeExpression('originalValue') // '&lt;input&gt;'
string.htmlSafe

wraps Ember.String.htmlSafe, allows composing

originalValue: '<input>',
newValue: string.htmlSafe('originalValue') // will not be escaped
string.indexOf

wraps String.prototype.indexOf(), allows composing

string: '121',
value: '1',
example: string.indexOf('string', 'value'), // 0
composingExample: string.indexOf(substr('string', 1), raw('1')) // 1
string.isHtmlSafe

wraps Ember.String.isHTMLSafe, allows composing

source1: '<input>',
source2: string.htmlSafe('<input>'),
value1: string.isHtmlSafe('source1'), // false
value2: string.isHtmlSafe('source2') // true
string.lastIndexOf

wraps String.prototype.lastIndexOf(), allows composing

string: '121',
value: '1',
example: string.lastIndexOf('string', 'value'), // 2
composingExample: string.lastIndexOf(substr('string', 0, 2), raw('1')) // 0
string.length

wraps String.prototype.length, allows composing

string1: 'abc',
string2: 'xyz',
example: string.length('string1'), // 3
composingExample: string.length(tag`${'string1'}${'string2'}`) // 6
string.match

wraps String.prototype.match, allows composing

string1: 'abc',
string2: 'xyz',
regex1: /abc/,
regex2: /xyz/,
example: string.match('string1', 'regex1'), // ['abc']
example: string.match('string1', 'regex2'), // null
composingExample: string.match(tag`${'string1'}${'string2'}`, 'regex2') // ['xyz']
string.replace

wraps String.prototype.replace, allows composing

string: 'abc',
substr: 'bc',
newSubstr: 'cb',
value1: string.replace('string', 'substr', 'newSubstr'), // 'acb'
value2: string.replace('string', 'substr', string.toUpper('newSubstr')) // 'aCB'
string.split

wraps String.prototype.split, allows composing

source: 'val1,val2',
key: ',',
value1: string.split('source', 'key'), // ['val1', 'val2']
value2: string.split('source', raw(',')) // ['val1', 'val2']
string.substr

wraps String.prototype.substr(), allows composing

string1: 'abcxyz',
string2: 'abc',
string3: 'xyz',
example: string.substr('string1', 2, 2), // 'cx'
composingExample: string.substr(tag`${'string2'}${'string3'}`, 2, 2) // 'cx'
string.substring

wraps String.prototype.substring(), allows composing

string1: 'abcxyz',
string2: 'abc',
string3: 'xyz',
example: string.substring('string1', 2, 4), // 'cx'
composingExample: string.substring(tag`${'string2'}${'string3'}`, 2, 4) // 'cx'
string.titleize

capitalizes words, allows composing

originalValue: 'james mcAvoy',
newValue: string.titleize('originalValue') // 'James Mcavoy'
string.toLower

wraps String.prototype.toLowerCase(), allows composing

originalValue: 'TestString',
newValue: string.toLower('originalValue') // 'teststring'
string.toUpper

wraps String.prototype.toUpperCase(), allows composing

originalValue: 'TestString',
newValue: string.toUpper('originalValue') // 'TESTSTRING'
string.trim

wraps String.prototype.trim(), allows composing

originalValue: ' TestString ',
newValue: string.trim('originalValue') // 'TestString'
string.underscore

wraps Ember.String.underscore, allows composing

originalValue: 'TestString',
newValue: string.underscore('originalValue') // 'test_string'
subtract

alias for difference

sum

adds numbers

source1: 1,
source2: 2,
source3: 3,
value1: sum('source1', 'source2', 'source3'), // 6
value2: sum('source1', collect('source2', 'source3')) // 6
tag

use a tagged template literal as a computed macro, allows composing

source: 'two',
value1: tag`one ${'source'} three`, // 'one two three'
value2: tag`one ${toUpper('source')} three` // 'one TWO three'
toStr

calls toString with args on whatever you provide

key1: {},
key2: 253,
key3: 254,
value1: toStr('key1'), // '[object Object]'
value2: toStr(math.max('key2', 'key3'), 16) // 'fe'
toString

alias for toStr

The toString name conflicts with window.toString and breaks Babel, which means you need to do something like this:

import { toString as toStr } from 'ember-awesome-macros';

That's why we provide the toStr alias.

typeOf

wraps typeOf operator

key1: {},
key2: false,
key3: '',
value1: typeOf('key1'), // 'object'
value2: typeOf(or('key2', 'key3')) // 'string'
unless

macro version of {{unless}} and the inverse of conditional, allows composing

condition1: false,
condition2: true,
expr1: 'my value 1',
expr2: 'my value 2',
value1: unless('condition1', 'expr1', 'expr2'), // 'my value 1'
value2: unless('condition2', 'expr1', 'expr2'), // 'my value 2'
value3: unless(and('condition1', 'condition2'), raw('my value 1'), raw('my value 2')) // 'my value 1'
xnor

applies logical XNOR operation

sourceTrue: true,
sourceFalse: false,

value1: xnor('sourceFalse', 'sourceFalse', 'sourceFalse'), // true
value2: xnor('sourceFalse', 'sourceTrue', 'sourceFalse'), // false
value3: xnor('sourceTrue', 'sourceTrue', 'sourceTrue') // true
xor

applies logical XOR operation

sourceTrue: true,
sourceFalse: false,

value1: xor('sourceFalse', 'sourceFalse', 'sourceFalse'), // false
value2: xor('sourceFalse', 'sourceTrue', 'sourceFalse'), // true
value3: xor('sourceTrue', 'sourceTrue', 'sourceTrue') // false

Contributing

Installation

  • git clone <repository-url>
  • cd ember-awesome-macros
  • npm install

Linting

  • npm run lint:hbs
  • npm run lint:js
  • npm run lint:js -- --fix

Running tests

  • ember test – Runs the test suite on the current Ember version
  • ember test --server – Runs the test suite in "watch mode"
  • ember try:each – Runs the test suite against multiple Ember versions

Running the dummy application

For more information on using ember-cli, visit https://ember-cli.com/.

License

This project is licensed under the MIT License.