├── .editorconfig ├── .ember-cli ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .template-lintrc.js ├── .travis.yml ├── .watchmanconfig ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── addon ├── .gitkeep └── utils │ ├── computed-map.js │ ├── computed-promise.js │ └── computed-reduce.js ├── app ├── .gitkeep └── utils │ ├── computed-map.js │ ├── computed-promise.js │ └── computed-reduce.js ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── index.js ├── package-lock.json ├── package.json ├── testem.js ├── tests ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ └── .gitkeep │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── resolver.js │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ ├── application.hbs │ │ │ └── components │ │ │ └── .gitkeep │ ├── config │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ └── public │ │ └── robots.txt ├── helpers │ └── .gitkeep ├── index.html ├── integration │ └── .gitkeep ├── test-helper.js └── unit │ ├── .gitkeep │ └── utils │ ├── computed-map-test.js │ ├── computed-promise-test.js │ └── computed-reduce-test.js └── vendor └── .gitkeep /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.hbs] 17 | insert_final_newline = false 18 | 19 | [*.{diff,md}] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | 17 | # ember-try 18 | /.node_modules.ember-try/ 19 | /bower.json.ember-try 20 | /package.json.ember-try 21 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | ecmaVersion: 2017, 5 | sourceType: 'module' 6 | }, 7 | plugins: [ 8 | 'ember' 9 | ], 10 | extends: [ 11 | 'eslint:recommended', 12 | 'plugin:ember/recommended' 13 | ], 14 | env: { 15 | browser: true 16 | }, 17 | rules: { 18 | }, 19 | overrides: [ 20 | // node files 21 | { 22 | files: [ 23 | '.eslintrc.js', 24 | '.template-lintrc.js', 25 | 'ember-cli-build.js', 26 | 'index.js', 27 | 'testem.js', 28 | 'blueprints/*/index.js', 29 | 'config/**/*.js', 30 | 'tests/dummy/config/**/*.js' 31 | ], 32 | excludedFiles: [ 33 | 'addon/**', 34 | 'addon-test-support/**', 35 | 'app/**', 36 | 'tests/dummy/app/**' 37 | ], 38 | parserOptions: { 39 | sourceType: 'script', 40 | ecmaVersion: 2015 41 | }, 42 | env: { 43 | browser: false, 44 | node: true 45 | }, 46 | plugins: ['node'], 47 | rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, { 48 | // add your custom rules and overrides for node files here 49 | }) 50 | } 51 | ] 52 | }; 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist/ 5 | /tmp/ 6 | 7 | # dependencies 8 | /bower_components/ 9 | /node_modules/ 10 | 11 | # misc 12 | /.env* 13 | /.sass-cache 14 | /connect.lock 15 | /coverage/ 16 | /libpeerconnection.log 17 | /npm-debug.log* 18 | /testem.log 19 | /yarn-error.log 20 | 21 | # ember-try 22 | /.node_modules.ember-try/ 23 | /bower.json.ember-try 24 | /package.json.ember-try 25 | 26 | # for a library or package, you might want to ignore these files since the code 27 | # is intended to run in multiple environments; otherwise, check them in: 28 | .node-version 29 | .nvmrc 30 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /tmp/ 4 | 5 | # dependencies 6 | /bower_components/ 7 | 8 | # misc 9 | /.bowerrc 10 | /.editorconfig 11 | /.ember-cli 12 | /.env* 13 | /.eslintignore 14 | /.eslintrc.js 15 | /.gitignore 16 | /.node-version 17 | /.nvmrc 18 | /.template-lintrc.js 19 | /.travis.yml 20 | /.watchmanconfig 21 | /bower.json 22 | /config/ember-try.js 23 | /CONTRIBUTING.md 24 | /ember-cli-build.js 25 | /testem.js 26 | /tests/ 27 | /yarn.lock 28 | .gitkeep 29 | 30 | # ember-try 31 | /.node_modules.ember-try/ 32 | /bower.json.ember-try 33 | /package.json.ember-try 34 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended' 5 | }; 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | # we recommend testing addons with the same minimum supported node version as Ember CLI 5 | # so that your addon works for all apps 6 | - "6" 7 | 8 | sudo: false 9 | dist: trusty 10 | 11 | addons: 12 | chrome: stable 13 | 14 | cache: 15 | directories: 16 | - $HOME/.npm 17 | 18 | env: 19 | global: 20 | # See https://git.io/vdao3 for details. 21 | - JOBS=1 22 | 23 | branches: 24 | only: 25 | - master 26 | # npm version tags 27 | - /^v\d+\.\d+\.\d+/ 28 | 29 | jobs: 30 | fail_fast: true 31 | allow_failures: 32 | - env: EMBER_TRY_SCENARIO=ember-canary 33 | 34 | include: 35 | # runs linting and tests with current locked deps 36 | 37 | - stage: "Tests" 38 | name: "Tests" 39 | script: 40 | - npm run lint:hbs 41 | - npm run lint:js 42 | - npm test 43 | 44 | # we recommend new addons test the current and previous LTS 45 | # as well as latest stable release (bonus points to beta/canary) 46 | - stage: "Additional Tests" 47 | env: EMBER_TRY_SCENARIO=ember-lts-2.18 48 | - env: EMBER_TRY_SCENARIO=ember-lts-3.4 49 | - env: EMBER_TRY_SCENARIO=ember-release 50 | - env: EMBER_TRY_SCENARIO=ember-beta 51 | - env: EMBER_TRY_SCENARIO=ember-canary 52 | - env: EMBER_TRY_SCENARIO=ember-default-with-jquery 53 | 54 | before_install: 55 | - npm config set spin false 56 | - npm install -g npm@4 57 | - npm --version 58 | 59 | script: 60 | - node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO 61 | 62 | notifications: 63 | email: false 64 | slack: 65 | secure: "fm1utPkfGK0g5fXpguKC0bK7a1CJfKug//HKXbrMVwHa7/AGRGMl1+Fr13g+L6xE6iqOiGmzGeIle866Les08tBYiJTCd/+/t+Mxt+WpStOQysCZKI7BYG9hYzgy6fun+zpC0gftlS5J+gi+nhC55CJ+fzawSAn8OQbYII1zzzQZrzxJJjnLoCjyEY4pG9AUNbaNk3nkJi1+qsjZa1BV+y6tgpCAoVbLBuJSkMkZV50AXQae+skXHUCyXMrmxWJD568zpPYed5aS2W5gNpBTwPJuSAJcHTOzob6pHaLZ2D5IxqCRq5P0KpTAGINhCZsouGrR3M9pIaOvw+TW2PyYYlsJvc3iN4LTVkTSDMhpZUMVxIvwIaWKZAl2LALlbQiKOqLBIP6Ztc5cLW66OsvrGaJyWl69Pf3wIutrPkSgYaHq13PZIA64OFEb2CE+CSofcQrTT2q3V9rLPXWUSbeLzrQRFTDi0mjoBCpXFvEAcZ8U3H8e9Nq+3xHaUBET2J3yg1dhzOKzM817scfAATwo48ScM4Ma5iUzyzqmN0O51Uu786xSBMOgYbkBVY7FaztLIAyRDZ+yFvEjJugtcKPUNh4zgFCA6ftD5NLIXbIVGC3t8XdWZeih/MFCUM/4sYrqhqzmy5M/OB49QQPSaRcjN8evGJTJ21tBWxOTrOx0V3U=" 66 | 67 | deploy: 68 | provider: npm 69 | email: me@tomasbasham.co.uk 70 | api_key: 71 | secure: "Wk/DE54NFXMqM7Zaj+92rzs8x1ArTEZdqudiRNn/WStW6d7hoAgwh/hTrbDB37VlLR2QLbyA8xH/WAPbjf62cG3xp06R1VcdIsfKsJqpf9jQ+ueGk9OGMRG5IWPgKEOOE+6XxDwzQ9zt8FY+8NYY/kdc+kbH1WijUGYzWFfMJqcmqtmAUgqUbKgeTtz+7eTQFg6CPQ2xxE7xsPjFQ05cHg19q9p8slRh1FXKEnq1bbMviiAAzM3M0JAAFnpyQqoyi6GxUxTOmp+/y0gwlayodSlZ4rDlqh6Lp6raVF8nYUlEfMk8vplu2x23QcNbLY9K2F55BXUJKlMPqSIF/I0NMocJ4iZxvj0c+WABRd7qv260c3HGpNexJ2w2uim2t7nM3CJf9i0S0hh8OspAslSzTOziwfp09P9d0qkPbPODKiy2vb3CRK+GmoHfQTkRHNAtMGE7i2qg4miKa5PVQMTqr6LRfaWKfcn3irjmSQ/5ITa8MgPx9noHL+OxOB9x2mh8iMICwK02Vo8kIzuUM0Uw6pNcOfbItvqmufiMgGGKGhDT38KIIb5gEoDFGD5DmaYxEylc6fo/m0odS4ub2eEI1Dxki0dS2r8gfu1vHfHxN0rtWN1v5NCujYL71uKbp+ELQzlnGL6gCC4jbI1JzZB3gc+NMirdZ9djKnI3IWDkXLA=" 72 | on: 73 | tags: true 74 | repo: tomasbasham/ember-cli-dispatch 75 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at tomasbasham@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | ## Installation 4 | 5 | * `git clone ` this repository 6 | * `cd ember-cli-dispatch` 7 | * `npm install` 8 | 9 | ## Linting 10 | 11 | * `npm run lint:hbs` 12 | * `npm run lint:js` 13 | * `npm run lint:js -- --fix` 14 | 15 | ## Running tests 16 | 17 | * `ember test` – Runs the test suite on the current Ember version 18 | * `ember test --server` – Runs the test suite in "watch mode" 19 | * `ember try:each` – Runs the test suite against multiple Ember versions 20 | 21 | ## Running the dummy application 22 | 23 | * `ember serve` 24 | * Visit the dummy application at 25 | [http://localhost:4200](http://localhost:4200). 26 | 27 | For more information on using ember-cli, visit 28 | [https://ember-cli.com/](https://ember-cli.com/). 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ember-cli-dispatch [![Build Status](https://travis-ci.com/tomasbasham/ember-cli-dispatch.svg?branch=master)](https://travis-ci.com/tomasbasham/ember-cli-dispatch) [![Maintainability](https://api.codeclimate.com/v1/badges/55f72c38f385fc50973f/maintainability)](https://codeclimate.com/github/tomasbasham/ember-cli-dispatch/maintainability) 2 | 3 | An [Ember CLI](https://ember-cli.com/) addon to extend computed properties with 4 | asynchronous values. 5 | 6 | Computed properties are really handy for taking one or more static properties, 7 | or in fact other computed properties, and transforming their values. However it 8 | is often the case that a value is not immediately present and may need to be 9 | fetch asynchronously. This prevents us from using computed properties because 10 | we cannot return a future value. To accomplish this currently we could return a 11 | promise from a computed property and create, potentially, a long chain of 12 | `then`'s and `catch`'s to then finally compute the value. This is very 13 | cumbersome and will not lend itself well if required within template logic. 14 | 15 | This addon implements a computed property macro that works internally with 16 | promises. Each computed property can be assigned a default value that will be 17 | used until the promise resolves and the computed property's value is updated. 18 | 19 | ## Compatibility 20 | 21 | * Ember.js v2.18 or above 22 | * Ember CLI v2.13 or above 23 | 24 | ## Installation 25 | 26 | From within your Ember CLI project directory run: 27 | ``` 28 | ember install ember-cli-dispatch 29 | ``` 30 | 31 | ## Usage 32 | 33 | This addon implements a utility function wrapping a promise object, which once 34 | resolved will set the value of a computed property to that returned by the 35 | promise. 36 | 37 | ### Computed Promise 38 | 39 | In order to create an asynchronous computed property you must import the 40 | `computedPromise` macro included in this addon. The macro accepts a property 41 | function, that may return a promise, and optionally a default value that will 42 | be used until the internal promise resolves. 43 | 44 | ##### Computed Promise Example 45 | 46 | ```JavaScript 47 | // app/controllers/application.js 48 | import Controller from '@ember/controller'; 49 | import $ from 'jquery'; 50 | 51 | import computedPromise from 'ember-cli-dispatch/utils/computed-promise'; 52 | 53 | export default Controller.extend({ 54 | serverResponse: computedPromise(function() { 55 | return $.getJSON('https://www.example.com/response.json') 56 | }, 'fetching...') 57 | }); 58 | ``` 59 | 60 | This will create a computed property named `serverResponse` that will take on 61 | the value `fetching...` until the result of the promise returned from jQuery's 62 | `getJson` method is resolved. Once resolved the computed property will equal 63 | the value returned by the promise. 64 | 65 | ### Bundled Macros 66 | 67 | In addition to the `computedPromise` macro this addon includes a couple of 68 | utility macros that make use of wrapping computationally expensive operations 69 | within promises. The first of these utility macros is `map` which transforms 70 | every value within an array; and the second is `reduce` which applies a 71 | function against an accumulator for each value of an array (from left-to-right) 72 | to reduce it to a single value. 73 | 74 | ##### Computed Map Example 75 | 76 | ```JavaScript 77 | // app/controllers/application.js 78 | import Controller from '@ember/controller'; 79 | import computedMap from 'ember-cli-dispatch/utils/computed-map'; 80 | 81 | export default Controller.extend({ 82 | scores: [1, 2, 3, 4, 5], 83 | 84 | doubleScores: computedMap('scores', function(score) { 85 | return score * 2; 86 | }, 'computing...') 87 | }); 88 | ``` 89 | 90 | This is a contrived example to create a computed property that doubles each of 91 | the values within the `scores` array. 92 | 93 | ##### Computed Reduce Example 94 | 95 | ```JavaScript 96 | // app/controllers/application.js 97 | import Controller from '@ember/controller'; 98 | import computedReduce from 'ember-cli-dispatch/utils/computed-reduce'; 99 | 100 | export default Controller.extend({ 101 | scores: [1, 2, 3, 4, 5], 102 | 103 | totalScores: computedReduce('scores', function(cumulativeTotal, score) { 104 | return cumulativeTotal + score; 105 | }, 'computing...') 106 | }); 107 | ``` 108 | 109 | This is a contrived example to create a computed property that totals up the 110 | values within the `scores` array. 111 | 112 | ### Custom Macro 113 | 114 | It is also possible to create your own asynchronous computed property macros. 115 | To do this you need to import the `computedPromise` macro. 116 | 117 | ##### Custom Macro Example 118 | 119 | ```JavaScript 120 | // app/utils/computed-macro.js 121 | import computedPromise from 'ember-cli-dispatch/utils/computed-promise'; 122 | import { get } from '@ember/object'; 123 | 124 | export default function computedMacro(propertyName, defaultValue) { 125 | return computedPromise(`${propertyName}.[]`, function() { 126 | return get(this, propertyName); 127 | }, defaultValue); 128 | }; 129 | ``` 130 | 131 | This shows a very basic implementation of a computed property macro that passes 132 | back the value of the specified property. This of course can be extended to 133 | perform whatever operation is required by your applications. 134 | 135 | ## License 136 | 137 | This project is licensed under the [MIT License](LICENSE.md). 138 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomasbasham/ember-cli-dispatch/7d688171b2df350d32e6173bab7ed39ced67f9c5/addon/.gitkeep -------------------------------------------------------------------------------- /addon/utils/computed-map.js: -------------------------------------------------------------------------------- 1 | import computedPromise from 'ember-cli-dispatch/utils/computed-promise'; 2 | import { get } from '@ember/object'; 3 | 4 | /* 5 | * Utility method, returning an ember computed 6 | * property that wraps up a map operation in a 7 | * promise object. The computed property will 8 | * take on a default value, if supplied, until 9 | * the promise is resolved at which point the 10 | * computed property will be set to the returned 11 | * data. 12 | * 13 | * @method computedMap 14 | * 15 | * @param {String} propertyName 16 | * The name of an array property to be mapped. 17 | * 18 | * @param {Function} propertyFunction 19 | * A function used to map the array. 20 | * 21 | * @param {String} defaultValue 22 | * The default value of the computed property. 23 | * 24 | * @return {Ember.computed} 25 | * An Ember computed property. 26 | */ 27 | export default function computedMap(propertyName, propertyFunction, defaultValue) { 28 | return computedPromise(`${propertyName}.[]`, function() { 29 | const property = get(this, propertyName); 30 | return property.map(propertyFunction); 31 | }, defaultValue); 32 | } 33 | -------------------------------------------------------------------------------- /addon/utils/computed-promise.js: -------------------------------------------------------------------------------- 1 | import EmberError from '@ember/error'; 2 | 3 | import { computed, get, set } from '@ember/object'; 4 | import { Promise } from 'rsvp'; 5 | 6 | /* 7 | * Utility method, returning an ember computed 8 | * property that wraps up a promise object. The 9 | * computed property will take on a default 10 | * value, if supplied, until the promise is 11 | * resolved at which point the computed property 12 | * will be set to the returned data. 13 | * 14 | * @method computedPromise 15 | * 16 | * @param {Rest} args 17 | * Dependent properties, function and default value. 18 | * 19 | * @return {Ember.computed} 20 | * An Ember computed property. 21 | */ 22 | export default function computedPromise(...args) { 23 | let defaultValue = args.pop(); 24 | let propertyFunction = null; 25 | 26 | // Grab the function and default value from the arguments. 27 | if (typeof defaultValue !== 'function') { 28 | propertyFunction = args.pop(); 29 | } else { 30 | propertyFunction = defaultValue; 31 | defaultValue = null; 32 | } 33 | 34 | if (typeof propertyFunction !== 'function') { 35 | throw new EmberError('Computed Promise declared without a property function'); 36 | } 37 | 38 | // The computed promise function. 39 | const computedPromiseWrapper = function(propertyName, value) { 40 | if (typeof value !== 'undefined') { 41 | return value; 42 | } 43 | 44 | // Convenience function to set the value of the property. 45 | const setProperty = (propertyName, value) => { 46 | if (value && get(this, propertyName) !== value) { 47 | set(this, propertyName, value); 48 | } 49 | }; 50 | 51 | // Resolve a promise and catch any errors. 52 | const promise = new Promise((resolve) => { 53 | resolve(propertyFunction.apply(this)); 54 | }).then(function(data) { 55 | setProperty(propertyName, data); 56 | return data; 57 | }).catch(function(errors) { 58 | setProperty(propertyName, errors); 59 | return errors; 60 | }); 61 | 62 | set(this, `${propertyName}Promise`, promise); 63 | return defaultValue; 64 | }; 65 | 66 | args.push(computedPromiseWrapper); 67 | return computed.apply(this, args); 68 | } 69 | -------------------------------------------------------------------------------- /addon/utils/computed-reduce.js: -------------------------------------------------------------------------------- 1 | import computedPromise from 'ember-cli-dispatch/utils/computed-promise'; 2 | import { get } from '@ember/object'; 3 | 4 | /* 5 | * Utility method, returning an ember computed 6 | * property that wraps up a reduce operation in 7 | * a promise object. The computed property will 8 | * take on a default value, if supplied, until 9 | * the promise is resolved at which point the 10 | * computed property will be set to the returned 11 | * data. 12 | * 13 | * @method computedMap 14 | * 15 | * @param {String} propertyName 16 | * The name of an array property to be reduced. 17 | * 18 | * @param {Function} propertyFunction 19 | * A function used to reduce the array. 20 | * 21 | * @param {String} defaultValue 22 | * The default value of the computed property. 23 | * 24 | * @return {Ember.computed} 25 | * An Ember computed property. 26 | */ 27 | export default function computedReduce(propertyName, propertyFunction, defaultValue) { 28 | return computedPromise(`${propertyName}.[]`, function() { 29 | const property = get(this, propertyName); 30 | return property.reduce(propertyFunction); 31 | }, defaultValue); 32 | } 33 | -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomasbasham/ember-cli-dispatch/7d688171b2df350d32e6173bab7ed39ced67f9c5/app/.gitkeep -------------------------------------------------------------------------------- /app/utils/computed-map.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-cli-dispatch/utils/computed-map'; 2 | -------------------------------------------------------------------------------- /app/utils/computed-promise.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-cli-dispatch/utils/computed-promise'; 2 | -------------------------------------------------------------------------------- /app/utils/computed-reduce.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-cli-dispatch/utils/computed-reduce'; 2 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | 5 | module.exports = function() { 6 | return Promise.all([ 7 | getChannelURL('release'), 8 | getChannelURL('beta'), 9 | getChannelURL('canary') 10 | ]).then((urls) => { 11 | return { 12 | scenarios: [ 13 | { 14 | name: 'ember-lts-2.18', 15 | env: { 16 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 'jquery-integration': true }) 17 | }, 18 | npm: { 19 | devDependencies: { 20 | '@ember/jquery': '^0.5.1', 21 | 'ember-source': '~2.18.0' 22 | } 23 | } 24 | }, 25 | { 26 | name: 'ember-lts-3.4', 27 | npm: { 28 | devDependencies: { 29 | 'ember-source': '~3.4.0' 30 | } 31 | } 32 | }, 33 | { 34 | name: 'ember-release', 35 | npm: { 36 | devDependencies: { 37 | 'ember-source': urls[0] 38 | } 39 | } 40 | }, 41 | { 42 | name: 'ember-beta', 43 | npm: { 44 | devDependencies: { 45 | 'ember-source': urls[1] 46 | } 47 | } 48 | }, 49 | { 50 | name: 'ember-canary', 51 | npm: { 52 | devDependencies: { 53 | 'ember-source': urls[2] 54 | } 55 | } 56 | }, 57 | // The default `.travis.yml` runs this scenario via `npm test`, 58 | // not via `ember try`. It's still included here so that running 59 | // `ember try:each` manually or from a customized CI config will run it 60 | // along with all the other scenarios. 61 | { 62 | name: 'ember-default', 63 | npm: { 64 | devDependencies: {} 65 | } 66 | }, 67 | { 68 | name: 'ember-default-with-jquery', 69 | env: { 70 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 71 | 'jquery-integration': true 72 | }) 73 | }, 74 | npm: { 75 | devDependencies: { 76 | '@ember/jquery': '^0.5.1' 77 | } 78 | } 79 | } 80 | ] 81 | }; 82 | }); 83 | }; 84 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(/* environment, appConfig */) { 4 | return { }; 5 | }; 6 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 4 | 5 | module.exports = function(defaults) { 6 | let app = new EmberAddon(defaults, { 7 | // Add options here 8 | }); 9 | 10 | /* 11 | This build file specifies the options for the dummy test app of this 12 | addon, located in `/tests/dummy` 13 | This build file does *not* influence how the addon or the app using it 14 | behave. You most likely want to be modifying `./index.js` or app's build file 15 | */ 16 | 17 | return app.toTree(); 18 | }; 19 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | name: require('./package').name 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-dispatch", 3 | "version": "1.3.1", 4 | "description": "An ember-cli addon to extend computed properties with asynchronous values", 5 | "keywords": [ 6 | "ember-addon", 7 | "computed", 8 | "property", 9 | "promise" 10 | ], 11 | "repository": "https://github.com/tomasbasham/ember-cli-dispatch", 12 | "license": "MIT", 13 | "author": "Tomas Basham ", 14 | "directories": { 15 | "doc": "doc", 16 | "test": "tests" 17 | }, 18 | "scripts": { 19 | "build": "ember build", 20 | "lint:hbs": "ember-template-lint .", 21 | "lint:js": "eslint .", 22 | "start": "ember serve", 23 | "test": "ember test", 24 | "test:all": "ember try:each" 25 | }, 26 | "dependencies": { 27 | "ember-cli-babel": "^7.1.2" 28 | }, 29 | "devDependencies": { 30 | "@ember/optional-features": "^0.6.3", 31 | "broccoli-asset-rev": "^2.7.0", 32 | "ember-cli": "~3.7.1", 33 | "ember-cli-dependency-checker": "^3.0.0", 34 | "ember-cli-eslint": "^4.2.3", 35 | "ember-cli-htmlbars": "^3.0.0", 36 | "ember-cli-htmlbars-inline-precompile": "^1.0.3", 37 | "ember-cli-inject-live-reload": "^1.8.2", 38 | "ember-cli-sri": "^2.1.1", 39 | "ember-cli-template-lint": "^1.0.0-beta.1", 40 | "ember-cli-uglify": "^2.1.0", 41 | "ember-disable-prototype-extensions": "^1.1.3", 42 | "ember-export-application-global": "^2.0.0", 43 | "ember-load-initializers": "^1.1.0", 44 | "ember-maybe-import-regenerator": "^0.1.6", 45 | "ember-qunit": "^3.4.1", 46 | "ember-resolver": "^5.0.1", 47 | "ember-source": "~3.7.0", 48 | "ember-source-channel-url": "^1.1.0", 49 | "ember-try": "^1.0.0", 50 | "eslint-plugin-ember": "^5.2.0", 51 | "eslint-plugin-node": "^7.0.1", 52 | "loader.js": "^4.7.0", 53 | "qunit-dom": "^0.8.0" 54 | }, 55 | "engines": { 56 | "node": "6.* || 8.* || >= 10.*" 57 | }, 58 | "ember-addon": { 59 | "configPath": "tests/dummy/config" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | test_page: 'tests/index.html?hidepassed', 3 | disable_watching: true, 4 | launch_in_ci: [ 5 | 'Chrome' 6 | ], 7 | launch_in_dev: [ 8 | 'Chrome' 9 | ], 10 | browser_args: { 11 | Chrome: { 12 | ci: [ 13 | // --no-sandbox is needed when running Chrome inside a container 14 | process.env.CI ? '--no-sandbox' : null, 15 | '--headless', 16 | '--disable-gpu', 17 | '--disable-dev-shm-usage', 18 | '--disable-software-rasterizer', 19 | '--mute-audio', 20 | '--remote-debugging-port=0', 21 | '--window-size=1440,900' 22 | ].filter(Boolean) 23 | } 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from './resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from './config/environment'; 5 | 6 | const App = Application.extend({ 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix, 9 | Resolver 10 | }); 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomasbasham/ember-cli-dispatch/7d688171b2df350d32e6173bab7ed39ced67f9c5/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomasbasham/ember-cli-dispatch/7d688171b2df350d32e6173bab7ed39ced67f9c5/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomasbasham/ember-cli-dispatch/7d688171b2df350d32e6173bab7ed39ced67f9c5/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomasbasham/ember-cli-dispatch/7d688171b2df350d32e6173bab7ed39ced67f9c5/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember-resolver'; 2 | 3 | export default Resolver; 4 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from './config/environment'; 3 | 4 | const Router = EmberRouter.extend({ 5 | location: config.locationType, 6 | rootURL: config.rootURL 7 | }); 8 | 9 | Router.map(function() { 10 | }); 11 | 12 | export default Router; 13 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomasbasham/ember-cli-dispatch/7d688171b2df350d32e6173bab7ed39ced67f9c5/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomasbasham/ember-cli-dispatch/7d688171b2df350d32e6173bab7ed39ced67f9c5/tests/dummy/app/styles/app.css -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |

