Skip to content

gabrielgrant/ember-awesome-macros

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ember-awesome-macros

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?

Two essential primitive macros come from a different addon: ember-macro-helpers

  • raw makes composing macros easier
  • writable makes setting macros possible

The API is not final until 1.0. I will be adding aliases as I think of better names for things, and possibly breaking or removing existing 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

Usage

ember install ember-awesome-macros
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

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',
value: array.filterBy('array', 'key', 2) // [{ test: 2 }]
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',
value: array.findBy('array', 'key', 2) // { test: 2 }
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.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],
value1: array.indexOf('array', 2), // 0
value2: array.indexOf('array', 2, 2) // 3
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],
value1: array.lastIndexOf('array', 2), // 3
value2: array.lastIndexOf('array', 2, 2) // 0
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

array: ['one', 'two'],
value1: array.reduce('array', (obj, cur, i) => {
  obj[cur] = i;
  return obj;
}, {}), // { one: 0, two: 1 }

string: 'one, two',
value2: array.reduce(split('string', raw(', ')), (obj, cur, i) => {
  obj[cur] = i;
  return obj;
}, {}) // { one: 0, two: 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]),
value1: array.without('array', 2), // [1, 3]
value2: array.without('array', array.objectAt(1)) // [1, 3]
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('source2', difference('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' }
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

neq

alias for notEqual

not

same as Ember.computed.not, but allows composing

source1: true,
source2: false,
value1: not('source1'), // false
value2: not(and('source1', 'source2')) // true
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
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('source2', product('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
quotient

divides numbers

source1: 3,
source2: 2,
source3: 1,
value1: quotient('source1', 'source2', 'source3'), // 1.5
value2: quotient('source2', quotient('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.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.replace

wraps String.prototype.replace, allows composing

string: 'abc',
substr: 'bc',
newSubstr: 'cb',
value1: string.replace('string', 'substr', 'newSubstr'), // 'acb'
value2: string.replace('source', '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.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.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('source2', sum('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'

About

A collection of Ember computed macros

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 98.7%
  • HTML 1.3%