Skip to content

kellyselden/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 Dependency Status devDependency Status Ember Observer Score

A random 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?

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.

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
Primitives
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
any

wraps Ember.MutableArray.any, allows composing

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

alias for collect

camelize

wraps Ember.String.camelize, allows composing

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

wraps Ember.String.capitalize, allows composing

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

wraps Ember.String.classify, allows composing

originalValue: 'test string',
newValue: classify('originalValue') // 'TestString'
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']]
compact

wraps Ember.MutableArray.compact, allows composing

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

functions like Ember.computed, but is composing friendly

key1: '1,2',

// your callback is passed the resolved values
computed1: computed('key1', value => {
  console.log(value); // '1,2'
  // do something else
}),

// you can compose your macros
computed2: computed(split('key1', raw(',')), value => {
  console.log(value); // [1, 2]
  // do something else
}),

// you can use enumerable helpers and property expansion
key2: [1, 2],
computed3: computed('key2.[]', value => {
  console.log(value); // [1, 2]
  // do something else
}),
key3: [{ key4: 1 }, { key4: 2 }],
computed4: computed('key3.@each.key4', value => {
  console.log(value); // [{ key4: 1 }, { key4: 2 }]
  // do something else
}),
key5: [{ key6: 1 }, { key7: 2 }],
computed5: computed('key5.{key6,key7}', (value1, value2) => {
  console.log(value1); // 1
  console.log(value2); // 2
  // do something else
})
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'
contains

alias for includes

dasherize

wraps Ember.String.dasherize, allows composing

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

wraps Ember.String.decamelize, allows composing

originalValue: 'TestString',
newValue: decamelize('originalValue') // 'test_string'
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 and allows composing

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

wraps Ember.MutableArray.every, allows composing

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

wraps Ember.MutableArray.filterBy, allows composing

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

wraps Ember.MutableArray.filter, allows composing

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

wraps Ember.MutableArray.findBy, allows composing

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

wraps Ember.MutableArray.find, allows composing

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

get the first item of an array

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

get a variable property name from an object

key: 'modelProperty',
model: {
  modelProperty: 'my value'
},
value: getBy('model', 'key') // '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' }
htmlSafe

wraps Ember.String.htmlSafe, allows composing

originalValue: '<input>',
newValue: htmlSafe('originalValue') // will not be escaped
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: includes('array', 'source1'), // true
value2: includes('array', 'source2'), // false
value3: includes(collect(raw('my value 1'), raw('my value 2')), raw('my value 1')) // true
indexOf

wraps Array.prototype.indexOf, allows composing

array: [2, 5, 9, 2],
value1: indexOf('array', 2), // 0
value2: indexOf('array', 2, 2) // 3
instanceOf

wraps instanceof operator

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

wraps Ember.Enumerable.isAny, allows composing

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

wraps Ember.Enumerable.isEvery, allows composing

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

wraps Ember.String.isHTMLSafe, allows composing

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

implements Array.prototype.join(), allows composing

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

wraps Array.prototype.lastIndexOf, allows composing

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

get the last item of an array

array: ['1', '2'],
string: '1, 2',
example: last('array'), // '2'
composingExample: last(split('string', raw(', '))) // '2'
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
mapBy

wraps Ember.MutableArray.mapBy, allows composing

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

wraps Ember.MutableArray.map, allows composing

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

exposes all Math functions

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

alias for product

not

same as Ember.computed.not, but allows composing

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

implements http://emberjs.com/api/classes/Ember.MutableArray.html#method_objectAt, allows composing

array: Ember.A(['my value']),
source1: 0,
source2: 1,
value1: objectAt('array', 'source1'), // 'my value'
value2: objectAt('array', 'source2'), // undefined
value3: objectAt(collect(raw('my value 1')), raw(0)) // 'my value'
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
promiseAll

combines promises using RSVP.all

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

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

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

can also wrap a computed property

products: promiseArray(computed(function() {
  return this.store.findAll('product');
}))
promiseHash

combines promises using RSVP.hash

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

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

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

can also wrap a computed property

product: promiseObject(computed(function() {
  return this.store.findRecord('product', 1);
}))
promiseResolve

wraps a value in an RSVP.resolve

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

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

subtracts numbers

source1: 3,
source2: 2,
source3: 1,
value1: quotient('source1', 'source2', 'source3'), // 1.5
value2: quotient('source2', quotient('source2', 'source3')) // 1.5
raw

a helper if you want to use "literals" in your macros

value1: equal('key1', raw('my value 1')),
value2: indexOf('key2', raw('my value 2'))

or you want to get fancy with composing

source: 'my computed value',
value: hash({
  prop1: 'source',
  prop2: raw('my raw value')
}) // { prop1: 'my computed value', prop2: 'my raw value' }
reduce

wraps Array.prototype.reduce, allows composing

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

string: 'one, two',
value2: reduce(split('string', raw(', ')), (obj, cur, i) => {
  obj[cur] = i;
  return obj;
}, {}) // { one: 0, two: 1 }
slice

wraps Array.prototype.slice, allows composing

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

implements String.prototype.split(), allows composing

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

wraps String.prototype.substr, allows composing

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

wraps String.prototype.substring, allows composing

string1: 'abcxyz',
string2: 'abc',
string3: 'xyz',
example: substring('string1', 2, 4), // 'cx'
composingExample: substring(tag`${'string2'}${'string3'}`, 2, 4) // 'cx'
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'
toLower

wraps String.prototype.toLowerCase, allows composing

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

wraps String.prototype.toUpperCase, allows composing

originalValue: 'TestString',
newValue: toUpper('originalValue') // 'TESTSTRING'
typeOf

wraps typeOf operator

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

wraps Ember.String.underscore, allows composing

originalValue: 'TestString',
newValue: underscore('originalValue') // 'test_string'
uniqBy

wraps Ember.MutableArray.uniqBy, allows composing

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

wraps Ember.MutableArray.uniq, allows composing

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

wraps Ember.Enumerable.without, allows composing

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

since all the macros are read-only, use this to bring back setting capability

value1: writable(and('key1', 'key2')), // setting this replaces the macro with your value
value2: writable(and('key1', 'key2'), {
  set() {
    // do something
    return 'new value';
  }
}), // setting this will not overwrite your macro
value3: writable(and('key1', 'key2'), function() {
  // do something
  return 'new value';
}), // same as above, but shorthand