Welcome to Ember

2 | 3 | {{outlet}} -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomasbasham/ember-cli-dispatch/7d688171b2df350d32e6173bab7ed39ced67f9c5/tests/dummy/app/templates/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(environment) { 4 | let ENV = { 5 | modulePrefix: 'dummy', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. 'with-controller': true 13 | }, 14 | EXTEND_PROTOTYPES: { 15 | // Prevent Ember Data from overriding Date.parse. 16 | Date: false 17 | } 18 | }, 19 | 20 | APP: { 21 | // Here you can pass flags/options to your application instance 22 | // when it is created 23 | } 24 | }; 25 | 26 | if (environment === 'development') { 27 | // ENV.APP.LOG_RESOLVER = true; 28 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 29 | // ENV.APP.LOG_TRANSITIONS = true; 30 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 31 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 32 | } 33 | 34 | if (environment === 'test') { 35 | // Testem prefers this... 36 | ENV.locationType = 'none'; 37 | 38 | // keep test console output quieter 39 | ENV.APP.LOG_ACTIVE_GENERATION = false; 40 | ENV.APP.LOG_VIEW_LOOKUPS = false; 41 | 42 | ENV.APP.rootElement = '#ember-testing'; 43 | ENV.APP.autoboot = false; 44 | } 45 | 46 | if (environment === 'production') { 47 | // here you can enable a production-specific feature 48 | } 49 | 50 | return ENV; 51 | }; 52 | -------------------------------------------------------------------------------- /tests/dummy/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "jquery-integration": false 3 | } 4 | -------------------------------------------------------------------------------- /tests/dummy/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions' 7 | ]; 8 | 9 | const isCI = !!process.env.CI; 10 | const isProduction = process.env.EMBER_ENV === 'production'; 11 | 12 | if (isCI || isProduction) { 13 | browsers.push('ie 11'); 14 | } 15 | 16 | module.exports = { 17 | browsers 18 | }; 19 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomasbasham/ember-cli-dispatch/7d688171b2df350d32e6173bab7ed39ced67f9c5/tests/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{content-for "body-footer"}} 31 | {{content-for "test-body-footer"}} 32 | 33 | 34 | -------------------------------------------------------------------------------- /tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomasbasham/ember-cli-dispatch/7d688171b2df350d32e6173bab7ed39ced67f9c5/tests/integration/.gitkeep -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from '../app'; 2 | import config from '../config/environment'; 3 | import { setApplication } from '@ember/test-helpers'; 4 | import { start } from 'ember-qunit'; 5 | 6 | setApplication(Application.create(config.APP)); 7 | 8 | start(); 9 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomasbasham/ember-cli-dispatch/7d688171b2df350d32e6173bab7ed39ced67f9c5/tests/unit/.gitkeep -------------------------------------------------------------------------------- /tests/unit/utils/computed-map-test.js: -------------------------------------------------------------------------------- 1 | import EmberObject from '@ember/object'; 2 | 3 | import computedMap from 'dummy/utils/computed-map'; 4 | 5 | import { A } from '@ember/array'; 6 | import { get } from '@ember/object'; 7 | import { module, test } from 'qunit'; 8 | import { Promise } from 'rsvp'; 9 | 10 | module('Unit | Utility | computed map', function() { 11 | test('it returns a default value', function(assert) { 12 | let subject = EmberObject.extend({ 13 | dataProperty: A([1, 2, 3, 4, 5]), 14 | myProperty: computedMap('dataProperty', function() { 15 | return new Promise(); 16 | }, 'default') 17 | }).create(); 18 | 19 | assert.equal(get(subject, 'myProperty'), 'default'); 20 | }); 21 | 22 | test('it returns a mapped value', function(assert) { 23 | let subject = EmberObject.extend({ 24 | dataProperty: A([1, 2, 3, 4, 5]), 25 | myProperty: computedMap('dataProperty', function(value) { 26 | return value * 2; 27 | }, 'default') 28 | }).create(); 29 | 30 | const done = assert.async(); 31 | 32 | get(subject, 'myProperty'); // Fire the computed property. 33 | get(subject, 'myPropertyPromise').then(function(value) { 34 | assert.deepEqual(value, [2, 4, 6, 8, 10]); 35 | done(); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /tests/unit/utils/computed-promise-test.js: -------------------------------------------------------------------------------- 1 | import EmberError from '@ember/error'; 2 | import EmberObject from '@ember/object'; 3 | 4 | import computedPromise from 'dummy/utils/computed-promise'; 5 | 6 | import { get } from '@ember/object'; 7 | import { module, test } from 'qunit'; 8 | import { Promise } from 'rsvp'; 9 | 10 | module('Unit | Utility | computed promise', function() { 11 | test('it throws an exception if a function is not present', function(assert) { 12 | assert.throws(computedPromise, EmberError, 'Computed Promise declared without a property function'); 13 | }); 14 | 15 | test('it returns a default value', function(assert) { 16 | let object = EmberObject.extend({ 17 | myProperty: computedPromise(function() { 18 | return new Promise(); 19 | }, 'default') 20 | }).create(); 21 | 22 | assert.equal(get(object, 'myProperty'), 'default'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /tests/unit/utils/computed-reduce-test.js: -------------------------------------------------------------------------------- 1 | import EmberObject from '@ember/object'; 2 | 3 | import computedReduce from 'dummy/utils/computed-reduce'; 4 | 5 | import { A } from '@ember/array'; 6 | import { get } from '@ember/object'; 7 | import { module, test } from 'qunit'; 8 | import { Promise } from 'rsvp'; 9 | 10 | module('Unit | Utility | computed reduce', function() { 11 | test('it returns a default value', function(assert) { 12 | let subject = EmberObject.extend({ 13 | dataProperty: A([1, 2, 3, 4, 5]), 14 | myProperty: computedReduce('dataProperty', function() { 15 | return new Promise(); 16 | }, 'default') 17 | }).create(); 18 | 19 | assert.equal(get(subject, 'myProperty'), 'default'); 20 | }); 21 | 22 | test('it returns a reduced value', function(assert) { 23 | let subject = EmberObject.extend({ 24 | dataProperty: A([1, 2, 3, 4, 5]), 25 | myProperty: computedReduce('dataProperty', function(cumulativeValue, value) { 26 | return cumulativeValue + value; 27 | }, 'default') 28 | }).create(); 29 | 30 | const done = assert.async(); 31 | 32 | get(subject, 'myProperty'); // Fire the computed property. 33 | get(subject, 'myPropertyPromise').then(function(value) { 34 | assert.equal(value, 15); 35 | done(); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomasbasham/ember-cli-dispatch/7d688171b2df350d32e6173bab7ed39ced67f9c5/vendor/.gitkeep --------------------------------------------------------------------------------