├── .editorconfig ├── .ember-cli ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .travis.yml ├── .watchmanconfig ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── addon └── index.js ├── app ├── .gitkeep └── initializers │ └── sort-by.js ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── index.js ├── package.json ├── testem.js ├── tests ├── .eslintrc.js ├── 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 │ │ └── targets.js │ └── public │ │ ├── crossdomain.xml │ │ └── robots.txt ├── helpers │ ├── destroy-app.js │ ├── module-for-acceptance.js │ ├── resolver.js │ └── start-app.js ├── index.html ├── test-helper.js └── unit │ └── computed-sortby-test.js ├── vendor └── .gitkeep └── yarn.lock /.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 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | ecmaVersion: 2017, 5 | sourceType: 'module' 6 | }, 7 | extends: 'eslint:recommended', 8 | env: { 9 | browser: true 10 | }, 11 | rules: { 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /.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 | /node_modules 9 | /bower_components 10 | 11 | # misc 12 | /.sass-cache 13 | /connect.lock 14 | /coverage/* 15 | /libpeerconnection.log 16 | npm-debug.log* 17 | testem.log 18 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /bower_components 2 | /config/ember-try.js 3 | /dist 4 | /tests 5 | /tmp 6 | **/.gitkeep 7 | .bowerrc 8 | .editorconfig 9 | .ember-cli 10 | .gitignore 11 | .eslintrc.js 12 | .watchmanconfig 13 | .travis.yml 14 | bower.json 15 | ember-cli-build.js 16 | testem.js 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | - "6" 5 | 6 | sudo: false 7 | 8 | cache: 9 | directories: 10 | - $HOME/.npm 11 | 12 | env: 13 | # we recommend testing LTS's and latest stable release (bonus points to beta/canary) 14 | - EMBER_TRY_SCENARIO=ember-lts-2.4 15 | - EMBER_TRY_SCENARIO=ember-lts-2.8 16 | - EMBER_TRY_SCENARIO=ember-release 17 | - EMBER_TRY_SCENARIO=ember-beta 18 | - EMBER_TRY_SCENARIO=ember-canary 19 | - EMBER_TRY_SCENARIO=ember-default 20 | 21 | matrix: 22 | fast_finish: true 23 | allow_failures: 24 | - env: EMBER_TRY_SCENARIO=ember-canary 25 | 26 | before_install: 27 | - npm config set spin false 28 | - npm install -g phantomjs-prebuilt 29 | - phantomjs --version 30 | 31 | install: 32 | - npm install 33 | 34 | 35 | script: 36 | # Usually, it's ok to finish the test scenario without reverting 37 | # to the addon's original dependency state, skipping "cleanup". 38 | - node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO test --skip-cleanup 39 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ember-computed-sortby Changelog 2 | 3 | ### 0.1.0 (May 4th, 2017) 4 | 5 | - [#3](https://github.com/workmanw/ember-computed-sortby/pull/3) Modernized the addon, upgrading structure and packages. 6 | 7 | ### 0.0.4 (May 4th, 2017) 8 | 9 | - [#2](https://github.com/workmanw/ember-computed-sortby/pull/2) Fixed a bug that was exposed by Ember 2.13. 10 | 11 | ### 0.0.3 (September 14th, 2015) 12 | 13 | - [3ee219085](https://github.com/workmanw/ember-computed-sortby/commit/3ee219085) Removed `sortDefinition` array syntax. Use additional arguments when defining the macro. 14 | 15 | ### 0.0.2 (September 11th, 2015) 16 | 17 | - Initial release 18 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ember-computed-sortby 2 | 3 | Addon to make sorting a little easier for Ember.js when the sort order is immutable. This addon was originally a proof of concept for [emberjs/rfcs#87](https://github.com/emberjs/rfcs/pull/87). However that RFC was rejected, so this will continue to live on as an addon. 4 | 5 | ## Documentation 6 | 7 | A computed property which returns a new sorted array of content from the 8 | a dependent array. The sort order is defined by the second, and any subsequent, 9 | string arguments. Adding a suffix of ':desc' to any of those string arguments 10 | will cause that order to be applied as descending. 11 | 12 | Example: 13 | 14 | ```javascript 15 | import sortBy from 'ember-computed-sortby'; 16 | 17 | let ToDoList = Ember.Object.extend({ 18 | // using standard ascending sort 19 | sortedTodos: sortBy('todos', 'name'), 20 | 21 | // using descending sort 22 | sortedTodosDesc: sortBy('todos', 'name:desc'), 23 | 24 | // using secondary sort 25 | sortedPriority: sortBy('todos', 'priority', 'name') 26 | }); 27 | 28 | let todoList = ToDoList.create({todos: [ 29 | { name: 'Unit Test', priority: 2 }, 30 | { name: 'Documentation', priority: 3 }, 31 | { name: 'Integration Test', priority: 2 }, 32 | { name: 'Release', priority: 1 } 33 | ]}); 34 | 35 | todoList.get('sortedTodos'); // [{ name:'Documentation', priority:3 }, { name: 'Integration Test', priority: 2 }, { name:'Release', priority:1 }, { name:'Unit Test', priority:2 }] 36 | todoList.get('sortedTodosDesc'); // [{ name:'Unit Test', priority:2 }, { name:'Release', priority:1 }, { name: 'Integration Test', priority: 2 }, { name:'Documentation', priority:3 }] 37 | todoList.get('priorityTodos'); // [{ name:'Release', priority:1 }, { name: 'Integration Test', priority: 2 }, { name:'Unit Test', priority:2 }, { name:'Documentation', priority:3 }] 38 | ``` 39 | Method documentation: 40 | ``` 41 | @method sort 42 | @for Ember.computed 43 | @param {String} itemsKey 44 | @param {String} property name(s) to sort on. Append ':desc' to trigger sort to be applied as descending. 45 | @return {Ember.ComputedProperty} computes a new sorted array based on the sort property array 46 | @public 47 | ``` 48 | 49 | ## Installing this addon 50 | 51 | From within your Ember CLI project directory: 52 | ``` 53 | ember install ember-computed-sortby 54 | ``` 55 | 56 | ## Running Tests 57 | 58 | ### Setup 59 | 60 | * `git clone` this repository 61 | * `yarn install` 62 | 63 | ### Testing 64 | 65 | * `ember test` 66 | * `ember test --server` 67 | -------------------------------------------------------------------------------- /addon/index.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | const { assert, get, compare, computed } = Ember; 4 | const a_slice = Array.prototype.slice; 5 | 6 | 7 | /** 8 | A computed property which returns a new sorted array of content from the 9 | a dependent array. The sort order is defined by the second, and any subsequent, 10 | string arguments. Adding a suffix of ':desc' to any of those string arguments 11 | will cause that order to be applied as descending. 12 | 13 | Example: 14 | 15 | ```javascript 16 | import sortBy from 'ember-computed-sortby'; 17 | 18 | let ToDoList = Ember.Object.extend({ 19 | // using standard ascending sort 20 | sortedTodos: sortBy('todos', 'name'), 21 | 22 | // using descending sort 23 | sortedTodosDesc: sortBy('todos', 'name:desc'), 24 | 25 | // using secondary sort 26 | sortedPriority: sortBy('todos', 'priority', 'name') 27 | }); 28 | 29 | let todoList = ToDoList.create({todos: [ 30 | { name: 'Unit Test', priority: 2 }, 31 | { name: 'Documentation', priority: 3 }, 32 | { name: 'Integration Test', priority: 2 }, 33 | { name: 'Release', priority: 1 } 34 | ]}); 35 | 36 | todoList.get('sortedTodos'); // [{ name:'Documentation', priority:3 }, { name: 'Integration Test', priority: 2 }, { name:'Release', priority:1 }, { name:'Unit Test', priority:2 }] 37 | todoList.get('sortedTodosDesc'); // [{ name:'Unit Test', priority:2 }, { name:'Release', priority:1 }, { name: 'Integration Test', priority: 2 }, { name:'Documentation', priority:3 }] 38 | todoList.get('priorityTodos'); // [{ name:'Release', priority:1 }, { name: 'Integration Test', priority: 2 }, { name:'Unit Test', priority:2 }, { name:'Documentation', priority:3 }] 39 | ``` 40 | 41 | @method sort 42 | @for Ember.computed 43 | @param {String} itemsKey 44 | @param {String} property name(s) to sort on. Append ':desc' to trigger sort to be applied as descending. 45 | @return {Ember.ComputedProperty} computes a new sorted array based on the sort property array 46 | @public 47 | */ 48 | export default function(/*itemsKey, sortDefinitions*/) { 49 | let sortDefinitions = a_slice.call(arguments); 50 | let itemsKey = sortDefinitions.shift(0); 51 | 52 | assert('Ember.computed.sortBy expects one or more string arguments provided as the sort definition.', sortDefinitions.length > 0); 53 | sortDefinitions.forEach(s => { 54 | assert('Ember.computed.sortBy expects one or more string arguments provided as the sort definition.', typeof s === 'string'); 55 | }); 56 | 57 | // Split out the sort definitions 58 | sortDefinitions = sortDefinitions.map(p => { 59 | let [prop, direction] = p.split(':'); 60 | direction = direction || 'asc'; 61 | return [prop, direction]; 62 | }); 63 | 64 | // Map out the dependantKeys for the computed macro 65 | let args = sortDefinitions.map(sortDefinition => { 66 | let prop = sortDefinition[0]; 67 | return `${itemsKey}.@each.${prop}`; 68 | }); 69 | 70 | // Push in the actual sorting function 71 | args.push(function() { 72 | let items = itemsKey === '@this' ? this : get(this, itemsKey); 73 | if (items === null || typeof items !== 'object') { return Ember.A(); } 74 | 75 | return Ember.A(items.slice().sort((itemA, itemB) => { 76 | for (let i = 0; i < sortDefinitions.length; ++i) { 77 | let [prop, direction] = sortDefinitions[i]; 78 | let result = compare(get(itemA, prop), get(itemB, prop)); 79 | if (result !== 0) { 80 | return (direction === 'desc') ? (-1 * result) : result; 81 | } 82 | } 83 | return 0; 84 | })); 85 | }); 86 | 87 | return computed.apply(null, args).readOnly(); 88 | } 89 | -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workmanw/ember-computed-sortby/18fbbfed3fcfad3b6532bf68b9b510fb49fb91e4/app/.gitkeep -------------------------------------------------------------------------------- /app/initializers/sort-by.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import sortBy from 'ember-computed-sortby'; 3 | 4 | export default { 5 | name: 'sortBy', 6 | initialize: function() { 7 | Ember.computed.sortBy = function() { 8 | Ember.deprecate('Using `Ember.computed.sortBy` is deprecated. Instead, import it directly using `import sortBy from \'ember-computed-sortby\'`.', false, { 9 | id: 'ember-computed-sortby.global-import', 10 | until: '1.0.0' 11 | }); 12 | return sortBy.apply(null, arguments); 13 | }; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | scenarios: [ 4 | { 5 | name: 'ember-lts-2.4', 6 | bower: { 7 | dependencies: { 8 | 'ember': 'components/ember#lts-2-4' 9 | }, 10 | resolutions: { 11 | 'ember': 'lts-2-4' 12 | } 13 | }, 14 | npm: { 15 | devDependencies: { 16 | 'ember-source': null 17 | } 18 | } 19 | }, 20 | { 21 | name: 'ember-lts-2.8', 22 | bower: { 23 | dependencies: { 24 | 'ember': 'components/ember#lts-2-8' 25 | }, 26 | resolutions: { 27 | 'ember': 'lts-2-8' 28 | } 29 | }, 30 | npm: { 31 | devDependencies: { 32 | 'ember-source': null 33 | } 34 | } 35 | }, 36 | { 37 | name: 'ember-release', 38 | bower: { 39 | dependencies: { 40 | 'ember': 'components/ember#release' 41 | }, 42 | resolutions: { 43 | 'ember': 'release' 44 | } 45 | }, 46 | npm: { 47 | devDependencies: { 48 | 'ember-source': null 49 | } 50 | } 51 | }, 52 | { 53 | name: 'ember-beta', 54 | bower: { 55 | dependencies: { 56 | 'ember': 'components/ember#beta' 57 | }, 58 | resolutions: { 59 | 'ember': 'beta' 60 | } 61 | }, 62 | npm: { 63 | devDependencies: { 64 | 'ember-source': null 65 | } 66 | } 67 | }, 68 | { 69 | name: 'ember-canary', 70 | bower: { 71 | dependencies: { 72 | 'ember': 'components/ember#canary' 73 | }, 74 | resolutions: { 75 | 'ember': 'canary' 76 | } 77 | }, 78 | npm: { 79 | devDependencies: { 80 | 'ember-source': null 81 | } 82 | } 83 | }, 84 | { 85 | name: 'ember-default', 86 | npm: { 87 | devDependencies: {} 88 | } 89 | } 90 | ] 91 | }; 92 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 'use strict'; 3 | 4 | module.exports = function(/* environment, appConfig */) { 5 | return { }; 6 | }; 7 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 3 | 4 | module.exports = function(defaults) { 5 | var app = new EmberAddon(defaults, { 6 | // Add options here 7 | }); 8 | 9 | /* 10 | This build file specifies the options for the dummy test app of this 11 | addon, located in `/tests/dummy` 12 | This build file does *not* influence how the addon or the app using it 13 | behave. You most likely want to be modifying `./index.js` or app's build file 14 | */ 15 | 16 | return app.toTree(); 17 | }; 18 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | name: 'ember-computed-sortby' 6 | }; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-computed-sortby", 3 | "version": "0.1.0", 4 | "description": "Computed property implementation of `Ember.Array.sortBy`.", 5 | "keywords": [ 6 | "ember-addon", 7 | "ember computed" 8 | ], 9 | "license": "MIT", 10 | "author": "", 11 | "directories": { 12 | "doc": "doc", 13 | "test": "tests" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/workmanw/ember-computed-sortby.git" 18 | }, 19 | "scripts": { 20 | "build": "ember build", 21 | "start": "ember server", 22 | "test": "ember try:each" 23 | }, 24 | "dependencies": { 25 | "ember-cli-babel": "^6.0.0" 26 | }, 27 | "devDependencies": { 28 | "broccoli-asset-rev": "^2.4.5", 29 | "ember-cli": "~2.13.0", 30 | "ember-cli-dependency-checker": "^1.3.0", 31 | "ember-cli-eslint": "^3.0.0", 32 | "ember-cli-htmlbars": "^1.1.1", 33 | "ember-cli-htmlbars-inline-precompile": "^0.4.0", 34 | "ember-cli-inject-live-reload": "^1.4.1", 35 | "ember-cli-qunit": "^4.0.0-beta.1", 36 | "ember-cli-shims": "^1.1.0", 37 | "ember-cli-uglify": "^1.2.0", 38 | "ember-disable-prototype-extensions": "^1.1.0", 39 | "ember-load-initializers": "^1.0.0", 40 | "ember-resolver": "^4.0.0", 41 | "ember-source": "~2.13.0", 42 | "loader.js": "^4.2.3" 43 | }, 44 | "engines": { 45 | "node": ">= 4" 46 | }, 47 | "ember-addon": { 48 | "configPath": "tests/dummy/config" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | "test_page": "tests/index.html?hidepassed", 4 | "disable_watching": true, 5 | "launch_in_ci": [ 6 | "PhantomJS" 7 | ], 8 | "launch_in_dev": [ 9 | "PhantomJS", 10 | "Chrome" 11 | ] 12 | }; 13 | -------------------------------------------------------------------------------- /tests/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | embertest: true 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Resolver from './resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from './config/environment'; 5 | 6 | let App; 7 | 8 | Ember.MODEL_FACTORY_INJECTIONS = true; 9 | 10 | App = Ember.Application.extend({ 11 | modulePrefix: config.modulePrefix, 12 | podModulePrefix: config.podModulePrefix, 13 | Resolver 14 | }); 15 | 16 | loadInitializers(App, config.modulePrefix); 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workmanw/ember-computed-sortby/18fbbfed3fcfad3b6532bf68b9b510fb49fb91e4/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workmanw/ember-computed-sortby/18fbbfed3fcfad3b6532bf68b9b510fb49fb91e4/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workmanw/ember-computed-sortby/18fbbfed3fcfad3b6532bf68b9b510fb49fb91e4/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |