├── .editorconfig ├── .ember-cli ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .travis.yml ├── .watchmanconfig ├── CHANGELOG.md ├── LICENSE ├── LICENSE.md ├── README.md ├── addon-test-support └── qunit.js ├── addon ├── model.js ├── models │ ├── filtered-record-array.js │ ├── record-array.js │ └── record-proxy.js ├── store.js └── uuid.js ├── app └── services │ └── simple-store.js ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── index.js ├── package.json ├── testem.js ├── tests ├── .eslintrc.js ├── acceptance │ ├── a-defaults-test.js │ ├── a-test.js │ ├── arrays-test.js │ ├── b-defaults-test.js │ ├── b-test.js │ ├── delete-object-test.js │ ├── edit-test.js │ ├── filters-test.js │ ├── isnone-test.js │ ├── relationships-test.js │ └── x-defaults-test.js ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ ├── delete-object.js │ │ │ └── relationships.js │ │ ├── helpers │ │ │ ├── .gitkeep │ │ │ └── is-equal.js │ │ ├── index.html │ │ ├── models │ │ │ ├── .gitkeep │ │ │ ├── custom-key.js │ │ │ ├── foo.js │ │ │ ├── robot.js │ │ │ ├── role.js │ │ │ ├── user.js │ │ │ ├── wat.js │ │ │ ├── wow.js │ │ │ ├── zap.js │ │ │ └── zing.js │ │ ├── resolver.js │ │ ├── router.js │ │ ├── routes │ │ │ ├── .gitkeep │ │ │ ├── arrays.js │ │ │ ├── custom-key.js │ │ │ ├── defaulta.js │ │ │ ├── defaultb.js │ │ │ ├── defaultx.js │ │ │ ├── delete-object.js │ │ │ ├── edit.js │ │ │ ├── filters.js │ │ │ ├── isnone.js │ │ │ ├── relationships.js │ │ │ ├── robots.js │ │ │ └── wat.js │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ ├── application.hbs │ │ │ ├── arrays.hbs │ │ │ ├── custom-key.hbs │ │ │ ├── defaulta.hbs │ │ │ ├── defaultb.hbs │ │ │ ├── defaultx.hbs │ │ │ ├── delete-object.hbs │ │ │ ├── edit.hbs │ │ │ ├── filters.hbs │ │ │ ├── isnone.hbs │ │ │ ├── relationships.hbs │ │ │ ├── robots.hbs │ │ │ └── wat.hbs │ ├── config │ │ ├── environment.js │ │ └── targets.js │ └── public │ │ └── robots.txt ├── helpers │ ├── .gitkeep │ ├── destroy-app.js │ ├── module-for-acceptance.js │ ├── qunit.js │ ├── registration.js │ ├── resolver.js │ └── start-app.js ├── index.html ├── test-helper.js └── unit │ ├── arrays-test.js │ ├── default-attr-test.js │ ├── model-test.js │ ├── record-array-test.js │ ├── relationship-test.js │ ├── service-test.js │ └── store-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 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | 12 | # misc 13 | /coverage/ 14 | 15 | # ember-try 16 | /.node_modules.ember-try/ 17 | -------------------------------------------------------------------------------- /.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 | 'ember-cli-build.js', 24 | 'index.js', 25 | 'testem.js', 26 | 'blueprints/*/index.js', 27 | 'config/**/*.js', 28 | 'tests/dummy/config/**/*.js' 29 | ], 30 | excludedFiles: [ 31 | 'addon/**', 32 | 'addon-test-support/**', 33 | 'app/**', 34 | 'tests/dummy/app/**' 35 | ], 36 | parserOptions: { 37 | sourceType: 'script', 38 | ecmaVersion: 2015 39 | }, 40 | env: { 41 | browser: false, 42 | node: true 43 | }, 44 | plugins: ['node'], 45 | rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, { 46 | // add your custom rules and overrides for node files here 47 | }) 48 | } 49 | ] 50 | }; 51 | -------------------------------------------------------------------------------- /.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 | /.sass-cache 13 | /connect.lock 14 | /coverage/ 15 | /libpeerconnection.log 16 | /npm-debug.log* 17 | /testem.log 18 | /yarn-error.log 19 | 20 | # ember-try 21 | /.node_modules.ember-try/ 22 | /bower.json.ember-try 23 | /package.json.ember-try 24 | -------------------------------------------------------------------------------- /.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 | .eslintrc.js 11 | .gitignore 12 | .watchmanconfig 13 | .travis.yml 14 | bower.json 15 | ember-cli-build.js 16 | testem.js 17 | yarn.lock 18 | 19 | # ember-try 20 | .node_modules.ember-try/ 21 | bower.json.ember-try 22 | package.json.ember-try 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | - "6" 5 | 6 | sudo: false 7 | dist: trusty 8 | 9 | addons: 10 | chrome: stable 11 | 12 | cache: 13 | yarn: true 14 | 15 | env: 16 | - EMBER_TRY_SCENARIO=ember-lts-2.12 17 | - EMBER_TRY_SCENARIO=ember-2.18.X 18 | - EMBER_TRY_SCENARIO=ember-3.1.X 19 | - EMBER_TRY_SCENARIO=ember-3.3.X 20 | - EMBER_TRY_SCENARIO=ember-3.4.X 21 | - EMBER_TRY_SCENARIO=ember-3.7.X 22 | 23 | before_install: 24 | - curl -o- -L https://yarnpkg.com/install.sh | bash 25 | - export PATH=$HOME/.yarn/bin:$PATH 26 | 27 | install: 28 | - yarn install --no-lockfile --non-interactive 29 | 30 | script: 31 | - yarn lint:js 32 | - node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO --skip-cleanup 33 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ember-cli-simple-store Changelog 2 | ============================== 3 | 4 | 5.9.4 5 | ----- 6 | 7 | * [DEPENDENCY]: fix for ember v3.4+ ([#91](https://github.com/toranb/ember-cli-simple-store/pull/91)) 8 | 9 | 5.9.3 10 | ----- 11 | 12 | * [DEPENDENCY]: upgrade to ember v3.1.1 13 | 14 | 5.9.2 15 | ----- 16 | 17 | * [WORKAROUND]: ember v3.1 compatability with proxy 18 | 19 | 5.9.1 20 | ----- 21 | 22 | * [DEPENDENCY]: update ember-runtime-enumerable-includes-polyfill to 2.1.0 23 | 24 | 5.9.0 25 | ----- 26 | 27 | * [DEPENDENCY]: upgrade to 3.0 ([#83](https://github.com/toranb/ember-cli-simple-store/pull/83)) 28 | 29 | * [UPDATE]: removed ember-uuid dependency ([commit](https://github.com/toranb/ember-cli-simple-store/commit/5c0fff4411b0641953882d11cb815bd8a0f6a58f)) 30 | 31 | 5.8.0 32 | ----- 33 | 34 | * [UPDATE]: testing updates ([#82](https://github.com/toranb/ember-cli-simple-store/pull/82)) 35 | 36 | 5.7.0 37 | ----- 38 | 39 | * [DEPENDENCY]: upgrade to ember 2.15.0 ([#80](https://github.com/toranb/ember-cli-simple-store/pull/80)) 40 | 41 | * [CLEANUP]: ember-codemod based on #176 RFC ([#80](https://github.com/toranb/ember-cli-simple-store/pull/80)) 42 | 43 | 5.6.0 44 | ----- 45 | 46 | * [DEPENDENCY]: upgrade to ember-cli v2.13.2 47 | ([#78](https://github.com/toranb/ember-cli-simple-store/pull/78)) 48 | 49 | 5.5.0 50 | ----- 51 | 52 | * [BUILD]: update to ember-cli v2.10.1 53 | ([#76](https://github.com/toranb/ember-cli-simple-store/pull/76)) 54 | 55 | * [BUG]: Change how dependent keys are registered on isDirty computed property 56 | ([#75](https://github.com/toranb/ember-cli-simple-store/pull/75)) 57 | 58 | * [EMBER]: upgraded to ember 1.13.13 59 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/365c30a2cbc731e33d9ea9dcfd3f28892f0ab2ff)) 60 | 61 | * [DEPRECATION]: use factoryFor 62 | ([#73](https://github.com/toranb/ember-cli-simple-store/pull/73)) 63 | 64 | 5.4.0 65 | ----- 66 | 67 | * [DOCS]: updated README to reflect pushArray api 68 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/b560d78f34751c7a483a435827549275dca152d5)) 69 | 70 | * [FEATURE]: added pushArray api 71 | ([#71](https://github.com/toranb/ember-cli-simple-store/pull/71)) 72 | 73 | 5.3.0 74 | ----- 75 | 76 | * [DEPENDENCY]: updated ember-getowner-polyfill 77 | ([#70](https://github.com/toranb/ember-cli-simple-store/pull/70)) 78 | 79 | * [UPDATE]: equal function to work with jquery && jquery 3.0 and above 80 | ([#68](https://github.com/toranb/ember-cli-simple-store/pull/68)) 81 | 82 | 5.2.0 83 | ----- 84 | 85 | * [BUG]: On willDestroy event set object/array refs are null 86 | ([#65](https://github.com/toranb/ember-cli-simple-store/pull/65)) 87 | 88 | 5.1.0 89 | ----- 90 | 91 | * [DEP WARNINGS]: fix w/ enumerable contains polyfill 92 | ([#64](https://github.com/toranb/ember-cli-simple-store/pull/64)) 93 | 94 | 95 | 5.0.0 96 | ----- 97 | 98 | * [CLEANUP]: removed find with filter dep warning 99 | ([#62](https://github.com/toranb/ember-cli-simple-store/pull/62)) 100 | 101 | * [FEATURE]: remove and clear invoke destroy on the record 102 | ([#61](https://github.com/toranb/ember-cli-simple-store/pull/61)) 103 | 104 | 105 | 4.3.0 106 | ----- 107 | 108 | * [BUG]: Fix coerceId function and push record with coerced ids 109 | ([#59](https://github.com/toranb/ember-cli-simple-store/pull/59)) 110 | 111 | 112 | 4.2.0 113 | ----- 114 | 115 | * [BUG]: Switch the names of the properties on a record proxy to avoid collisions with defined values 116 | ([#57](https://github.com/toranb/ember-cli-simple-store/pull/57)) 117 | 118 | 119 | 4.1.0 120 | ----- 121 | 122 | * [FEATURE]: Add support for custom id properties 123 | ([#53](https://github.com/toranb/ember-cli-simple-store/pull/53)) 124 | 125 | 126 | 4.0.1 127 | ----- 128 | 129 | * [DOCS]: added ember 2.4 example app to the readme 130 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/71f1b02517291b88738deb5d92f6d891d595802e)) 131 | 132 | * [DOCS]: Removing obsolete installation documentation 133 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/38e4a82c680662d65cbe3e556e065b162adf9919)) 134 | 135 | 136 | 4.0.0 137 | ----- 138 | 139 | * [DOCS]: updated the readme to be v4.0.0 friendly 140 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/1c7bbebf83a0cebca9bc5389f89670a3be6cb4dd)) 141 | 142 | * [DEPRECATION]: added find with filter api dep warning for 3rd arg 143 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/f0ac6db144f3597876ab3fdb203e2909f47c5ddd)) 144 | 145 | * [REFACTOR]: removed the unsubscribe api (never released) in favor of destroy 146 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/d1eeeb62f628b70d7a396178df6beb293809087c)) 147 | 148 | * [REFACTOR]: Unsubscribe on destroy of record array 149 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/22908b22932e99772fc87ce66b5831c4dc48788c)) 150 | 151 | * [REFACTOR]: Consider better compatability with ember-data 152 | ([#45](https://github.com/toranb/ember-cli-simple-store/issues/45)) 153 | 154 | * [REFACTOR]: Expose Store as an Ember Service 155 | ([#44](https://github.com/toranb/ember-cli-simple-store/issues/44)) 156 | 157 | * [REFACTOR]: Adding record proxy type 158 | ([#43](https://github.com/toranb/ember-cli-simple-store/issues/43)) 159 | 160 | * [REFACTOR]: Fixing run loop scheduling 161 | ([#40](https://github.com/toranb/ember-cli-simple-store/issues/40)) 162 | 163 | * [REFACTOR]: Removing bind and jQuery dep from store 164 | ([#39](https://github.com/toranb/ember-cli-simple-store/issues/39)) 165 | 166 | * [REFACTOR]: Creating types for record array proxy 167 | ([#37](https://github.com/toranb/ember-cli-simple-store/issues/37)) 168 | 169 | * [FEATURE]: v4.0.0 to improve performance 170 | ([#38](https://github.com/toranb/ember-cli-simple-store/issues/38)) 171 | 172 | 173 | 3.6.0 174 | ----- 175 | 176 | * [DOCS]: Update README.md 177 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/d7bc3808325785cd61f80707fb4db1e41c7e1d68)) 178 | 179 | * [FEATURE]: Conditionally disable initializer 180 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/edf2d41b60ab2959224146dee4169e9805189a7a)) 181 | 182 | * [DEPENDENCY]: Lock jQuery to 1.11.3 183 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/16de403f8b2eb7e3f115a83f8ff8f6402d02243c)) 184 | 185 | * [DOCS]: Fix readme examples 186 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/2de430133dd9345de0034124880bf1c42fcef451)) 187 | 188 | 189 | 3.5.0 190 | ----- 191 | 192 | * [REFACTOR]: removed the duplicate ArrayProxy (filter w/ object) 193 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/454e4e4e2ad7cd3cd5faf2ea76d71685af5cc1dd)) 194 | 195 | 196 | 3.4.0 197 | ----- 198 | 199 | * [DEPENDENCY]: more getOwner cleanup 200 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/3f1b88b2492eaf81ecba8bd67ce465d18a37c9f6)) 201 | 202 | 203 | 3.3.0 204 | ----- 205 | 206 | * [DEPENDENCY]: using getOwner to support ember 2.3 207 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/dc6ac20e82ecb1750e5c23041075f7a2b2893512)) 208 | 209 | * [REFACTOR]: cleaning up the computed properties 210 | ([#27](https://github.com/toranb/ember-cli-simple-store/pull/27)) 211 | 212 | * [DEPENDENCY]: update ember-cli and all other dependencies 213 | ([#26](https://github.com/toranb/ember-cli-simple-store/pull/26)) 214 | 215 | 216 | 3.2.0 217 | ----- 218 | 219 | * [TEST]: added unit and acceptance tests for isPrimed (starting null) 220 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/1e1e6d0231c1a3a34974d06ffe352ffc5215a2d5)) 221 | 222 | * [FEATURE]: replaced === undefined operator with Ember.isNone 223 | ([#21](https://github.com/toranb/ember-cli-simple-store/pull/21)) 224 | 225 | 226 | 3.1.0 227 | ----- 228 | 229 | * [DEPENDENCY]: removing container from the initializer for ember 2.1 230 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/1f68d63e445a83b9298d03c9bbf95af7b93680d7)) 231 | 232 | 233 | 3.0.0 234 | ----- 235 | 236 | * [FEATURE]: array proxy push returns record 237 | ([#19](https://github.com/toranb/ember-cli-simple-store/pull/19)) 238 | 239 | * [FEATURE]: array proxy now supports remove 240 | ([#18](https://github.com/toranb/ember-cli-simple-store/pull/18)) 241 | 242 | * [TEST]: added another array proxy push test to prove type is respected 243 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/f4353f9933983622d825438c94d662e955d95b3c)) 244 | 245 | * [FEATURE]: find(all) now returns array proxy 246 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/f8e8fa725237804b0f2ca972c12a91d77a226c33)) 247 | 248 | 249 | 2.4.0 250 | ----- 251 | 252 | * [FEATURE]: bound array proxy now supports push 253 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/4a73bd9bb5147cc17aedfdc22cbdd36b6c7b9e2f)) 254 | 255 | * [DEPENDENCY]: added es5Shim to test phantomJS w/ bind 256 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/4f46a392b3be0ec93864342ba2edddbd3430e293)) 257 | 258 | 259 | 2.3.0 260 | ----- 261 | 262 | * [BUG]: array attrs are now compared accurately 263 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/fd4b7dd7a8210cc4eb4da324a4a61e162ccf2ab7)) 264 | 265 | 266 | 2.2.0 267 | ----- 268 | 269 | * [DEPENDENCY]: updated ember-cli to 0.2.7 270 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/a4642351a3a93a264ad24710d0bcef70f91243db)) 271 | 272 | * [DEPENDENCY]: bumping bower version of ember to 1.12.1 273 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/d8683cd20a14fe19ccbe32f0842bea5d0fab0e2d)) 274 | 275 | 276 | 2.1.0 277 | ----- 278 | 279 | * [FEATURE]: added isNotDirty to allow for easy inverse template usage 280 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/b28514a9f5504f3ebf2c28afd253846b7bbe527e)) 281 | 282 | 283 | 2.0.0 284 | ----- 285 | 286 | * [FEATURE]: attr now supports default value for dirty tracking 287 | ([#13](https://github.com/toranb/ember-cli-simple-store/pull/13)) 288 | 289 | 290 | 1.0.4 291 | ----- 292 | 293 | * [REFACTOR] removing prototype extensions 294 | ([#14](https://github.com/toranb/ember-cli-simple-store/pull/14)) 295 | 296 | * [DEPENDENCY] getting new dependencies and bumping versions 297 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/9f2e9b867edf99a4b0b49e087bf4f8408768e4a1)) 298 | 299 | 300 | 1.0.3 301 | ----- 302 | 303 | * [BUG] when setting field to empty string, model is not dirty 304 | ([#12](https://github.com/toranb/ember-cli-simple-store/pull/12)) 305 | 306 | 307 | 1.0.2 308 | ----- 309 | 310 | * [TEST]: adding test for save then immediate rollback 311 | ([#10](https://github.com/toranb/ember-cli-simple-store/pull/10)) 312 | 313 | * [BUG]: rollback is now idempotent 314 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/2d0cc9417b1bee7f674923906953045e8dc61a97)) 315 | 316 | * [PERIPHERAL]: updating to ember 1.11.3 317 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/aa572e44b1a9c057726ea9d5a5782e59cb7e5727)) 318 | 319 | 320 | 1.0.1 321 | ----- 322 | 323 | * [BUG]: isDirty should remain when undefined attr set to empty str 324 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/944f9c56d4282c6d9d887128ca9a8f45d81998fe)) 325 | 326 | 327 | 1.0.0 328 | ----- 329 | 330 | * [DOCS]: added changelog 331 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/d93349710094cd9fdc7cfb5775582f1cb36a4cfe)) 332 | 333 | * [DOCS]: updated the readme to show more example apps 334 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/0522f17209aed395de42e15175c8f7293f3944dd)) 335 | 336 | * [FEATURE]: findOne is now computed (w/ proxy'd methods) 337 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/b89b1808e45986e2bf9f29fea09f4df8cf5e6c12)) 338 | 339 | * [FEATURE]: findById is now computed (w/ proxy'd methods) 340 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/ab9c7dabe6da3efaa62480b96ccaecd1069f1e8b)) 341 | 342 | 343 | 0.9.3 344 | ----- 345 | 346 | * [DOCS]: updated readme with more modern examples 347 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/daa1b037fe550604a80bc92ab775ea563960157f)) 348 | 349 | * [TEST]: adding tests to show behavior when object with no "id" is pushed into store 350 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/212ba12cedda8721859d78e011405c6a6c049990)) 351 | 352 | 0.9.2 353 | ----- 354 | 355 | * [DOCS]: simplified the store how-to in the readme 356 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/39b75b67fa68e7f8e7c42058c8c00d28fa9d2fd5)) 357 | 358 | 0.9.1 359 | ----- 360 | 361 | * [FEATURE]: Dirty tracking 362 | ([#7](https://github.com/toranb/ember-cli-simple-store/pull/7)) 363 | 364 | * [REFACTOR]: fixing individual attrIsDirty check 365 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/558db91b818e22133f72d617821c1a402ea41d0b)) 366 | 367 | * [ENHANCEMENT]: updated model to store isPrimed separate from isDirty 368 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/5a861b997c257fceceff01ad078330c428c60086)) 369 | 370 | * [REFACTOR]: simplifying the isDirty computed property 371 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/cfdcdf9c3b1f30d1ed470db6a9dfe62b4b756123)) 372 | 373 | * [TEST]: adding test to show working with ints 374 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/e8277b130fdb390c9fa0f6c06753fd82679f8627)) 375 | 376 | * [ENHANCEMENT]: extracting isDirty property to computed property to be managed by model 377 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/52399c757053d97a26a24e712ccb0d483bbe8e9f)) 378 | 379 | * [DOCS]: added downloads and emberobserver score to the readme 380 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/aff3a5680962f23498afb0fece8277cac166370b)) 381 | 382 | 0.7.1 383 | ----- 384 | 385 | * [PERIPHERAL]: updating unit tests to avoid container dep warning 386 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/1fcc7001635f30907d14cf9ded7e60b8178774f7)) 387 | 388 | 0.7.0 389 | ----- 390 | 391 | * [FEATURE]: added the ability to pass a custom filter func to find 392 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/20822288fbf0c37879659f6ef9f7149bd1eb3e70)) 393 | 394 | * [BUILD]: setting up travis to run 0.12 and 0.10 versions of node 395 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/ab74ad171d0b8bd4531c8908e4c98e3feba1e973)) 396 | 397 | 0.6.2 398 | ----- 399 | 400 | * [DEPENDENCY]: upgrading ember to 1.11 beta 5 401 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/9e5ea860c89588ae5e5f5437ffa8315ca02174d9)) 402 | 403 | * [DEPENDENCY]: upgrading ember-cli to 0.2.0 404 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/27bee82aad7b1b52161e695564ece7461df120a6)) 405 | 406 | * [PERIPHERAL]: Updating package.json and bumping the version of ember-cli 407 | ([#2](https://github.com/toranb/ember-cli-simple-store/pull/2)) 408 | 409 | * [DOC]: updating the documentation for the new clear API 410 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/e0b976601041eba69eaa99afaa405482b4e3b7c9)) 411 | 412 | 0.6.1 413 | ----- 414 | 415 | * [DEPENDENCY]: upgrading ember-cli to 0.1.15 416 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/2d9944bb91b2770f3d175742947fabe0655c2273)) 417 | 418 | * [PERIPHERAL]: updating package.json 419 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/cae91b9d9a0e62751c55237e22a60219cc7912a5)) 420 | 421 | * [FEATURE]: Adding the ability to clear all types from store 422 | ([#1](https://github.com/toranb/ember-cli-simple-store/pull/1)) 423 | 424 | * [REFACTOR]: changes based on PR comments 425 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/9be10f8bdea08450a42eaec44250bb9ddc9dc484)) 426 | 427 | 0.6.0 428 | ----- 429 | 430 | * [ENHANCEMENT]: added the ability to remove all objects 431 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/25a6ae0d308e011b7f385de3aebd0ffc32153c19)) 432 | 433 | 0.5.0 434 | ----- 435 | 436 | * [FEATURE]: priming a model attr will not flip the dirty bit 437 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/c537a5858c2ad518e44f7a05c2500603e237df63)) 438 | 439 | 440 | 0.4.1 441 | ----- 442 | 443 | * [CLEANUP]: removed unnecessary return statement 444 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/c0e16247b2917e5ba81f9243b5d21a4ffdd17e47)) 445 | 446 | 0.4.0 447 | ----- 448 | 449 | * [FEATURE]: updated the isDirty api for property based access 450 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/9c198d764567678665b1d63ebee2383ba48008e0)) 451 | 452 | 0.3.0 453 | ----- 454 | 455 | * [DOCS]: added dirty tracking example app to the readme 456 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/081cd6a3450a987ae13c1ba21b6513aeb5fc80ae)) 457 | 458 | * [FEATURE]: added dirty tracking at the property level 459 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/5b0801263e73ee8ecc684200a0c93c000ffa3763)) 460 | 461 | 0.2.1 462 | ----- 463 | 464 | * [BUG]: fixed the global state leak found in the attr 465 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/b812ccacadae665226a3db7b9b88652754661772)) 466 | 467 | 0.2.0 468 | ----- 469 | 470 | * [FEATURE]: added dirty tracking attr and model 471 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/0f61e0a0cc5be916107b88b5ac918aa630130496)) 472 | 473 | 0.1.1 474 | ----- 475 | 476 | * [BUG]: updated initializer name to be store/removed ember-data 477 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/d16e84e97303223dace0add7235a76f3d09ea4f9)) 478 | 479 | * [DOCS]: updated the example app url 480 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/737eb7267fee5a3c4d17a1ad627537f4d4ae492f)) 481 | 482 | 0.1.0 483 | ----- 484 | 485 | * [FEATURE]: added findOne api 486 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/f7d35b9ce02ce65a633af2e1ec4092e0af6c5b96)) 487 | 488 | * [DOCS]: updated the readme to fix a formatting issue 489 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/df7eb0f4f40cc1c9fa1c35da0258bff20dd276e0)) 490 | 491 | * [DOCS]: updated the readme to further define the api documentation 492 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/36a5aa5e3179eaa777b569766ae8c1c1c38b3172)) 493 | 494 | * [DOCS]: updated the readme to fix travis-ci build status image 495 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/08c65db8be2b9e9c08192c48a96d9c66e1458c3f)) 496 | 497 | * [DOCS]: updated the readme to show the api is 4 methods 498 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/3ca803e6c5e43a776343e70d2951f922b849c5cd)) 499 | 500 | * [DOCS]: updated the readme to call out that you must remove ember-data 501 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/4f0d6951c321c021fbe3f60cf072e4a4cebdae8b)) 502 | 503 | * [INIT]: init addon commit 504 | ([commit](https://github.com/toranb/ember-cli-simple-store/commit/1d56e42710f20d1ea06d5d2c8cb891329d8d9d55)) 505 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2015 Toran Billups http://toranbillups.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 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-cli-simple-store 2 | 3 | [![Build Status][]](https://travis-ci.org/toranb/ember-cli-simple-store) 4 | [![Code Climate][climate-badge]][climate] 5 | [![Downloads](https://img.shields.io/npm/dm/ember-cli-simple-store.svg)](https://www.npmjs.com/package/ember-cli-simple-store) 6 | [![Score](http://emberobserver.com/badges/ember-cli-simple-store.svg)](http://emberobserver.com/addons/ember-cli-simple-store) 7 | [![npm package][npm-badge]][npm] 8 | 9 | ## Description 10 | 11 | [ember-cli][] addon that provides a simple identity map for [ember.js][] web applications 12 | 13 | ## Installation 14 | 15 | ``` 16 | ember install ember-cli-simple-store 17 | ``` 18 | 19 | ## You get 6 methods: push/pushArray/remove/find/findOne/clear 20 | 21 | ```js 22 | //create or update person model 23 | 24 | simpleStore.push("person", {id: 1, name: "toran"}); 25 | ``` 26 | 27 | ```js 28 | //create or update multiple person models - observers are notified only once all models have been created or updated 29 | 30 | simpleStore.pushArray("person", [{id: 1, name: "john"}, {id: 2, name: "jane"}]); 31 | ``` 32 | 33 | ```js 34 | //remove person model with id=123 35 | 36 | simpleStore.remove("person", 123); 37 | ``` 38 | 39 | ```js 40 | //find all person models 41 | 42 | simpleStore.find("person"); 43 | ``` 44 | 45 | ```js 46 | //find a single person model with id=123 47 | 48 | simpleStore.find("person", 123); 49 | ``` 50 | 51 | ```js 52 | //find all person models with account_id=789 53 | 54 | simpleStore.find("person", {account_id: 789}); 55 | ``` 56 | 57 | ```js 58 | //find all person models with name toran and salary > 100 59 | 60 | var filter = function(person) { 61 | var name = person.get("name"); 62 | var salary = person.get("salary"); 63 | return name === "toran" && salary > 100; 64 | } 65 | simpleStore.find("person", filter); 66 | ``` 67 | 68 | ```js 69 | //find the first person model 70 | 71 | simpleStore.findOne("person"); 72 | ``` 73 | 74 | ```js 75 | //clear the entire identity map of all person models 76 | 77 | simpleStore.clear("person"); 78 | ``` 79 | 80 | ```js 81 | //clear the entire identity map of all models 82 | 83 | simpleStore.clear(); 84 | ``` 85 | 86 | ## Using the store by example 87 | 88 | Below I'll show how you can use the store with a simple ember object to find/add/remove/update 89 | 90 | The full example below relies on a small xhr addon [PromiseMixin][] 91 | 92 | ```js 93 | import Ember from "ember"; 94 | import PromiseMixin from "ember-promise/mixins/promise"; 95 | 96 | var PersonRepository = Ember.Object.extend({ 97 | simpleStore: Ember.inject.service(), 98 | find() { 99 | var simpleStore = this.get("simpleStore"); 100 | return PromiseMixin.xhr("/api/people/", "GET").then(function(response) { 101 | response.forEach(function(person) { 102 | simpleStore.push("person", person); 103 | }); 104 | return simpleStore.find("person"); 105 | }); 106 | }, 107 | findById(id) { 108 | var simpleStore = this.get("simpleStore"); 109 | return simpleStore.find("person", id); 110 | }, 111 | insert(person) { 112 | var simpleStore = this.get("simpleStore"); 113 | var hash = {data: JSON.stringify(person)}; 114 | return new Ember.RSVP.Promise(function(resolve,reject) { 115 | return PromiseMixin.xhr("/api/people/", "POST", hash).then(function(persisted) { 116 | var inserted = simpleStore.push("person", persisted); 117 | resolve(inserted); 118 | }, function(err) { 119 | reject(err); 120 | }); 121 | }); 122 | }, 123 | update(person) { 124 | var person_id = person.get("id"); 125 | var hash = {data: JSON.stringify(person)}; 126 | var endpoint = "/api/people/%@/".fmt(person_id); 127 | return PromiseMixin.xhr(endpoint, "PUT", hash); 128 | }, 129 | remove(person) { 130 | var simpleStore = this.get("simpleStore"); 131 | var person_id = person.get("id"); 132 | var endpoint = "/api/people/%@/".fmt(person_id); 133 | return new Ember.RSVP.Promise(function(resolve,reject) { 134 | return PromiseMixin.xhr(endpoint, "DELETE").then(function(arg) { 135 | simpleStore.remove("person", person_id); 136 | resolve(arg); 137 | }, function(err) { 138 | reject(err); 139 | }); 140 | }); 141 | } 142 | }); 143 | 144 | export default PersonRepository; 145 | ``` 146 | 147 | ## What about relationship support? 148 | 149 | A one-to-many example might look something like this 150 | 151 | ```js 152 | import Ember from 'ember'; 153 | 154 | export default Ember.Object.extend({ 155 | simpleStore: Ember.inject.service(), 156 | role: Ember.computed.alias('belongs_to.firstObject'), 157 | belongs_to: Ember.computed(function() { 158 | let userId = this.get('id'); 159 | let simpleStore = this.get('simpleStore'); 160 | let filter = function(role) { 161 | let users = role.get('users'); 162 | return Ember.$.inArray(userId, users) > -1; 163 | }; 164 | return simpleStore.find('role', filter); 165 | }) 166 | }); 167 | 168 | //a test to show how this api works from the outside 169 | 170 | test('role property returns associated model or undefined', function(assert) { 171 | let user = simpleStore.push('user', {id: 1}); 172 | simpleStore.push('role', {id: 2, name: 'Admin', users: [1]}); 173 | let role = user.get('role'); 174 | assert.equal(role.get('id'), 2); 175 | assert.equal(role.get('name'), 'Admin'); 176 | role.set('users', []); 177 | role = user.get('role'); 178 | assert.equal(role, undefined); 179 | }); 180 | ``` 181 | 182 | ## What about dirty tracking? 183 | 184 | If you want the ability to track if your model is dirty use the attr for each field and the Model to get save/rollback 185 | 186 | ```js 187 | import { attr, Model } from "ember-cli-simple-store/model"; 188 | 189 | var Person = Model.extend({ 190 | firstName: attr(), 191 | lastName: attr(), 192 | fullName: function() { 193 | var first = this.get("firstName"); 194 | var last = this.get("lastName"); 195 | return first + " " + last; 196 | }.property("firstName", "lastName") 197 | }); 198 | 199 | //save your object to reset isDirty 200 | var person = Person.create({id: 1, firstName: "x", lastName: "y"}); 201 | person.set("firstName", "toran"); 202 | person.save(); 203 | 204 | //rollback your object to reset isDirty and restore it 205 | person.set("firstName", "foobar"); 206 | person.rollback(); 207 | ``` 208 | 209 | If you want to know if an individual property isDirty you can ask like so 210 | 211 | ```js 212 | person.get("firstNameIsDirty"); //undefined 213 | person.set("firstName", "foobar"); 214 | person.get("firstNameIsDirty"); //true 215 | ``` 216 | 217 | For new forms that start with undefined properties you can define the default state for isDirty. Example: you have a model that is bound to a form with a checkbox input. The create form starts with a new model so each bound property is undefined. If the user decides to check the bound checkbox (setting the value to true) and then decides to uncheck it (setting the value to false) you would expect the form is not dirty - but because undefined !== false you find the model is dirty. To prevent this behavior set a default value for dirty tracking on the models attr like so. 218 | 219 | ```js 220 | var Animal = Model.extend({ 221 | omnivore: attr(false) 222 | }); 223 | 224 | var animal = Animal.create(); 225 | 226 | animal.get("omnivore"); //undefined 227 | animal.get("isDirty"); //false 228 | animal.set("omnivore", true); 229 | animal.get("isDirty"); //true 230 | animal.set("omnivore", false); 231 | animal.get("isDirty"); //false 232 | ``` 233 | 234 | ## What about custom primary keys? 235 | 236 | By default, simple store will use the 'id' property on your records as the primary key. If you wish to override this, reopen your 237 | model class and add a `primaryKey` attribute. 238 | 239 | ```js 240 | var Food = Ember.Object.extend({ 241 | food_id: null, 242 | calories: null 243 | }); 244 | 245 | Food.reopenClass({ 246 | primaryKey: 'food_id' 247 | }); 248 | 249 | simpleStore.push('food', { 250 | food_id: 5, 251 | calories: 500 252 | }); 253 | 254 | // Returns the pushed record 255 | simpleStore.find('food', 5); 256 | ``` 257 | 258 | ## Example applications 259 | 260 | **Simplest example with the least amount of complexity (tests included)** 261 | 262 | https://github.com/toranb/kanban-board-without-ember-data 263 | 264 | **Simple example running ember 2.4** 265 | 266 | https://github.com/toranb/ember-2-skeleton-app 267 | 268 | **Async example that will paint right away (loading section included w/ tests)** 269 | 270 | https://github.com/toranb/async-kanban-board-store-example 271 | 272 | **Async example with relationships (loading section included w/ tests)** 273 | 274 | https://github.com/toranb/async-kanban-with-relationships-store-example 275 | 276 | **Dirty tracking example with both save and rollback** 277 | 278 | https://github.com/toranb/ember-cli-store-dirty-tracking-example 279 | 280 | ## Running the unit tests 281 | 282 | npm install 283 | ember test 284 | 285 | ## License 286 | 287 | Copyright © 2015 Toran Billups http://toranbillups.com 288 | 289 | Licensed under the MIT License 290 | 291 | 292 | [Build Status]: https://travis-ci.org/toranb/ember-cli-simple-store.svg?branch=master 293 | [ember-cli]: http://www.ember-cli.com/ 294 | [ember.js]: http://emberjs.com/ 295 | [PromiseMixin]: https://github.com/toranb/ember-promise 296 | 297 | [npm-badge]: https://img.shields.io/npm/v/ember-cli-simple-store.svg?style=flat-square 298 | [npm]: https://www.npmjs.org/package/ember-cli-simple-store 299 | 300 | [climate-badge]: https://codeclimate.com/github/toranb/ember-cli-simple-store/badges/gpa.svg 301 | [climate]: https://codeclimate.com/github/toranb/ember-cli-simple-store 302 | -------------------------------------------------------------------------------- /addon-test-support/qunit.js: -------------------------------------------------------------------------------- 1 | import { run } from '@ember/runloop'; 2 | import { test as qunitTest } from 'qunit'; 3 | 4 | let test = function(...args) { 5 | 6 | function wrapper(assert) { 7 | // this is needed for maintaining scope in a test 8 | let env = assert.test.testEnvironment; 9 | run(() => { 10 | callback.apply(env, arguments); 11 | }); 12 | } 13 | 14 | // callback is original function 15 | let callback = args.splice(1, 1, wrapper)[0]; 16 | 17 | // args is fixed with new wrapper function 18 | qunitTest.apply(null, args); 19 | 20 | }; 21 | 22 | export { test }; 23 | -------------------------------------------------------------------------------- /addon/model.js: -------------------------------------------------------------------------------- 1 | import { not } from '@ember/object/computed'; 2 | import { isNone } from '@ember/utils'; 3 | import EmberObject, { 4 | get, 5 | defineProperty, 6 | computed 7 | } from '@ember/object'; 8 | 9 | function equal(first, second) { 10 | if (first instanceof Array && second instanceof Array) { 11 | return first.length === second.length && first.every((v, i) => v === second[i]); 12 | } 13 | 14 | return first === second; 15 | } 16 | 17 | function factory(obj) { 18 | return obj.get("constructor.ClassMixin.ownerConstructor") || obj.constructor; 19 | } 20 | 21 | function attrs(obj) { 22 | let all = []; 23 | factory(obj).eachComputedProperty(function (key, meta) { 24 | if (meta.isAttribute) { 25 | all.push(key); 26 | } 27 | }); 28 | 29 | return all; 30 | } 31 | 32 | function clone(obj) { 33 | let copy = {}; 34 | factory(obj).eachComputedProperty(function (key, meta) { 35 | if (meta.isAttribute) { 36 | copy[key] = obj.get(key); 37 | } 38 | }); 39 | 40 | return copy; 41 | } 42 | 43 | let attr = function() { 44 | let meta = { 45 | isAttribute: true, 46 | defaults: arguments[0] 47 | }; 48 | 49 | return computed({ 50 | get(key) { 51 | let data = this.get("_data") || {}; 52 | 53 | return data[key]; 54 | }, 55 | set(key, value) { 56 | let data = this.get("_data") || {}; 57 | let dirty = this.get("_dirty") || {}; 58 | let primed = this.get("_primed") || {}; 59 | let defaults = this.get("_defaults") || {}; 60 | 61 | defaults[key] = meta.defaults; 62 | 63 | if (!this.get("isDirty")) { 64 | this.set("_oldState", clone(this)); 65 | } 66 | 67 | let ready = value === "" && (isNone(data[key])); 68 | 69 | dirty[key + ":isDirty"] = true; 70 | data[key] = value; 71 | 72 | if(!ready && !primed[key + ":isPrimed"]) { 73 | primed[key + ":isPrimed"] = true; 74 | } 75 | 76 | return data[key]; 77 | } 78 | }).property("_data").meta(meta); 79 | }; 80 | 81 | let Model = EmberObject.extend({ 82 | init() { 83 | this._super(...arguments); 84 | this._reset(); 85 | this._setup(); 86 | this.set("_defaults", {}); 87 | this.set("_data", clone(this)); 88 | this.set("_oldState", clone(this)); 89 | }, 90 | rollback() { 91 | let oldState = this.get("_oldState"); 92 | for(let key in oldState){ 93 | this.set(key, oldState[key]); 94 | } 95 | this._reset(); 96 | }, 97 | save() { 98 | let oldState = clone(this); 99 | this.set("_oldState", oldState); 100 | this._reset(); 101 | }, 102 | _reset() { 103 | this.set("isPrimed", false); 104 | this.set("_dirty", {}); 105 | this.set("_primed", {}); 106 | }, 107 | _setup() { 108 | let model = this; 109 | let attributes = attrs(this); 110 | 111 | attributes.forEach((attrName) => { 112 | defineProperty(model, attrName + "IsDirty", computed(function() { 113 | let current = this.get(attrName); 114 | let defaults = this.get("_defaults")[attrName]; 115 | let original = this.get("_oldState." + attrName); 116 | let dirty = this.get("_dirty"); 117 | let dirtyKey = attrName + ":isDirty"; 118 | let legit = (equal(current, defaults) && isNone(original)) || (equal(original, current)); 119 | 120 | return legit ? undefined : dirty[dirtyKey]; 121 | }).property("_dirty", "_defaults", "" + attrName)); 122 | 123 | let dynamicPrimedKey = attrName + "IsPrimed"; 124 | 125 | defineProperty(model, dynamicPrimedKey, computed(function() { 126 | let primed = this.get("_primed"); 127 | let primedKey = attrName + ":isPrimed"; 128 | 129 | return primed[primedKey]; 130 | }).property("_primed", "" + attrName)); 131 | }); 132 | 133 | let modelIsDirtyAttrs = attributes.map((attr) => attr + "IsDirty"); 134 | defineProperty(model, "isNotDirty", not('isDirty')); 135 | 136 | defineProperty(model, "isDirty", computed(...modelIsDirtyAttrs, function() { 137 | return modelIsDirtyAttrs.some((attr) => get(model, attr) === true); 138 | })); 139 | } 140 | }); 141 | 142 | export { attr, Model }; 143 | -------------------------------------------------------------------------------- /addon/models/filtered-record-array.js: -------------------------------------------------------------------------------- 1 | import { A } from '@ember/array'; 2 | import { computed } from '@ember/object'; 3 | import RecordArray from './record-array'; 4 | 5 | export default RecordArray.extend({ 6 | content: computed(function () { 7 | return this.updateContent(); 8 | }), 9 | 10 | updateContent() { 11 | let source = this.get("_source"); 12 | let filter_func = this.get("_filter_func"); 13 | 14 | return A(source.filter(filter_func)); 15 | }, 16 | 17 | willDestroy() { 18 | this._unregisterRecordArray(); 19 | this._super(...arguments); 20 | }, 21 | 22 | _unregisterRecordArray() { 23 | let store = this.get("_store"); 24 | store._unsubscribe(this); 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /addon/models/record-array.js: -------------------------------------------------------------------------------- 1 | import { A } from '@ember/array'; 2 | import ArrayProxy from '@ember/array/proxy'; 3 | import { computed } from '@ember/object'; 4 | 5 | export default ArrayProxy.extend({ 6 | _store: null, 7 | _type: null, 8 | 9 | content: computed(function () { 10 | return A(this.get("_source")); 11 | }), 12 | 13 | push(data) { 14 | let type = this.get('_type'); 15 | return this.get('_store').push(type, data); 16 | }, 17 | 18 | remove(id) { 19 | let type = this.get('_type'); 20 | this.get('_store').remove(type, id); 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /addon/models/record-proxy.js: -------------------------------------------------------------------------------- 1 | import ObjectProxy from '@ember/object/proxy'; 2 | import { computed } from '@ember/object'; 3 | import { getOwner } from '@ember/application'; 4 | 5 | export default ObjectProxy.extend({ 6 | _source: null, 7 | _store: null, 8 | _type: null, 9 | 10 | content: computed("_source.[]", function() { 11 | return this.compute(); 12 | }), 13 | 14 | init() { 15 | this._super(...arguments); 16 | const store = this.get('_store'); 17 | const type = this.get('_type'); 18 | 19 | let model = getOwner(store).lookup(`model:${type}`); 20 | 21 | for(let method in model) { 22 | if(typeof model[method] === "function") { 23 | if(!this[method]) { 24 | this.proxyMethod(method); 25 | } 26 | } 27 | } 28 | }, 29 | 30 | compute() { }, 31 | 32 | proxyMethod(method) { 33 | this[method] = function() { 34 | let content = this.get("content"); 35 | return content[method].apply(content, arguments); 36 | }; 37 | } 38 | }); 39 | -------------------------------------------------------------------------------- /addon/store.js: -------------------------------------------------------------------------------- 1 | import Service from '@ember/service'; 2 | import { uuid } from './uuid'; 3 | import { A } from '@ember/array'; 4 | import { run } from '@ember/runloop'; 5 | import EmberObject, { setProperties, get } from '@ember/object'; 6 | import { assert } from '@ember/debug'; 7 | import { getOwner } from '@ember/application'; 8 | import RecordProxy from "./models/record-proxy"; 9 | import RecordArray from "./models/record-array"; 10 | import FilteredRecordArray from "./models/filtered-record-array"; 11 | 12 | function buildRecord(type, data, store) { 13 | let record = createRecord(type, data, store); 14 | arrayForType(type, store).pushObject(record); 15 | 16 | return record; 17 | } 18 | 19 | function createRecord(type, data, store) { 20 | let factory = factoryForType(type, store); 21 | let primaryKey = primaryKeyForType(type, store); 22 | 23 | assert("No model was found for type: " + type, factory); 24 | 25 | let record = factory.create(data); 26 | let id = data[primaryKey]; 27 | identityMapForType(type, store)[id] = record; 28 | 29 | return record; 30 | } 31 | 32 | function factoryForType(type, store) { 33 | return getOwner(store).factoryFor("model:" + type); 34 | } 35 | 36 | function primaryKeyForType(type, store) { 37 | const factory = factoryForType(type, store) || {}; 38 | // http://emberjs.com/deprecations/v2.x/#toc_migrating-from-_lookupfactory-to-factoryfor 39 | return factory.class && (factory.class.primaryKey || 'id'); 40 | } 41 | 42 | function arrayForType(type, store) { 43 | let all = store.get("array"); 44 | let models = all[type] || []; 45 | all[type] = models; 46 | return A(models); 47 | } 48 | 49 | function identityMapForType(type, store) { 50 | let typeIdentityMap = store.get("identityMap"); 51 | let idIdentityMap = typeIdentityMap[type] || {}; 52 | typeIdentityMap[type] = idIdentityMap; 53 | 54 | return idIdentityMap; 55 | } 56 | 57 | const ServiceType = Service || EmberObject; 58 | 59 | let Store = ServiceType.extend({ 60 | init() { 61 | this._super(...arguments); 62 | this.reset(); 63 | }, 64 | reset() { 65 | this.set("recompute", A()); 66 | this.set("filtersMap", {}); 67 | this.set("identityMap", {}); 68 | 69 | let array = this.get("array") || {}; 70 | Object.keys(array).forEach((type) => { 71 | arrayForType(type, this).forEach(record => record.destroy()); 72 | }); 73 | this.set("array", {}); 74 | }, 75 | willDestroy() { 76 | this.setProperties({ 77 | "array": null, 78 | "identityMap": null, 79 | "filtersMap": null, 80 | "recompute": null 81 | }); 82 | }, 83 | clear(type) { 84 | if (type === undefined) { 85 | this.reset(); 86 | } 87 | 88 | delete this.get("identityMap")[type]; 89 | let records = arrayForType(type, this); 90 | records.forEach(record => record.destroy()); 91 | records.clear(); 92 | this.scheduleUpdate(type); 93 | }, 94 | remove(type, id) { 95 | let record = this._findById(type, id); 96 | if (record) { 97 | let primaryKey = primaryKeyForType(type, this); 98 | delete this.get("identityMap")[type][record[primaryKey]]; 99 | arrayForType(type, this).removeObject(record); 100 | record.destroy(); 101 | this.scheduleUpdate(type); 102 | } 103 | }, 104 | push(type, data) { 105 | let primaryKey = primaryKeyForType(type, this); 106 | data[primaryKey] = this._coerceId(data[primaryKey]); 107 | let record = this._findById(type, data[primaryKey]); 108 | 109 | if (record) { 110 | setProperties(record, data); 111 | } else { 112 | record = buildRecord(type, data, this); 113 | } 114 | 115 | this.scheduleUpdate(type); 116 | 117 | return record; 118 | }, 119 | pushArray(type, dataArray) { 120 | let primaryKey = primaryKeyForType(type, this); 121 | let records = []; 122 | 123 | dataArray.forEach((data) => { 124 | data[primaryKey] = this._coerceId(data[primaryKey]); 125 | let record = this._findById(type, data[primaryKey]); 126 | 127 | if (record) { 128 | setProperties(record, data); 129 | } else { 130 | record = createRecord(type, data, this); 131 | records.push(record); 132 | } 133 | }); 134 | 135 | arrayForType(type, this).pushObjects(records); 136 | 137 | this.scheduleUpdate(type); 138 | 139 | return records; 140 | }, 141 | scheduleUpdate(type) { 142 | let recompute = this.get("recompute"); 143 | recompute.addObject(type); 144 | run.scheduleOnce('actions', this, 'updateFilters'); 145 | }, 146 | updateFilters() { 147 | if (this.get('isDestroyed') || this.get('isDestroying')) { 148 | return; 149 | } 150 | let recompute = this.get("recompute"); 151 | let filtersMap = this.get("filtersMap"); 152 | 153 | Object.keys(filtersMap).forEach((type) => { 154 | let filters = filtersMap[type] || []; 155 | 156 | filters.forEach((recordArray) => { 157 | if (recompute.includes(type)) { 158 | let updatedContent = recordArray.updateContent(); 159 | recordArray.set("content", updatedContent); 160 | } 161 | }); 162 | }); 163 | 164 | this.set("recompute", A()); 165 | }, 166 | _unsubscribe(...args) { 167 | let updatedFiltersMap; 168 | let filterIds = A(args.map((func) => { 169 | let primaryKey = primaryKeyForType(func.type, this); 170 | return func[primaryKey]; 171 | })); 172 | let filtersMap = this.get("filtersMap"); 173 | Object.keys(filtersMap).forEach((type) => { 174 | let primaryKey = primaryKeyForType(type, this); 175 | let filters = filtersMap[type] || []; 176 | updatedFiltersMap = filters.filter((func) => !filterIds.includes(func[primaryKey])); 177 | filtersMap[type] = updatedFiltersMap; 178 | }); 179 | }, 180 | find(type, options) { 181 | if (typeof options === "undefined") { 182 | return this._findAllProxy(type); 183 | } 184 | 185 | if (options instanceof Function) { 186 | return this._findWithFilterFunc(type, options); 187 | } 188 | 189 | if (typeof options === "object") { 190 | let params = Object.keys(options); 191 | 192 | assert("No key was found in the filter options", params.length); 193 | 194 | let attr = params[0]; 195 | let value = options[attr]; 196 | 197 | return this._findWithFilterFunc(type, function filterFunction(m) { 198 | return get(m, attr) === value; 199 | }); 200 | } 201 | 202 | return this._findByIdComputed(type, options); 203 | }, 204 | findOne(type) { 205 | return RecordProxy.create({ 206 | _store: this, 207 | _type: type, 208 | _source: this._findAll(type), 209 | compute() { 210 | return this.get("_source").objectAt(0); 211 | } 212 | }); 213 | }, 214 | _findById(type, id) { 215 | let identityMap = identityMapForType(type, this); 216 | return identityMap[id] || null; 217 | }, 218 | _findAll(type) { 219 | return arrayForType(type, this); 220 | }, 221 | _findAllProxy(type) { 222 | return RecordArray.create({ 223 | _type: type, 224 | _store: this, 225 | _source: this._findAll(type) 226 | }); 227 | }, 228 | _findWithFilterFunc(type, filter_func) { 229 | let func = FilteredRecordArray.create({ 230 | _type: type, 231 | _store: this, 232 | _id: uuid(), 233 | _filter_func: filter_func, 234 | _source: this._findAll(type) 235 | }); 236 | let filtersMap = this.get("filtersMap"); 237 | let filters = filtersMap[type] || []; 238 | filters.push(func); 239 | filtersMap[type] = filters; 240 | return func; 241 | }, 242 | _coerceId(id) { 243 | let numberId = Number(id); 244 | if (!isNaN(numberId) && numberId.toString().length === id.toString().length) { 245 | return numberId; 246 | } 247 | return id; 248 | }, 249 | _findByIdComputed(type, id) { 250 | let actualId = this._coerceId(id); 251 | let primaryKey = primaryKeyForType(type, this); 252 | 253 | return RecordProxy.create({ 254 | _store: this, 255 | _type: type, 256 | _filter_value: actualId, 257 | _source: this._findAll(type), 258 | compute() { 259 | let filter_value = this.get("_filter_value"); 260 | return this.get("_source").findBy(primaryKey, filter_value); 261 | } 262 | }); 263 | } 264 | }); 265 | 266 | export default Store; 267 | -------------------------------------------------------------------------------- /addon/uuid.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Fast UUID generator, RFC4122 version 4 compliant. 3 | * @author Jeff Ward (jcward.com). 4 | * @license MIT license 5 | * @link http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 6 | **/ 7 | 8 | /** 9 | * ES 6 Module 10 | * @author Andrew Hacking (ahacking@gmail.com) 11 | * 12 | **/ 13 | const lut = []; for (var i=0; i<256; i++) { lut[i] = (i<16?'0':'')+(i).toString(16); } 14 | 15 | /** 16 | `uuid` generates a Version 4 UUID using Jeff Wards high performance generator. 17 | 18 | @method v4uuid 19 | @for Orbit 20 | @returns {String} a version 4 UUID 21 | */ 22 | export function uuid() { 23 | const d0 = Math.random()*0xffffffff|0; 24 | const d1 = Math.random()*0xffffffff|0; 25 | const d2 = Math.random()*0xffffffff|0; 26 | const d3 = Math.random()*0xffffffff|0; 27 | return lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+'-'+ 28 | lut[d1&0xff]+lut[d1>>8&0xff]+'-'+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+'-'+ 29 | lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+'-'+lut[d2>>16&0xff]+lut[d2>>24&0xff]+ 30 | lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff]; 31 | } 32 | -------------------------------------------------------------------------------- /app/services/simple-store.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-cli-simple-store/store'; 2 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | useYarn: true, 4 | scenarios: [ 5 | { 6 | name: 'ember-lts-2.12', 7 | npm: { 8 | devDependencies: { 9 | 'ember-source': '~2.12.0' 10 | } 11 | } 12 | }, 13 | { 14 | name: 'ember-2.18.X', 15 | npm: { 16 | devDependencies: { 17 | 'ember-source': '~2.18.0' 18 | } 19 | } 20 | }, 21 | { 22 | name: 'ember-3.1.X', 23 | npm: { 24 | devDependencies: { 25 | 'ember-source': '~3.1.1' 26 | } 27 | } 28 | }, 29 | { 30 | name: 'ember-3.3.X', 31 | npm: { 32 | devDependencies: { 33 | 'ember-source': '~3.3.0' 34 | } 35 | } 36 | }, 37 | { 38 | name: 'ember-3.4.X', 39 | npm: { 40 | devDependencies: { 41 | 'ember-source': '~3.4.0' 42 | } 43 | } 44 | }, 45 | { 46 | name: 'ember-3.7.X', 47 | npm: { 48 | devDependencies: { 49 | 'ember-source': '~3.7.0' 50 | } 51 | } 52 | } 53 | ] 54 | }; 55 | -------------------------------------------------------------------------------- /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: 'ember-cli-simple-store' 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-simple-store", 3 | "version": "5.9.4", 4 | "description": "ember-cli addon that provides a simple identity map for ember.js web applications", 5 | "scripts": { 6 | "lint:js": "eslint .", 7 | "start": "ember serve", 8 | "test": "ember try:each", 9 | "build": "ember build" 10 | }, 11 | "homepage": "https://github.com/toranb/ember-cli-simple-store", 12 | "bugs": { 13 | "url": "https://github.com/toranb/ember-cli-simple-store/issues" 14 | }, 15 | "engines": { 16 | "node": "6.* || 8.* || >= 10.*" 17 | }, 18 | "devDependencies": { 19 | "broccoli-asset-rev": "^2.7.0", 20 | "ember-cli": "~3.3.0", 21 | "ember-cli-app-version": "^2.0.0", 22 | "ember-cli-dependency-checker": "^2.0.0", 23 | "ember-cli-eslint": "^4.2.1", 24 | "ember-cli-htmlbars": "^2.0.1", 25 | "ember-cli-htmlbars-inline-precompile": "^1.0.0", 26 | "ember-cli-inject-live-reload": "^1.4.1", 27 | "ember-cli-qunit": "^4.3.2", 28 | "ember-cli-shims": "^1.2.0", 29 | "ember-cli-sri": "^2.1.0", 30 | "ember-cli-uglify": "^2.0.0", 31 | "ember-disable-prototype-extensions": "^1.1.2", 32 | "ember-export-application-global": "^2.0.0", 33 | "ember-load-initializers": "^1.1.0", 34 | "ember-maybe-import-regenerator": "^0.1.6", 35 | "ember-resolver": "^4.0.0", 36 | "ember-source": "~3.4.7", 37 | "ember-try": "^0.2.23", 38 | "eslint-plugin-ember": "^5.0.0", 39 | "eslint-plugin-node": "^6.0.1", 40 | "loader.js": "^4.2.3" 41 | }, 42 | "dependencies": { 43 | "ember-cli-babel": "^6.8.2", 44 | "ember-runtime-enumerable-includes-polyfill": "^2.1.0" 45 | }, 46 | "keywords": [ 47 | "data", 48 | "ember", 49 | "ember-addon", 50 | "ember-cli", 51 | "simple", 52 | "store" 53 | ], 54 | "repository": { 55 | "type": "git", 56 | "url": "https://github.com/toranb/ember-cli-simple-store" 57 | }, 58 | "license": "MIT", 59 | "author": "Toran Billups toranb@gmail.com", 60 | "directories": { 61 | "doc": "doc", 62 | "test": "tests" 63 | }, 64 | "ember-addon": { 65 | "configPath": "tests/dummy/config" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /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/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | embertest: true 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /tests/acceptance/a-defaults-test.js: -------------------------------------------------------------------------------- 1 | import { test } from 'qunit'; 2 | import moduleForAcceptance from '../helpers/module-for-acceptance'; 3 | 4 | moduleForAcceptance('Acceptance: Defaults A Test'); 5 | 6 | test('text input with no default value should be dirty when value added then removed', function(assert) { 7 | visit('/defaulta'); 8 | andThen(() => { 9 | assert.equal(find("input.name").val(), ""); 10 | assert.equal(find(".nameDirty").text(), ""); 11 | assert.equal(find(".namePrimed").text(), ""); 12 | }); 13 | fillIn("input.name", "x"); 14 | andThen(() => { 15 | assert.equal(find("input.name").val(), "x"); 16 | assert.equal(find(".nameDirty").text(), "true"); 17 | assert.equal(find(".namePrimed").text(), "true"); 18 | }); 19 | fillIn("input.name", ""); 20 | andThen(() => { 21 | assert.equal(find("input.name").val(), ""); 22 | assert.equal(find(".nameDirty").text(), "true"); 23 | assert.equal(find(".namePrimed").text(), "true"); 24 | }); 25 | }); 26 | 27 | test('checkbox input with default value of false should not be dirty when unchecked', function(assert) { 28 | visit('/defaulta'); 29 | andThen(() => { 30 | assert.equal(find("input.funny").is(":checked"), false); 31 | assert.equal(find(".funnyDirty").text(), ""); 32 | assert.equal(find(".funnyPrimed").text(), ""); 33 | }); 34 | click("input.funny"); 35 | andThen(() => { 36 | assert.equal(find("input.funny").is(":checked"), true); 37 | assert.equal(find(".funnyDirty").text(), "true"); 38 | assert.equal(find(".funnyPrimed").text(), "true"); 39 | }); 40 | click("input.funny"); 41 | andThen(() => { 42 | assert.equal(find("input.funny").is(":checked"), false); 43 | assert.equal(find(".funnyDirty").text(), ""); 44 | assert.equal(find(".funnyPrimed").text(), "true"); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /tests/acceptance/a-test.js: -------------------------------------------------------------------------------- 1 | import Foo from "dummy/models/foo"; 2 | import { test } from 'qunit'; 3 | import moduleForAcceptance from '../helpers/module-for-acceptance'; 4 | 5 | moduleForAcceptance('Acceptance: A Test', { 6 | beforeEach: () => { 7 | Foo.create({name: "wat"}); 8 | } 9 | }); 10 | 11 | test('the attr in test a should not hold global state across objects', function(assert) { 12 | visit('/wat'); 13 | andThen(() => { 14 | assert.ok(true); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /tests/acceptance/arrays-test.js: -------------------------------------------------------------------------------- 1 | import { test } from 'qunit'; 2 | import moduleForAcceptance from '../helpers/module-for-acceptance'; 3 | 4 | moduleForAcceptance('Acceptance: Arrays Test', { 5 | beforeEach() { 6 | this.store = this.application.__container__.lookup('service:simple-store'); 7 | }, 8 | afterEach() { 9 | this.store = null; 10 | } 11 | }); 12 | 13 | test('pushArray will trigger filter for each subscription', function(assert) { 14 | visit('/arrays'); 15 | andThen(() => { 16 | assert.equal(find('.one').find('option').length, 3); 17 | assert.equal(find('.two').find('option').length, 3); 18 | assert.equal(find('.three').find('option').length, 3); 19 | assert.equal(find('.four').find('option').length, 3); 20 | 21 | this.store.pushArray('robot', [{id: 2, size: 20}, {id: 1, size: 10}, {id: 3, size: 30}]); 22 | this.store.pushArray('zing', [{id: 2, number: 80}, {id: 1, number: 90}, {id: 3, number: 70}]); 23 | }); 24 | andThen(() => { 25 | assert.equal(find('.one').find('option').length, 0); 26 | assert.equal(find('.two').find('option').length, 2); 27 | assert.equal(find('.three').find('option').length, 0); 28 | assert.equal(find('.four').find('option').length, 2); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /tests/acceptance/b-defaults-test.js: -------------------------------------------------------------------------------- 1 | import { test } from 'qunit'; 2 | import moduleForAcceptance from '../helpers/module-for-acceptance'; 3 | 4 | moduleForAcceptance('Acceptance: Defaults B Test'); 5 | 6 | test('text input with default value of empty string should not be dirty when value added then removed', function(assert) { 7 | visit('/defaultb'); 8 | andThen(() => { 9 | assert.equal(find("input.name").val(), ""); 10 | assert.equal(find(".nameDirty").text(), ""); 11 | assert.equal(find(".namePrimed").text(), ""); 12 | }); 13 | fillIn("input.name", "x"); 14 | andThen(() => { 15 | assert.equal(find("input.name").val(), "x"); 16 | assert.equal(find(".nameDirty").text(), "true"); 17 | assert.equal(find(".namePrimed").text(), "true"); 18 | }); 19 | fillIn("input.name", ""); 20 | andThen(() => { 21 | assert.equal(find("input.name").val(), ""); 22 | assert.equal(find(".nameDirty").text(), ""); 23 | assert.equal(find(".namePrimed").text(), "true"); 24 | }); 25 | }); 26 | 27 | test('checkbox input with no default value should be dirty when unchecked', function(assert) { 28 | visit('/defaultb'); 29 | andThen(() => { 30 | assert.equal(find("input.funny").is(":checked"), false); 31 | assert.equal(find(".funnyDirty").text(), ""); 32 | assert.equal(find(".funnyPrimed").text(), ""); 33 | }); 34 | click("input.funny"); 35 | andThen(() => { 36 | assert.equal(find("input.funny").is(":checked"), true); 37 | assert.equal(find(".funnyDirty").text(), "true"); 38 | assert.equal(find(".funnyPrimed").text(), "true"); 39 | }); 40 | click("input.funny"); 41 | andThen(() => { 42 | assert.equal(find("input.funny").is(":checked"), false); 43 | assert.equal(find(".funnyDirty").text(), "true"); 44 | assert.equal(find(".funnyPrimed").text(), "true"); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /tests/acceptance/b-test.js: -------------------------------------------------------------------------------- 1 | import { test } from 'qunit'; 2 | import moduleForAcceptance from '../helpers/module-for-acceptance'; 3 | 4 | moduleForAcceptance('Acceptance: B Test'); 5 | 6 | test('the attr in test b should not hold global state across objects', function(assert) { 7 | visit('/wat'); 8 | andThen(() => { 9 | assert.equal(find("input.name").val(), ""); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /tests/acceptance/delete-object-test.js: -------------------------------------------------------------------------------- 1 | import { run } from '@ember/runloop'; 2 | import { test } from 'qunit'; 3 | import moduleForAcceptance from '../helpers/module-for-acceptance'; 4 | 5 | moduleForAcceptance('Acceptance: Delete Object Test'); 6 | 7 | test('when object is removed from store all timers and observers are killed', function(assert) { 8 | let lastNumber; 9 | let done = assert.async(); 10 | visit('/delete-object'); 11 | andThen(() => { 12 | assert.ok(parseInt(find(".number-is").text(), 10) > 0); 13 | }); 14 | run.later(() => { 15 | find("button.stop").trigger("click"); 16 | lastNumber = parseInt(find(".number-is").text(), 10); 17 | }, 500); 18 | run.later(() => { 19 | assert.equal(parseInt(find(".number-is").text(), 10), lastNumber); 20 | assert.equal(window.number, lastNumber + 1); 21 | done(); 22 | }, 1500); 23 | }); 24 | -------------------------------------------------------------------------------- /tests/acceptance/edit-test.js: -------------------------------------------------------------------------------- 1 | import { test } from 'qunit'; 2 | import moduleForAcceptance from '../helpers/module-for-acceptance'; 3 | 4 | moduleForAcceptance('Acceptance: Edit Test'); 5 | 6 | test('attribute change from value to empty string should result in isPrimed', function(assert) { 7 | visit('/edit'); 8 | andThen(() => { 9 | assert.equal(find("input.name").val(), "toran"); 10 | assert.equal(find(".nameDirty").text(), ""); 11 | assert.equal(find(".namePrimed").text(), ""); 12 | }); 13 | fillIn("input.name", ""); 14 | andThen(() => { 15 | assert.equal(find("input.name").val(), ""); 16 | assert.equal(find(".nameDirty").text(), "true"); 17 | assert.equal(find(".namePrimed").text(), "true"); 18 | }); 19 | fillIn("input.name", "x"); 20 | andThen(() => { 21 | assert.equal(find("input.name").val(), "x"); 22 | assert.equal(find(".nameDirty").text(), "true"); 23 | assert.equal(find(".namePrimed").text(), "true"); 24 | }); 25 | fillIn("input.name", "toran"); 26 | andThen(() => { 27 | assert.equal(find("input.name").val(), "toran"); 28 | assert.equal(find(".nameDirty").text(), ""); 29 | assert.equal(find(".namePrimed").text(), "true"); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /tests/acceptance/filters-test.js: -------------------------------------------------------------------------------- 1 | import { test } from 'qunit'; 2 | import moduleForAcceptance from '../helpers/module-for-acceptance'; 3 | 4 | moduleForAcceptance('Acceptance: Filters Test', { 5 | beforeEach() { 6 | this.store = this.application.__container__.lookup('service:simple-store'); 7 | }, 8 | afterEach() { 9 | this.store = null; 10 | } 11 | }); 12 | 13 | test('push will trigger filter for each subscription', function(assert) { 14 | visit('/filters'); 15 | andThen(() => { 16 | assert.equal(find('.one').find('option').length, 3); 17 | assert.equal(find('.two').find('option').length, 3); 18 | assert.equal(find('.three').find('option').length, 3); 19 | assert.equal(find('.four').find('option').length, 3); 20 | this.store.push('robot', {id: 2, size: 20}); 21 | this.store.push('robot', {id: 1, size: 10}); 22 | this.store.push('robot', {id: 3, size: 30}); 23 | this.store.push('zing', {id: 2, number: 80}); 24 | this.store.push('zing', {id: 1, number: 90}); 25 | this.store.push('zing', {id: 3, number: 70}); 26 | }); 27 | andThen(() => { 28 | assert.equal(find('.one').find('option').length, 0); 29 | assert.equal(find('.two').find('option').length, 2); 30 | assert.equal(find('.three').find('option').length, 0); 31 | assert.equal(find('.four').find('option').length, 2); 32 | }); 33 | }); 34 | 35 | test('filters can be thrown out when you navigate away from a given route', function(assert) { 36 | visit('/filters'); 37 | andThen(() => { 38 | assert.equal(currentURL(), '/filters'); 39 | let filtersMap = this.store.get('filtersMap'); 40 | let robotFilters = filtersMap['robot']; 41 | let zingFilters = filtersMap['zing']; 42 | assert.equal(robotFilters.length, 2); 43 | assert.equal(zingFilters.length, 2); 44 | }); 45 | click('.link-robots'); 46 | andThen(() => { 47 | assert.equal(currentURL(), '/robots'); 48 | let filtersMap = this.store.get('filtersMap'); 49 | let robotFilters = filtersMap['robot']; 50 | let zingFilters = filtersMap['zing']; 51 | //the filters route did cleanup so removed the filters used :) 52 | assert.equal(robotFilters.length, 2); 53 | assert.equal(zingFilters.length, 0); 54 | }); 55 | click('.link-wat'); 56 | andThen(() => { 57 | assert.equal(currentURL(), '/wat'); 58 | let filtersMap = this.store.get('filtersMap'); 59 | let robotFilters = filtersMap['robot']; 60 | let zingFilters = filtersMap['zing']; 61 | //the robots route did not cleanup so we now have a memory leak :( 62 | assert.equal(robotFilters.length, 2); 63 | assert.equal(zingFilters.length, 0); 64 | }); 65 | click('.link-filters'); 66 | andThen(() => { 67 | assert.equal(currentURL(), '/filters'); 68 | let filtersMap = this.store.get('filtersMap'); 69 | let robotFilters = filtersMap['robot']; 70 | let zingFilters = filtersMap['zing']; 71 | //the wat route did not cleanup so we now have a memory leak :( 72 | assert.equal(robotFilters.length, 4); 73 | assert.equal(zingFilters.length, 2); 74 | }); 75 | }); 76 | 77 | test('filters on models with custom primary keys can be thrown out when you leave a route', function(assert) { 78 | visit('/custom-key'); 79 | andThen(() => { 80 | assert.equal(currentURL(), '/custom-key'); 81 | let filtersMap = this.store.get('filtersMap'); 82 | let customKeyFilters = filtersMap['custom-key']; 83 | assert.equal(customKeyFilters.length, 1); 84 | }); 85 | click('.link-wat'); 86 | andThen(() => { 87 | assert.equal(currentURL(), '/wat'); 88 | let filtersMap = this.store.get('filtersMap'); 89 | let customKeyFilters = filtersMap['custom-key']; 90 | assert.equal(customKeyFilters.length, 0); 91 | }); 92 | }); 93 | 94 | test('each filter function will be updated during a push with multiple listeners across multiple routes', function(assert) { 95 | visit('/filters'); 96 | andThen(() => { 97 | assert.equal(currentURL(), '/filters'); 98 | assert.equal(find('.one').find('option').length, 3); 99 | assert.equal(find('.two').find('option').length, 3); 100 | assert.equal(find('.three').find('option').length, 3); 101 | assert.equal(find('.four').find('option').length, 3); 102 | this.store.push('robot', {id: 2, size: 20}); 103 | this.store.push('robot', {id: 1, size: 10}); 104 | this.store.push('robot', {id: 3, size: 30}); 105 | this.store.push('zing', {id: 2, number: 80}); 106 | this.store.push('zing', {id: 1, number: 90}); 107 | this.store.push('zing', {id: 3, number: 70}); 108 | }); 109 | andThen(() => { 110 | assert.equal(find('.one').find('option').length, 0); 111 | assert.equal(find('.two').find('option').length, 2); 112 | assert.equal(find('.three').find('option').length, 0); 113 | assert.equal(find('.four').find('option').length, 2); 114 | }); 115 | click('.link-robots'); 116 | andThen(() => { 117 | assert.equal(currentURL(), '/robots'); 118 | assert.equal(find('.nine').find('option').length, 1); 119 | assert.equal(find('.eight').find('option').length, 1); 120 | this.store.push('robot', {id: 15, name: 'seven', size: 15}); 121 | }); 122 | andThen(() => { 123 | assert.equal(find('.nine').find('option').length, 1); 124 | assert.equal(find('.eight').find('option').length, 2); 125 | }); 126 | click('.link-filters'); 127 | andThen(() => { 128 | assert.equal(currentURL(), '/filters'); 129 | assert.equal(find('.one').find('option').length, 1); 130 | assert.equal(find('.two').find('option').length, 5); 131 | assert.equal(find('.three').find('option').length, 0); 132 | assert.equal(find('.four').find('option').length, 2); 133 | }); 134 | }); 135 | -------------------------------------------------------------------------------- /tests/acceptance/isnone-test.js: -------------------------------------------------------------------------------- 1 | import { test } from 'qunit'; 2 | import moduleForAcceptance from '../helpers/module-for-acceptance'; 3 | 4 | moduleForAcceptance('Acceptance: isNone Test'); 5 | 6 | test('attribute change from value to empty string should result in isPrimed for null and undefined', function(assert) { 7 | visit('/isnone'); 8 | andThen(function() { 9 | assert.equal(find("input.name:eq(0)").val(), ""); 10 | assert.equal(find(".nameDirty:eq(0)").text(), ""); 11 | assert.equal(find(".namePrimed:eq(0)").text(), ""); 12 | assert.equal(find("input.name:eq(1)").val(), ""); 13 | assert.equal(find(".nameDirty:eq(1)").text(), ""); 14 | assert.equal(find(".namePrimed:eq(1)").text(), ""); 15 | }); 16 | fillIn("input.name:eq(0)", ""); 17 | fillIn("input.name:eq(1)", ""); 18 | andThen(function() { 19 | assert.equal(find("input.name:eq(0)").val(), ""); 20 | assert.equal(find(".nameDirty:eq(0)").text(), "true"); 21 | assert.equal(find(".namePrimed:eq(0)").text(), ""); 22 | assert.equal(find("input.name:eq(1)").val(), ""); 23 | assert.equal(find(".nameDirty:eq(1)").text(), "true"); 24 | assert.equal(find(".namePrimed:eq(1)").text(), ""); 25 | }); 26 | fillIn("input.name:eq(0)", "x"); 27 | fillIn("input.name:eq(1)", "x"); 28 | andThen(function() { 29 | assert.equal(find("input.name:eq(0)").val(), "x"); 30 | assert.equal(find(".nameDirty:eq(0)").text(), "true"); 31 | assert.equal(find(".namePrimed:eq(0)").text(), "true"); 32 | assert.equal(find("input.name:eq(1)").val(), "x"); 33 | assert.equal(find(".nameDirty:eq(1)").text(), "true"); 34 | assert.equal(find(".namePrimed:eq(1)").text(), "true"); 35 | }); 36 | fillIn("input.name:eq(0)", ""); 37 | fillIn("input.name:eq(1)", ""); 38 | andThen(function() { 39 | assert.equal(find("input.name:eq(0)").val(), ""); 40 | assert.equal(find(".nameDirty:eq(0)").text(), "true"); 41 | assert.equal(find(".namePrimed:eq(0)").text(), "true"); 42 | assert.equal(find("input.name:eq(1)").val(), ""); 43 | assert.equal(find(".nameDirty:eq(1)").text(), "true"); 44 | assert.equal(find(".namePrimed:eq(1)").text(), "true"); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /tests/acceptance/relationships-test.js: -------------------------------------------------------------------------------- 1 | import { test } from 'qunit'; 2 | import moduleForAcceptance from '../helpers/module-for-acceptance'; 3 | 4 | moduleForAcceptance('Acceptance: Relationships Test', { 5 | beforeEach() { 6 | this.store = this.application.__container__.lookup('service:simple-store'); 7 | } 8 | }); 9 | 10 | test('changing the users role will alter the underlying relationship', function(assert) { 11 | visit('/relationships'); 12 | andThen(() => { 13 | let options = find('.all-roles'); 14 | assert.equal(options.find('option').length, 2); 15 | assert.equal(options.find('option:eq(0)').val(), 8); 16 | assert.equal(options.find('option:eq(1)').val(), 9); 17 | assert.equal(options.find('option:eq(0)').text(), 'Admin'); 18 | assert.equal(options.find('option:eq(1)').text(), 'Guest'); 19 | assert.equal(options.find('option:selected').val(), 9); 20 | assert.equal(this.store.find('user', 2).get('role.id'), 9); 21 | }); 22 | fillIn('.all-roles', 8); 23 | andThen(() => { 24 | let options = find('.all-roles'); 25 | assert.equal(options.find('option').length, 2); 26 | assert.equal(options.find('option:selected').val(), 8); 27 | assert.equal(this.store.find('user', 2).get('role.id'), 8); 28 | }); 29 | }); 30 | 31 | test('updateContent only fires for models with subscribers and only once per run', function(assert) { 32 | let roleUpdated = 0; 33 | visit('/relationships'); 34 | andThen(() => { 35 | assert.equal(this.store.find('user', 2).get('role.id'), 9); 36 | let filtersMap = this.store.get("filtersMap"); 37 | let roleFunc = filtersMap["role"]; 38 | let origRoleFunc = roleFunc[0].updateContent; 39 | roleFunc[0].updateContent = function() { 40 | roleUpdated = roleUpdated + 1; 41 | return origRoleFunc.apply(this, arguments); 42 | }; 43 | let userFunc = filtersMap["user"]; 44 | assert.equal(userFunc, undefined); 45 | }); 46 | fillIn('.all-roles', 8); 47 | andThen(() => { 48 | assert.equal(this.store.find('user', 2).get('role.id'), 8); 49 | assert.equal(roleUpdated, 1); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /tests/acceptance/x-defaults-test.js: -------------------------------------------------------------------------------- 1 | import { test } from 'qunit'; 2 | import moduleForAcceptance from '../helpers/module-for-acceptance'; 3 | 4 | moduleForAcceptance('Acceptance: Defaults X Test'); 5 | 6 | test('checkbox input with default value should not be dirty when unchecked', function(assert) { 7 | visit('/defaultx'); 8 | andThen(() => { 9 | assert.equal(find("input.funny").is(":checked"), true); 10 | assert.equal(find(".funnyDirty").text(), ""); 11 | assert.equal(find(".funnyPrimed").text(), ""); 12 | }); 13 | click("input.funny"); 14 | andThen(() => { 15 | assert.equal(find("input.funny").is(":checked"), false); 16 | assert.equal(find(".funnyDirty").text(), "true"); 17 | assert.equal(find(".funnyPrimed").text(), "true"); 18 | }); 19 | click("input.funny"); 20 | andThen(() => { 21 | assert.equal(find("input.funny").is(":checked"), true); 22 | assert.equal(find(".funnyDirty").text(), ""); 23 | assert.equal(find(".funnyPrimed").text(), "true"); 24 | }); 25 | click("input.funny"); 26 | andThen(() => { 27 | assert.equal(find("input.funny").is(":checked"), false); 28 | assert.equal(find(".funnyDirty").text(), "true"); 29 | assert.equal(find(".funnyPrimed").text(), "true"); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /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/toranb/ember-cli-simple-store/ac6955430e26a7cd833aa3764228f4e3f8f96748/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toranb/ember-cli-simple-store/ac6955430e26a7cd833aa3764228f4e3f8f96748/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/delete-object.js: -------------------------------------------------------------------------------- 1 | import { inject as service } from '@ember/service'; 2 | import Controller from '@ember/controller'; 3 | 4 | export default Controller.extend({ 5 | simpleStore: service(), 6 | actions: { 7 | stop: function() { 8 | let simpleStore = this.get("simpleStore"); 9 | simpleStore.remove("wat", 1); 10 | } 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/relationships.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | 3 | export default Controller.extend({ 4 | actions: { 5 | changed: function(new_role_id) { 6 | let user = this.get("model"); 7 | user.change_role(new_role_id); 8 | } 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toranb/ember-cli-simple-store/ac6955430e26a7cd833aa3764228f4e3f8f96748/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/helpers/is-equal.js: -------------------------------------------------------------------------------- 1 | import { helper } from '@ember/component/helper'; 2 | 3 | export default helper((params) => { 4 | return params[0] === params[1]; 5 | }); 6 | -------------------------------------------------------------------------------- /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/toranb/ember-cli-simple-store/ac6955430e26a7cd833aa3764228f4e3f8f96748/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/models/custom-key.js: -------------------------------------------------------------------------------- 1 | import { attr, Model } from "ember-cli-simple-store/model"; 2 | 3 | let CustomKeyModel = Model.extend({ 4 | name: attr() 5 | }); 6 | 7 | CustomKeyModel.reopenClass({ 8 | primaryKey: 'arbitraryKey' 9 | }); 10 | 11 | export default CustomKeyModel; 12 | -------------------------------------------------------------------------------- /tests/dummy/app/models/foo.js: -------------------------------------------------------------------------------- 1 | import { attr, Model } from "ember-cli-simple-store/model"; 2 | 3 | export default Model.extend({ 4 | name: attr(), 5 | funny: attr(false) 6 | }); 7 | -------------------------------------------------------------------------------- /tests/dummy/app/models/robot.js: -------------------------------------------------------------------------------- 1 | import { attr, Model } from "ember-cli-simple-store/model"; 2 | 3 | export default Model.extend({ 4 | name: attr() 5 | }); 6 | -------------------------------------------------------------------------------- /tests/dummy/app/models/role.js: -------------------------------------------------------------------------------- 1 | import { attr, Model } from "ember-cli-simple-store/model"; 2 | 3 | export default Model.extend({ 4 | name: attr() 5 | }); 6 | -------------------------------------------------------------------------------- /tests/dummy/app/models/user.js: -------------------------------------------------------------------------------- 1 | import { computed } from '@ember/object'; 2 | import { alias } from '@ember/object/computed'; 3 | import { inject as service } from '@ember/service'; 4 | import { attr, Model } from "ember-cli-simple-store/model"; 5 | 6 | export default Model.extend({ 7 | name: attr(), 8 | simpleStore: service(), 9 | role: alias("belongs_to.firstObject"), 10 | belongs_to: computed(function() { 11 | let user_id = this.get("id"); 12 | let store = this.get("simpleStore"); 13 | let filter = function(role) { 14 | let users = role.get("users"); 15 | return users.indexOf(user_id) > -1; 16 | }; 17 | return store.find("role", filter); 18 | }), 19 | change_role(new_role_id) { 20 | let user_name = this.get("name"); 21 | let user_id = this.get("id"); 22 | let store = this.get("simpleStore"); 23 | let old_role = this.get("role"); 24 | let old_role_users = old_role.get("users") || []; 25 | let updated_old_role_users = old_role_users.filter((id) => { 26 | return id !== user_id; 27 | }); 28 | let new_role = store.find("role", new_role_id); 29 | let new_role_users = new_role.get("users") || []; 30 | store.push("user", {id: user_id, name: user_name}); //only to test perf 31 | // old_role.set("users", updated_old_role_users); in v3 this worked but v4 requires a push 32 | store.push("role", {id: old_role.get("id"), users: updated_old_role_users}); 33 | // new_role.set("users", new_role_users.concat(user_id)); in v3 this worked but v4 requires a push 34 | store.push("role", {id: new_role.get("id"), users: new_role_users.concat(user_id)}); 35 | store.push("user", {id: user_id, nope: "wat"}); //only to test perf 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /tests/dummy/app/models/wat.js: -------------------------------------------------------------------------------- 1 | import { observer } from '@ember/object'; 2 | import { later, cancel } from '@ember/runloop'; 3 | import { attr, Model } from "ember-cli-simple-store/model"; 4 | 5 | let onTimeout = function() { 6 | if(!this.isDestroyed) { 7 | let number = this.get("number"); 8 | this.set("number", number + 1); 9 | } 10 | this.timer = later(onTimeout.bind(this), 100); 11 | }; 12 | 13 | export default Model.extend({ 14 | number: attr(), 15 | init() { 16 | this.timer = later(onTimeout.bind(this), 100); 17 | this._super(); 18 | }, 19 | observeNumber: observer("number", function() { 20 | let number = this.get("number"); 21 | window.number = number + 1; 22 | }), 23 | willDestroy() { 24 | cancel(this.timer); 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /tests/dummy/app/models/wow.js: -------------------------------------------------------------------------------- 1 | import { attr, Model } from "ember-cli-simple-store/model"; 2 | 3 | export default Model.extend({ 4 | name: attr(""), 5 | funny: attr() 6 | }); 7 | -------------------------------------------------------------------------------- /tests/dummy/app/models/zap.js: -------------------------------------------------------------------------------- 1 | import { attr, Model } from "ember-cli-simple-store/model"; 2 | 3 | export default Model.extend({ 4 | name: attr(), 5 | funny: attr() 6 | }); 7 | -------------------------------------------------------------------------------- /tests/dummy/app/models/zing.js: -------------------------------------------------------------------------------- 1 | import { attr, Model } from "ember-cli-simple-store/model"; 2 | 3 | export default Model.extend({ 4 | name: attr() 5 | }); 6 | -------------------------------------------------------------------------------- /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 | this.route("wat", {path: "/wat"}); 11 | this.route("defaulta", {path: "/defaulta"}); 12 | this.route("defaultb", {path: "/defaultb"}); 13 | this.route("defaultx", {path: "/defaultx"}); 14 | this.route("edit", {path: "/edit"}); 15 | this.route("isnone", {path: "/isnone"}); 16 | this.route("relationships", {path: "/relationships"}); 17 | this.route("arrays", {path: "/arrays"}); 18 | this.route("filters", {path: "/filters"}); 19 | this.route("robots", {path: "/robots"}); 20 | this.route("custom-key"); 21 | this.route("delete-object"); 22 | }); 23 | 24 | export default Router; 25 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toranb/ember-cli-simple-store/ac6955430e26a7cd833aa3764228f4e3f8f96748/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/routes/arrays.js: -------------------------------------------------------------------------------- 1 | import { inject as service } from '@ember/service'; 2 | import Route from '@ember/routing/route'; 3 | import { get } from '@ember/object'; 4 | 5 | let ArraysRoute = Route.extend({ 6 | simpleStore: service(), 7 | model() { 8 | let simpleStore = this.get("simpleStore"); 9 | if(simpleStore.find("robot").get("length") === 0) { 10 | // hack to prevent another push of the same data 11 | simpleStore.pushArray("robot", 12 | [ 13 | {id: 1, name: "r one", size: 51}, 14 | {id: 2, name: "r two", size: 52}, 15 | {id: 3, name: "r three", size: 53} 16 | ] 17 | ); 18 | 19 | simpleStore.pushArray("zing", 20 | [ 21 | {id: 1, name: "z one", number: 49}, 22 | {id: 2, name: "z two", number: 48}, 23 | {id: 3, name: "z three", number: 47} 24 | ] 25 | ); 26 | } 27 | let one = function(m) { 28 | return get(m, "size") > 50; 29 | }; 30 | let two = function(m) { 31 | return get(m, "size") > 10; 32 | }; 33 | let three = function(m) { 34 | return get(m, "number") < 50; 35 | }; 36 | let four = function(m) { 37 | return get(m, "number") < 90; 38 | }; 39 | let filterone = simpleStore.find("robot", one); 40 | let filtertwo = simpleStore.find("robot", two); 41 | let filterthree = simpleStore.find("zing", three); 42 | let filterfour = simpleStore.find("zing", four); 43 | return {filterone, filtertwo, filterthree, filterfour}; 44 | }, 45 | setupController(controller, hash) { 46 | controller.set("filterone", hash.filterone); 47 | controller.set("filtertwo", hash.filtertwo); 48 | controller.set("filterthree", hash.filterthree); 49 | controller.set("filterfour", hash.filterfour); 50 | }, 51 | actions: { 52 | willTransition() { 53 | let currentModel = this.get("currentModel"); 54 | currentModel.filterone.destroy(); 55 | currentModel.filtertwo.destroy(); 56 | currentModel.filterthree.destroy(); 57 | currentModel.filterfour.destroy(); 58 | } 59 | } 60 | }); 61 | 62 | export default ArraysRoute; 63 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/custom-key.js: -------------------------------------------------------------------------------- 1 | import { inject as service } from '@ember/service'; 2 | import Route from '@ember/routing/route'; 3 | 4 | export default Route.extend({ 5 | simpleStore: service(), 6 | model() { 7 | let simpleStore = this.get("simpleStore"); 8 | if(simpleStore.find("custom-key").get("length") === 0) { 9 | //hack to prevent another push of the same data 10 | simpleStore.push("custom-key", {arbitraryKey: 1, name: "k one"}); 11 | simpleStore.push("custom-key", {arbitraryKey: 2, name: "k two"}); 12 | simpleStore.push("custom-key", {arbitraryKey: 3, name: "k three"}); 13 | } 14 | 15 | return simpleStore.find('custom-key', (record => 16 | record.get('name').indexOf('three') >= 0)); 17 | }, 18 | actions: { 19 | willTransition () { 20 | this.get('controller.model').destroy(); 21 | } 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/defaulta.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | import Foo from 'dummy/models/foo'; 3 | 4 | export default Route.extend({ 5 | model: function() { 6 | return Foo.create(); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/defaultb.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | import Wow from 'dummy/models/wow'; 3 | 4 | export default Route.extend({ 5 | model: function() { 6 | return Wow.create(); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/defaultx.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | import Foo from 'dummy/models/foo'; 3 | 4 | export default Route.extend({ 5 | model: function() { 6 | return Foo.create({name: "wat", funny: true}); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/delete-object.js: -------------------------------------------------------------------------------- 1 | import { inject as service } from '@ember/service'; 2 | import Route from '@ember/routing/route'; 3 | 4 | export default Route.extend({ 5 | simpleStore: service(), 6 | model() { 7 | let simpleStore = this.get("simpleStore"); 8 | return simpleStore.push("wat", {id: 1, number: 0}); 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/edit.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | import Foo from 'dummy/models/foo'; 3 | 4 | let EditRoute = Route.extend({ 5 | model: function() { 6 | return Foo.create({id: 1, name: "toran"}); 7 | } 8 | }); 9 | 10 | export default EditRoute; 11 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/filters.js: -------------------------------------------------------------------------------- 1 | import { inject as service } from '@ember/service'; 2 | import Route from '@ember/routing/route'; 3 | import { get } from '@ember/object'; 4 | 5 | let FiltersRoute = Route.extend({ 6 | simpleStore: service(), 7 | model() { 8 | let simpleStore = this.get("simpleStore"); 9 | if(simpleStore.find("robot").get("length") === 0) { 10 | //hack to prevent another push of the same data 11 | simpleStore.push("robot", {id: 1, name: "r one", size: 51}); 12 | simpleStore.push("robot", {id: 2, name: "r two", size: 52}); 13 | simpleStore.push("robot", {id: 3, name: "r three", size: 53}); 14 | simpleStore.push("zing", {id: 1, name: "z one", number: 49}); 15 | simpleStore.push("zing", {id: 2, name: "z two", number: 48}); 16 | simpleStore.push("zing", {id: 3, name: "z three", number: 47}); 17 | } 18 | let one = function(m) { 19 | return get(m, "size") > 50; 20 | }; 21 | let two = function(m) { 22 | return get(m, "size") > 10; 23 | }; 24 | let three = function(m) { 25 | return get(m, "number") < 50; 26 | }; 27 | let four = function(m) { 28 | return get(m, "number") < 90; 29 | }; 30 | let filterone = simpleStore.find("robot", one); 31 | let filtertwo = simpleStore.find("robot", two); 32 | let filterthree = simpleStore.find("zing", three); 33 | let filterfour = simpleStore.find("zing", four); 34 | return {filterone, filtertwo, filterthree, filterfour}; 35 | }, 36 | setupController(controller, hash) { 37 | controller.set("filterone", hash.filterone); 38 | controller.set("filtertwo", hash.filtertwo); 39 | controller.set("filterthree", hash.filterthree); 40 | controller.set("filterfour", hash.filterfour); 41 | }, 42 | actions: { 43 | willTransition() { 44 | let currentModel = this.get("currentModel"); 45 | currentModel.filterone.destroy(); 46 | currentModel.filtertwo.destroy(); 47 | currentModel.filterthree.destroy(); 48 | currentModel.filterfour.destroy(); 49 | } 50 | } 51 | }); 52 | 53 | export default FiltersRoute; 54 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/isnone.js: -------------------------------------------------------------------------------- 1 | import { A } from '@ember/array'; 2 | import Route from '@ember/routing/route'; 3 | import Foo from 'dummy/models/foo'; 4 | 5 | let IsNoneRoute = Route.extend({ 6 | model: function() { 7 | let one = Foo.create({id: 1, name: undefined}); 8 | let two = Foo.create({id: 2, name: null}); 9 | return A([one, two]); 10 | } 11 | }); 12 | 13 | export default IsNoneRoute; 14 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/relationships.js: -------------------------------------------------------------------------------- 1 | import { inject as service } from '@ember/service'; 2 | import Route from '@ember/routing/route'; 3 | 4 | let RelationshipsRoute = Route.extend({ 5 | simpleStore: service(), 6 | model() { 7 | let user_id = 2; 8 | let simpleStore = this.get("simpleStore"); 9 | simpleStore.push("role", {id: 8, name: "Admin", users: []}); 10 | simpleStore.push("role", {id: 9, name: "Guest", users: [user_id]}); 11 | simpleStore.push("user", {id: user_id, name: "toran"}); 12 | let model = simpleStore.find("user", user_id); 13 | let roles = simpleStore.find("role"); 14 | return {model: model, roles: roles}; 15 | }, 16 | setupController(controller, hash) { 17 | controller.set("model", hash.model); 18 | controller.set("roles", hash.roles); 19 | } 20 | }); 21 | 22 | export default RelationshipsRoute; 23 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/robots.js: -------------------------------------------------------------------------------- 1 | import { inject as service } from '@ember/service'; 2 | import Route from '@ember/routing/route'; 3 | import { get } from '@ember/object'; 4 | 5 | let RobotsRoute = Route.extend({ 6 | simpleStore: service(), 7 | model: function() { 8 | let simpleStore = this.get("simpleStore"); 9 | simpleStore.push("robot", {id: 9, name: "nine", size: 229}); 10 | simpleStore.push("robot", {id: 8, name: "eight", size: 9}); 11 | simpleStore.push("robot", {id: 7, name: "seven", size: 11}); 12 | //this filter existed previously but was removed (now it will be recreated) 13 | let one = function(m) { 14 | return get(m, "size") > 50; 15 | }; 16 | //this filter is new and different 17 | let eight = function(m) { 18 | return get(m, "size") > 10 && get(m, "name") === "seven"; 19 | }; 20 | let filternine = simpleStore.find("robot", one); 21 | let filtereight = simpleStore.find("robot", eight); 22 | return {filternine, filtereight}; 23 | }, 24 | setupController: function(controller, hash) { 25 | controller.set("filternine", hash.filternine); 26 | controller.set("filtereight", hash.filtereight); 27 | } 28 | }); 29 | 30 | export default RobotsRoute; 31 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/wat.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/routing/route'; 2 | import Foo from 'dummy/models/foo'; 3 | 4 | let WatRoute = Route.extend({ 5 | model: function() { 6 | return Foo.create(); 7 | } 8 | }); 9 | 10 | export default WatRoute; 11 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 20px; 3 | } 4 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |

Welcome to Ember

2 | 3 | {{outlet}} 4 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/arrays.hbs: -------------------------------------------------------------------------------- 1 | 6 | 11 | 16 | 21 | 22 | {{#link-to 'wat' class='link-wat'}}wat{{/link-to}} 23 | {{#link-to 'robots' class='link-robots'}}robots{{/link-to}} 24 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/custom-key.hbs: -------------------------------------------------------------------------------- 1 | 6 | {{#link-to 'wat' class='link-wat'}}wat{{/link-to}} 7 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/defaulta.hbs: -------------------------------------------------------------------------------- 1 | {{input class="name" value=model.name}} 2 | {{input class="funny" checked=model.funny type="checkbox"}} 3 |
{{model.nameIsDirty}}
4 |
{{model.nameIsPrimed}}
5 |
{{model.funnyIsDirty}}
6 |
{{model.funnyIsPrimed}}
7 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/defaultb.hbs: -------------------------------------------------------------------------------- 1 | {{input class="name" value=model.name}} 2 | {{input class="funny" checked=model.funny type="checkbox"}} 3 |
{{model.nameIsDirty}}
4 |
{{model.nameIsPrimed}}
5 |
{{model.funnyIsDirty}}
6 |
{{model.funnyIsPrimed}}
7 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/defaultx.hbs: -------------------------------------------------------------------------------- 1 | {{input class="name" value=model.name}} 2 | {{input class="funny" checked=model.funny type="checkbox"}} 3 |
{{model.nameIsDirty}}
4 |
{{model.nameIsPrimed}}
5 |
{{model.funnyIsDirty}}
6 |
{{model.funnyIsPrimed}}
7 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/delete-object.hbs: -------------------------------------------------------------------------------- 1 |

{{model.number}}

2 | 3 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/edit.hbs: -------------------------------------------------------------------------------- 1 | {{input class="name" value=model.name}} 2 |
{{model.nameIsDirty}}
3 |
{{model.nameIsPrimed}}
4 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/filters.hbs: -------------------------------------------------------------------------------- 1 | 6 | 11 | 16 | 21 | 22 | {{#link-to 'wat' class='link-wat'}}wat{{/link-to}} 23 | {{#link-to 'robots' class='link-robots'}}robots{{/link-to}} 24 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/isnone.hbs: -------------------------------------------------------------------------------- 1 | {{#each model as |item|}} 2 | {{input class="name" value=item.name}} 3 |
{{item.nameIsDirty}}
4 |
{{item.nameIsPrimed}}
5 | {{/each}} 6 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/relationships.hbs: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/robots.hbs: -------------------------------------------------------------------------------- 1 | 6 | 11 | 12 | {{#link-to 'wat' class='link-wat'}}wat{{/link-to}} 13 | {{#link-to 'filters' class='link-filters'}}filters{{/link-to}} 14 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/wat.hbs: -------------------------------------------------------------------------------- 1 | {{input class="name" value=model.name}} 2 | 3 | {{#link-to 'filters' class='link-filters'}}filters{{/link-to}} 4 | {{#link-to 'robots' class='link-robots'}}robots{{/link-to}} 5 | -------------------------------------------------------------------------------- /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/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/toranb/ember-cli-simple-store/ac6955430e26a7cd833aa3764228f4e3f8f96748/tests/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/helpers/destroy-app.js: -------------------------------------------------------------------------------- 1 | import { run } from '@ember/runloop'; 2 | 3 | export default function destroyApp(application) { 4 | run(application, 'destroy'); 5 | } 6 | -------------------------------------------------------------------------------- /tests/helpers/module-for-acceptance.js: -------------------------------------------------------------------------------- 1 | import { resolve } from 'rsvp'; 2 | import { module } from 'qunit'; 3 | import startApp from '../helpers/start-app'; 4 | import destroyApp from '../helpers/destroy-app'; 5 | 6 | export default function(name, options = {}) { 7 | module(name, { 8 | beforeEach() { 9 | this.application = startApp(); 10 | 11 | if (options.beforeEach) { 12 | return options.beforeEach.apply(this, arguments); 13 | } 14 | }, 15 | 16 | afterEach() { 17 | let afterEach = options.afterEach && options.afterEach.apply(this, arguments); 18 | return resolve(afterEach).then(() => destroyApp(this.application)); 19 | } 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /tests/helpers/qunit.js: -------------------------------------------------------------------------------- 1 | import { warn } from '@ember/debug'; 2 | 3 | let qunit = function deprecatedQunitHelper() { 4 | warn( 5 | "The ember-cli-simple-store `qunit` test helper has been moved. Please update your imports to `import { test } from 'ember-cli-simple-store/test-support/qunit';`", 6 | false, 7 | { id: 'ember-cli-simple-store.qunit-import' } 8 | ); 9 | } 10 | 11 | export { qunit }; 12 | -------------------------------------------------------------------------------- /tests/helpers/registration.js: -------------------------------------------------------------------------------- 1 | import SimpleStore from "ember-cli-simple-store/store"; 2 | 3 | export default function(owner, keys) { 4 | let factory = window.require("ember-resolver")["default"]; 5 | let resolver = factory.create({ namespace: { modulePrefix: "dummy" }}); 6 | 7 | owner.register("service:simple-store", SimpleStore); 8 | 9 | keys.forEach((key) => { 10 | owner.register(key, resolver.resolve("dummy@" + key)); 11 | }); 12 | 13 | return owner.lookup("service:simple-store"); 14 | } 15 | -------------------------------------------------------------------------------- /tests/helpers/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from '../../resolver'; 2 | import config from '../../config/environment'; 3 | 4 | const resolver = Resolver.create(); 5 | 6 | resolver.namespace = { 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix 9 | }; 10 | 11 | export default resolver; 12 | -------------------------------------------------------------------------------- /tests/helpers/start-app.js: -------------------------------------------------------------------------------- 1 | import { run } from '@ember/runloop'; 2 | import { merge } from '@ember/polyfills'; 3 | import Application from '../../app'; 4 | import config from '../../config/environment'; 5 | 6 | export default function startApp(attrs) { 7 | let attributes = merge({}, config.APP); 8 | attributes.autoboot = true; 9 | attributes = merge(attributes, attrs); // use defaults, but you can override; 10 | 11 | return run(() => { 12 | let application = Application.create(attributes); 13 | application.setupForTesting(); 14 | application.injectTestHelpers(); 15 | return application; 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /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/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/arrays-test.js: -------------------------------------------------------------------------------- 1 | import EmberObject, { observer } from '@ember/object'; 2 | import { getOwner } from '@ember/application'; 3 | import { moduleFor } from "ember-qunit"; 4 | import { test } from "ember-cli-simple-store/test-support/qunit"; 5 | 6 | let Thing, Stuff; 7 | 8 | moduleFor("service:simple-store", "arrays unit tests", { 9 | beforeEach: function () { 10 | const owner = getOwner(this); 11 | this.store = this.subject(); 12 | Stuff = EmberObject.extend(); 13 | 14 | Thing = EmberObject.extend({ 15 | observationCount: 0, 16 | name: "", 17 | stuff: this.store.find("stuff"), 18 | observeStuff: observer("stuff.[]", function () { 19 | let currentObservations = this.get("observationCount"); 20 | this.set("observationCount", currentObservations + 1); 21 | }) 22 | }); 23 | owner.register("model:stuff", Stuff); 24 | }, 25 | afterEach: function() { 26 | delete this.store; 27 | } 28 | }); 29 | 30 | test("observers are only notified once regardless of the number of models added to the store", function (assert) { 31 | let thing1 = Thing.create({name: "thing1"}); 32 | 33 | this.store.pushArray("stuff", [{ id: "1", name: "stuff1"}, { id: "2", name: "stuff2"}]); 34 | 35 | assert.equal(thing1.get("observationCount"), 1); 36 | }); 37 | -------------------------------------------------------------------------------- /tests/unit/default-attr-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { attr, Model } from "ember-cli-simple-store/model"; 3 | 4 | let Animal, leopard; 5 | 6 | module("default attr unit tests", { 7 | beforeEach: function() { 8 | Animal = Model.extend({ 9 | name: attr(""), 10 | fast: attr(false) 11 | }); 12 | } 13 | }); 14 | 15 | test("isDirty property on class will show correctly when set to default string value", function(assert){ 16 | leopard = Animal.create({name: "toran", fast: true}); 17 | assert.equal(false, leopard.get("isDirty")); 18 | leopard.set("name", "baz"); 19 | assert.equal(true, leopard.get("isDirty")); 20 | assert.equal("baz", leopard.get("name")); 21 | leopard.set("name", ""); 22 | assert.equal(true, leopard.get("isDirty")); 23 | assert.equal("", leopard.get("name")); 24 | assert.equal(true, leopard.get("nameIsDirty")); 25 | assert.equal(true, leopard.get("nameIsPrimed")); 26 | }); 27 | 28 | test("isDirty property on class will show correctly when set to default boolean value", function(assert){ 29 | leopard = Animal.create({name: "toran", fast: true}); 30 | assert.equal(false, leopard.get("isDirty")); 31 | leopard.set("fast", false); 32 | assert.equal(true, leopard.get("isDirty")); 33 | assert.equal(false, leopard.get("fast")); 34 | assert.equal(true, leopard.get("fastIsDirty")); 35 | assert.equal(true, leopard.get("fastIsPrimed")); 36 | }); 37 | 38 | test("save will reset isDirty", function(assert){ 39 | leopard = Animal.create({name: "toran", fast: true}); 40 | assert.equal(false, leopard.get("isDirty")); 41 | leopard.set("name", "baz"); 42 | assert.equal(true, leopard.get("isDirty")); 43 | leopard.save(); 44 | assert.equal(false, leopard.get("isDirty")); 45 | assert.equal("baz", leopard.get("name")); 46 | }); 47 | 48 | test("rollback will reset isDirty for string attr with default value", function(assert){ 49 | leopard = Animal.create({name: "toran", fast: true}); 50 | assert.equal(false, leopard.get("isDirty")); 51 | leopard.set("name", "baz"); 52 | assert.equal(true, leopard.get("isDirty")); 53 | leopard.rollback(); 54 | assert.equal(false, leopard.get("isDirty")); 55 | assert.equal("toran", leopard.get("name")); 56 | leopard.set("name", ""); 57 | assert.equal(true, leopard.get("isDirty")); 58 | leopard.rollback(); 59 | assert.equal(false, leopard.get("isDirty")); 60 | assert.equal("toran", leopard.get("name")); 61 | }); 62 | 63 | test("rollback will reset isDirty for boolean attr with default value", function(assert){ 64 | leopard = Animal.create({name: "toran", fast: true}); 65 | assert.equal(false, leopard.get("isDirty")); 66 | leopard.set("fast", false); 67 | assert.equal(true, leopard.get("isDirty")); 68 | leopard.rollback(); 69 | assert.equal(false, leopard.get("isDirty")); 70 | assert.equal(true, leopard.get("fast")); 71 | }); 72 | 73 | test("rollback after it has been saved will be a no-op", function(assert){ 74 | leopard = Animal.create({name: "toran", fast: true}); 75 | assert.equal(false, leopard.get("isDirty")); 76 | leopard.set("name", "baz"); 77 | assert.equal(true, leopard.get("isDirty")); 78 | leopard.set("name", "wat"); 79 | assert.equal(true, leopard.get("isDirty")); 80 | leopard.save(); 81 | assert.equal(false, leopard.get("isDirty")); 82 | assert.equal("wat", leopard.get("name")); 83 | leopard.rollback(); 84 | assert.equal(false, leopard.get("isDirty")); 85 | assert.equal("wat", leopard.get("name")); 86 | }); 87 | 88 | test("isDirty on the model is reset only after all values set back to original values", function(assert){ 89 | leopard = Animal.create({name: "toran", fast: true}); 90 | assert.equal("toran", leopard.get("name")); 91 | assert.equal(true, leopard.get("fast")); 92 | assert.equal(false, leopard.get("isDirty")); 93 | leopard.set("name", "foo"); 94 | leopard.set("fast", false); 95 | assert.equal(true, leopard.get("isDirty")); 96 | leopard.set("name", "toran"); 97 | assert.equal(true, leopard.get("isDirty")); 98 | leopard.set("fast", true); 99 | assert.equal(false, leopard.get("isDirty")); 100 | }); 101 | 102 | test("isDirty on the model is not reset when value is undefined and set to empty string", function(assert){ 103 | leopard = Animal.create({name: undefined, fast: true}); 104 | assert.equal(undefined, leopard.get("name")); 105 | assert.equal(false, leopard.get("isDirty")); 106 | leopard.set("name", "baz"); 107 | assert.equal(true, leopard.get("isDirty")); 108 | leopard.set("name", undefined); 109 | assert.equal(false, leopard.get("isDirty")); 110 | leopard.set("name", ""); 111 | assert.equal(false, leopard.get("isDirty")); 112 | }); 113 | 114 | test("isDirty on the model is not reset when value is null and set to empty string", function(assert){ 115 | leopard = Animal.create({name: null, fast: true}); 116 | assert.equal(null, leopard.get("name")); 117 | assert.equal(false, leopard.get("isDirty")); 118 | leopard.set("name", "baz"); 119 | assert.equal(true, leopard.get("isDirty")); 120 | leopard.set("name", undefined); 121 | assert.equal(true, leopard.get("isDirty")); 122 | leopard.set("name", null); 123 | assert.equal(false, leopard.get("isDirty")); 124 | leopard.set("name", ""); 125 | assert.equal(false, leopard.get("isDirty")); 126 | }); 127 | 128 | test("isDirty on the model is not reset when value is undefined and set to default boolean value", function(assert){ 129 | leopard = Animal.create({name: "toran", fast: undefined}); 130 | assert.equal(undefined, leopard.get("fast")); 131 | assert.equal(false, leopard.get("isDirty")); 132 | leopard.set("fast", true); 133 | assert.equal(true, leopard.get("isDirty")); 134 | leopard.set("fast", undefined); 135 | assert.equal(false, leopard.get("isDirty")); 136 | leopard.set("fast", false); 137 | assert.equal(false, leopard.get("isDirty")); 138 | }); 139 | 140 | test("isDirty on the model is not reset when value is null and set to default boolean value", function(assert){ 141 | leopard = Animal.create({name: "toran", fast: null}); 142 | assert.equal(undefined, leopard.get("fast")); 143 | assert.equal(false, leopard.get("isDirty")); 144 | leopard.set("fast", true); 145 | assert.equal(true, leopard.get("isDirty")); 146 | leopard.set("fast", undefined); 147 | assert.equal(true, leopard.get("isDirty")); 148 | leopard.set("fast", null); 149 | assert.equal(false, leopard.get("isDirty")); 150 | leopard.set("fast", false); 151 | assert.equal(false, leopard.get("isDirty")); 152 | }); 153 | 154 | test("isDirty on the model is reset when value starts as empty array and set back to empty array value", function(assert){ 155 | let Batman = Model.extend({batarangs: attr()}); 156 | let watman = Batman.create({batarangs: []}); 157 | assert.deepEqual([], watman.get("batarangs")); 158 | assert.equal(false, watman.get("isDirty")); 159 | watman.set("batarangs", [1, 2]); 160 | assert.equal(true, watman.get("isDirty")); 161 | watman.set("batarangs", []); 162 | assert.equal(false, watman.get("isDirty")); 163 | watman.set("batarangs", [3]); 164 | assert.equal(true, watman.get("isDirty")); 165 | watman.set("batarangs", []); 166 | assert.equal(false, watman.get("isDirty")); 167 | }); 168 | 169 | test("isDirty on the model is reset when value starts as empty array and set back to empty array value and default value is empty array", function(assert){ 170 | let Batman = Model.extend({batarangs: attr([])}); 171 | let watman = Batman.create({batarangs: []}); 172 | assert.deepEqual([], watman.get("batarangs")); 173 | assert.equal(false, watman.get("isDirty")); 174 | watman.set("batarangs", [1, 2]); 175 | assert.equal(true, watman.get("isDirty")); 176 | watman.set("batarangs", []); 177 | assert.equal(false, watman.get("isDirty")); 178 | watman.set("batarangs", [3]); 179 | assert.equal(true, watman.get("isDirty")); 180 | watman.set("batarangs", []); 181 | assert.equal(false, watman.get("isDirty")); 182 | }); 183 | 184 | test("isDirty on the model is reset when value starts as undefined and set to default array value", function(assert){ 185 | let Batman = Model.extend({batarangs: attr([])}); 186 | let watman = Batman.create({batarangs: undefined}); 187 | assert.equal(undefined, watman.get("batarangs")); 188 | assert.equal(false, watman.get("isDirty")); 189 | watman.set("batarangs", [1, 2]); 190 | assert.equal(true, watman.get("isDirty")); 191 | watman.set("batarangs", []); 192 | assert.equal(false, watman.get("isDirty")); 193 | }); 194 | 195 | test("rolling back a model with no changes is a no-op", function(assert){ 196 | leopard = Animal.create({name: "toran", fast: true}); 197 | assert.equal("toran", leopard.get("name")); 198 | assert.equal(true, leopard.get("fast")); 199 | assert.equal(false, leopard.get("isDirty")); 200 | assert.equal(undefined, leopard.get("nameIsDirty")); 201 | assert.equal(undefined, leopard.get("nameIsPrimed")); 202 | assert.equal(undefined, leopard.get("fastIsDirty")); 203 | assert.equal(undefined, leopard.get("fastIsPrimed")); 204 | 205 | leopard.rollback(); 206 | assert.equal("toran", leopard.get("name")); 207 | assert.equal(true, leopard.get("fast")); 208 | assert.equal(false, leopard.get("isDirty")); 209 | assert.equal(undefined, leopard.get("nameIsDirty")); 210 | assert.equal(undefined, leopard.get("nameIsPrimed")); 211 | assert.equal(undefined, leopard.get("fastIsDirty")); 212 | assert.equal(undefined, leopard.get("fastIsPrimed")); 213 | }); 214 | 215 | test("rolling back and saving a new model with no changes is a no-op", function(assert){ 216 | leopard = Animal.create(); 217 | assert.equal(undefined, leopard.get("name")); 218 | assert.equal(undefined, leopard.get("fast")); 219 | assert.equal(false, leopard.get("isDirty")); 220 | assert.equal(undefined, leopard.get("nameIsDirty")); 221 | assert.equal(undefined, leopard.get("nameIsPrimed")); 222 | assert.equal(undefined, leopard.get("fastIsDirty")); 223 | assert.equal(undefined, leopard.get("fastIsPrimed")); 224 | leopard.set("name", "wat"); 225 | 226 | leopard.rollback(); 227 | assert.equal(undefined, leopard.get("name")); 228 | assert.equal(undefined, leopard.get("fast")); 229 | assert.equal(false, leopard.get("isDirty")); 230 | assert.equal(undefined, leopard.get("nameIsDirty")); 231 | assert.equal(undefined, leopard.get("nameIsPrimed")); 232 | assert.equal(undefined, leopard.get("fastIsDirty")); 233 | assert.equal(undefined, leopard.get("fastIsPrimed")); 234 | 235 | leopard.save(); 236 | assert.equal(undefined, leopard.get("name")); 237 | assert.equal(undefined, leopard.get("fast")); 238 | assert.equal(false, leopard.get("isDirty")); 239 | assert.equal(undefined, leopard.get("nameIsDirty")); 240 | assert.equal(undefined, leopard.get("nameIsPrimed")); 241 | assert.equal(undefined, leopard.get("fastIsDirty")); 242 | assert.equal(undefined, leopard.get("fastIsPrimed")); 243 | 244 | leopard.set("name", "wat"); 245 | leopard.save(); 246 | assert.equal("wat", leopard.get("name")); 247 | assert.equal(undefined, leopard.get("fast")); 248 | assert.equal(false, leopard.get("isDirty")); 249 | assert.equal(undefined, leopard.get("nameIsDirty")); 250 | assert.equal(undefined, leopard.get("nameIsPrimed")); 251 | assert.equal(undefined, leopard.get("fastIsDirty")); 252 | assert.equal(undefined, leopard.get("fastIsPrimed")); 253 | }); 254 | 255 | test("isDirty is true if the values are cleared out", function(assert){ 256 | leopard = Animal.create({name: "toran", fast: true}); 257 | assert.equal(false, leopard.get("isDirty")); 258 | leopard.set("name", ""); 259 | assert.equal(true, leopard.get("isDirty")); 260 | leopard.set("name", "toran"); 261 | assert.equal(false, leopard.get("isDirty")); 262 | leopard.set("fast", false); 263 | assert.equal(true, leopard.get("isDirty")); 264 | leopard.set("fast", true); 265 | assert.equal(false, leopard.get("isDirty")); 266 | }); 267 | -------------------------------------------------------------------------------- /tests/unit/model-test.js: -------------------------------------------------------------------------------- 1 | import { computed } from '@ember/object'; 2 | import { module, test } from 'qunit'; 3 | import { attr, Model } from "ember-cli-simple-store/model"; 4 | 5 | let Person, brandon; 6 | let data = {id: 1, firstName: "Brandon", lastName: "Williams"}; 7 | 8 | module("model unit tests", { 9 | beforeEach: function() { 10 | Person = Model.extend({ 11 | wat: "", 12 | firstName: attr(), 13 | lastName: attr(), 14 | fullName: computed(function() { 15 | let first = this.get("firstName"); 16 | let last = this.get("lastName"); 17 | return first + " " + last; 18 | }).property("firstName", "lastName") 19 | }); 20 | } 21 | }); 22 | 23 | test("attr will serve as both gettr and settr", function(assert){ 24 | brandon = Person.create(data); 25 | assert.equal("Brandon", brandon.get("firstName")); 26 | assert.equal("Williams", brandon.get("lastName")); 27 | assert.equal("Brandon Williams", brandon.get("fullName")); 28 | brandon.set("firstName", "x"); 29 | brandon.set("lastName", "y"); 30 | assert.equal("x", brandon.get("firstName")); 31 | assert.equal("y", brandon.get("lastName")); 32 | assert.equal("x y", brandon.get("fullName")); 33 | }); 34 | 35 | test("isDirty property on class will update if attr is changed", function(assert){ 36 | brandon = Person.create(data); 37 | assert.equal(false, brandon.get("isDirty")); 38 | brandon.set("firstName", "baz"); 39 | assert.equal(true, brandon.get("isDirty")); 40 | }); 41 | 42 | test("isNotDirty property on class will reflect the inverse of isDirty computed", function(assert){ 43 | brandon = Person.create(data); 44 | assert.equal(false, brandon.get("isDirty")); 45 | assert.equal(true, brandon.get("isNotDirty")); 46 | brandon.set("firstName", "baz"); 47 | assert.equal(true, brandon.get("isDirty")); 48 | assert.equal(false, brandon.get("isNotDirty")); 49 | brandon.set("firstName", "Brandon"); 50 | assert.equal(false, brandon.get("isDirty")); 51 | assert.equal(true, brandon.get("isNotDirty")); 52 | }); 53 | 54 | test("isDirty property on class will not update if non attr is changed", function(assert){ 55 | brandon = Person.create(data); 56 | assert.equal(false, brandon.get("isDirty")); 57 | brandon.set("wat", "baz"); 58 | assert.equal(false, brandon.get("isDirty")); 59 | }); 60 | 61 | test("save will reset isDirty", function(assert){ 62 | brandon = Person.create(data); 63 | assert.equal(false, brandon.get("isDirty")); 64 | brandon.set("firstName", "baz"); 65 | assert.equal(true, brandon.get("isDirty")); 66 | brandon.save(); 67 | assert.equal(false, brandon.get("isDirty")); 68 | assert.equal("baz", brandon.get("firstName")); 69 | }); 70 | 71 | test("save will update internal state", function(assert){ 72 | brandon = Person.create(data); 73 | let preState = brandon.get("_oldState"); 74 | assert.equal(2, Object.keys(preState).length); 75 | assert.equal("Brandon", preState["firstName"]); 76 | assert.equal("Williams", preState["lastName"]); 77 | 78 | brandon.set("firstName", "baz"); 79 | let initState = brandon.get("_oldState"); 80 | assert.equal(2, Object.keys(initState).length); 81 | assert.equal("Brandon", initState["firstName"]); 82 | assert.equal("Williams", initState["lastName"]); 83 | 84 | brandon.save(); 85 | let postState = brandon.get("_oldState"); 86 | assert.equal(2, Object.keys(postState).length); 87 | assert.equal("baz", postState["firstName"]); 88 | assert.equal("Williams", postState["lastName"]); 89 | }); 90 | 91 | test("rollback will reset isDirty", function(assert){ 92 | brandon = Person.create(data); 93 | assert.equal(false, brandon.get("isDirty")); 94 | brandon.set("firstName", "baz"); 95 | assert.equal(true, brandon.get("isDirty")); 96 | brandon.rollback(); 97 | assert.equal(false, brandon.get("isDirty")); 98 | assert.equal("Brandon", brandon.get("firstName")); 99 | }); 100 | 101 | test("rollback will not alter the internal state", function(assert){ 102 | brandon = Person.create(data); 103 | let preState = brandon.get("_oldState"); 104 | assert.equal(2, Object.keys(preState).length); 105 | assert.equal("Brandon", preState["firstName"]); 106 | assert.equal("Williams", preState["lastName"]); 107 | 108 | brandon.set("firstName", "baz"); 109 | let initState = brandon.get("_oldState"); 110 | assert.equal(2, Object.keys(initState).length); 111 | assert.equal("Brandon", initState["firstName"]); 112 | assert.equal("Williams", initState["lastName"]); 113 | 114 | brandon.rollback(); 115 | let postState = brandon.get("_oldState"); 116 | assert.equal(2, Object.keys(postState).length); 117 | assert.equal("Brandon", postState["firstName"]); 118 | assert.equal("Williams", postState["lastName"]); 119 | }); 120 | 121 | test("rollback after it has been saved will be a no-op", function(assert){ 122 | brandon = Person.create(data); 123 | assert.equal(false, brandon.get("isDirty")); 124 | brandon.set("firstName", "baz"); 125 | assert.equal(true, brandon.get("isDirty")); 126 | brandon.set("firstName", "wat"); 127 | assert.equal(true, brandon.get("isDirty")); 128 | brandon.save(); 129 | assert.equal(false, brandon.get("isDirty")); 130 | assert.equal("wat", brandon.get("firstName")); 131 | brandon.rollback(); 132 | assert.equal(false, brandon.get("isDirty")); 133 | assert.equal("wat", brandon.get("firstName")); 134 | }); 135 | 136 | test("internal state will be only set the first time a property is set", function(assert){ 137 | brandon = Person.create(data); 138 | let preState = brandon.get("_oldState"); 139 | assert.equal(2, Object.keys(preState).length); 140 | assert.equal("Brandon", preState["firstName"]); 141 | assert.equal("Williams", preState["lastName"]); 142 | 143 | brandon.set("firstName", "baz"); 144 | let initState = brandon.get("_oldState"); 145 | assert.equal(2, Object.keys(initState).length); 146 | assert.equal("Brandon", initState["firstName"]); 147 | assert.equal("Williams", initState["lastName"]); 148 | brandon.set("_oldState.firstName", "nogo"); 149 | brandon.set("_oldState.lastName", "nope"); 150 | 151 | brandon.set("firstName", "baz"); 152 | let postState = brandon.get("_oldState"); 153 | assert.equal(2, Object.keys(postState).length); 154 | assert.equal("nogo", postState["firstName"]); 155 | assert.equal("nope", postState["lastName"]); 156 | }); 157 | 158 | test("isDirty on the individual property will update if attr is changed", function(assert){ 159 | brandon = Person.create(data); 160 | assert.equal("Brandon", brandon.get("firstName")); 161 | assert.equal("Williams", brandon.get("lastName")); 162 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 163 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 164 | assert.equal(undefined, brandon.get("lastNameIsDirty")); 165 | assert.equal(undefined, brandon.get("lastNameIsPrimed")); 166 | brandon.set("lastName", "wat"); 167 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 168 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 169 | assert.equal(true, brandon.get("lastNameIsDirty")); 170 | assert.equal(true, brandon.get("lastNameIsPrimed")); 171 | }); 172 | 173 | test("isDirty on the individual property is reset after save", function(assert){ 174 | brandon = Person.create(data); 175 | assert.equal("Brandon", brandon.get("firstName")); 176 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 177 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 178 | brandon.set("firstName", "baz"); 179 | assert.equal(true, brandon.get("firstNameIsDirty")); 180 | assert.equal(true, brandon.get("firstNameIsPrimed")); 181 | brandon.save(); 182 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 183 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 184 | }); 185 | 186 | test("isDirty on the model is reset only after all values set back to original values", function(assert){ 187 | brandon = Person.create(data); 188 | assert.equal("Brandon", brandon.get("firstName")); 189 | assert.equal("Williams", brandon.get("lastName")); 190 | assert.equal(false, brandon.get("isDirty")); 191 | brandon.set("firstName", "foo"); 192 | brandon.set("lastName", "bar"); 193 | assert.equal(true, brandon.get("isDirty")); 194 | brandon.set("firstName", "Brandon"); 195 | assert.equal(true, brandon.get("isDirty")); 196 | brandon.set("lastName", "Williams"); 197 | assert.equal(false, brandon.get("isDirty")); 198 | }); 199 | 200 | test("isDirty on the model is reset after value set back to original value", function(assert){ 201 | brandon = Person.create(data); 202 | assert.equal("Brandon", brandon.get("firstName")); 203 | assert.equal(false, brandon.get("isDirty")); 204 | brandon.set("firstName", "baz"); 205 | assert.equal(true, brandon.get("isDirty")); 206 | brandon.set("firstName", "Brandon"); 207 | assert.equal(false, brandon.get("isDirty")); 208 | }); 209 | 210 | test("isDirty on the model is updated when value is empty string and later set to undefined", function(assert){ 211 | brandon = Person.create({id: 1, firstName: "", lastName: "Williams"}); 212 | assert.equal("", brandon.get("firstName")); 213 | assert.equal(false, brandon.get("isDirty")); 214 | brandon.set("firstName", "baz"); 215 | assert.equal(true, brandon.get("isDirty")); 216 | brandon.set("firstName", ""); 217 | assert.equal(false, brandon.get("isDirty")); 218 | brandon.set("firstName", undefined); 219 | assert.equal(true, brandon.get("isDirty")); 220 | }); 221 | 222 | test("isDirty on the model is updated when value is undefined then set to undefined and later empty string", function(assert){ 223 | brandon = Person.create({id: 1, firstName: undefined, lastName: "Williams"}); 224 | assert.equal(undefined, brandon.get("firstName")); 225 | assert.equal(false, brandon.get("isDirty")); 226 | brandon.set("firstName", "baz"); 227 | assert.equal(true, brandon.get("isDirty")); 228 | brandon.set("firstName", undefined); 229 | assert.equal(false, brandon.get("isDirty")); 230 | brandon.set("firstName", ""); 231 | assert.equal(true, brandon.get("isDirty")); 232 | }); 233 | 234 | test("isDirty on the model is updated when value is null then set to empty string and later null", function(assert){ 235 | brandon = Person.create({id: 1, firstName: null, lastName: "Williams"}); 236 | assert.equal(null, brandon.get("firstName")); 237 | assert.equal(false, brandon.get("isDirty")); 238 | brandon.set("firstName", "baz"); 239 | assert.equal(true, brandon.get("isDirty")); 240 | brandon.set("firstName", ""); 241 | assert.equal(true, brandon.get("isDirty")); 242 | brandon.set("firstName", null); 243 | assert.equal(false, brandon.get("isDirty")); 244 | }); 245 | 246 | test("isDirty on the model is updated when value is undefined then set to empty string and later undefined", function(assert){ 247 | brandon = Person.create({id: 1, firstName: undefined, lastName: "Williams"}); 248 | assert.equal(undefined, brandon.get("firstName")); 249 | assert.equal(false, brandon.get("isDirty")); 250 | brandon.set("firstName", "baz"); 251 | assert.equal(true, brandon.get("isDirty")); 252 | brandon.set("firstName", ""); 253 | assert.equal(true, brandon.get("isDirty")); 254 | brandon.set("firstName", undefined); 255 | assert.equal(false, brandon.get("isDirty")); 256 | }); 257 | 258 | test("isDirty on the model is reset when original value is null and set back to null", function(assert){ 259 | brandon = Person.create({id: 1, firstName: null, lastName: "Williams"}); 260 | assert.equal(undefined, brandon.get("firstName")); 261 | assert.equal(false, brandon.get("isDirty")); 262 | brandon.set("firstName", "baz"); 263 | assert.equal(true, brandon.get("isDirty")); 264 | brandon.set("firstName", null); 265 | assert.equal(false, brandon.get("isDirty")); 266 | assert.equal(undefined, brandon.get("firstName")); 267 | }); 268 | 269 | test("isDirty on the model is reset when original value is 0 and set back to 0", function(assert){ 270 | brandon = Person.create({id: 1, firstName: 0, lastName: "Williams"}); 271 | assert.equal(0, brandon.get("firstName")); 272 | assert.equal(false, brandon.get("isDirty")); 273 | brandon.set("firstName", "baz"); 274 | assert.equal(true, brandon.get("isDirty")); 275 | brandon.set("firstName", 0); 276 | assert.equal(false, brandon.get("isDirty")); 277 | brandon.set("firstName", "0"); 278 | assert.equal(true, brandon.get("isDirty")); 279 | }); 280 | 281 | test("isDirty on the individual property is reset after value set back to original value", function(assert){ 282 | brandon = Person.create(data); 283 | assert.equal("Brandon", brandon.get("firstName")); 284 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 285 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 286 | brandon.set("firstName", "baz"); 287 | assert.equal(true, brandon.get("firstNameIsDirty")); 288 | assert.equal(true, brandon.get("firstNameIsPrimed")); 289 | brandon.set("firstName", "Brandon"); 290 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 291 | assert.equal(true, brandon.get("firstNameIsPrimed")); 292 | }); 293 | 294 | test("isDirty on the individual property is reset after rollback", function(assert){ 295 | brandon = Person.create(data); 296 | assert.equal("Brandon", brandon.get("firstName")); 297 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 298 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 299 | brandon.set("firstName", "baz"); 300 | assert.equal(true, brandon.get("firstNameIsDirty")); 301 | assert.equal(true, brandon.get("firstNameIsPrimed")); 302 | brandon.rollback(); 303 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 304 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 305 | }); 306 | 307 | test("rollback after it has been saved will be a no-op at the property level also", function(assert){ 308 | brandon = Person.create(data); 309 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 310 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 311 | brandon.set("firstName", "baz"); 312 | assert.equal(true, brandon.get("firstNameIsDirty")); 313 | assert.equal(true, brandon.get("firstNameIsPrimed")); 314 | brandon.save(); 315 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 316 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 317 | assert.equal("baz", brandon.get("firstName")); 318 | brandon.rollback(); 319 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 320 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 321 | assert.equal("baz", brandon.get("firstName")); 322 | }); 323 | 324 | test("a prime of the attr with an empty string will alter isDirty but not isPrimed", function(assert) { 325 | brandon = Person.create(); 326 | assert.equal(undefined, brandon.get("firstName")); 327 | assert.equal(false, brandon.get("isDirty")); 328 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 329 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 330 | brandon.set("firstName", ""); 331 | assert.equal("", brandon.get("firstName")); 332 | assert.equal(true, brandon.get("isDirty")); 333 | assert.equal(true, brandon.get("firstNameIsDirty")); 334 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 335 | }); 336 | 337 | test("a prime of the attr with a non empty string will alter both isDirty and isPrimed", function(assert) { 338 | brandon = Person.create(); 339 | assert.equal(undefined, brandon.get("firstName")); 340 | assert.equal(false, brandon.get("isDirty")); 341 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 342 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 343 | brandon.set("firstName", "x"); 344 | assert.equal("x", brandon.get("firstName")); 345 | assert.equal(true, brandon.get("isDirty")); 346 | assert.equal(true, brandon.get("firstNameIsDirty")); 347 | assert.equal(true, brandon.get("firstNameIsPrimed")); 348 | }); 349 | 350 | test("isDirty is smart enough to know when the attr has been restored", function(assert){ 351 | brandon = Person.create(data); 352 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 353 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 354 | brandon.set("firstName", "baz"); 355 | assert.equal(true, brandon.get("firstNameIsDirty")); 356 | assert.equal(true, brandon.get("firstNameIsPrimed")); 357 | assert.equal("baz", brandon.get("firstName")); 358 | brandon.set("firstName", "Brandon"); 359 | assert.equal("Brandon", brandon.get("firstName")); 360 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 361 | assert.equal(true, brandon.get("firstNameIsPrimed")); 362 | }); 363 | 364 | test("rolling back a model with no changes is a no-op", function(assert){ 365 | brandon = Person.create(data); 366 | assert.equal("Brandon", brandon.get("firstName")); 367 | assert.equal("Williams", brandon.get("lastName")); 368 | assert.equal(false, brandon.get("isDirty")); 369 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 370 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 371 | assert.equal(undefined, brandon.get("lastNameIsDirty")); 372 | assert.equal(undefined, brandon.get("lastNameIsPrimed")); 373 | 374 | brandon.rollback(); 375 | assert.equal("Brandon", brandon.get("firstName")); 376 | assert.equal("Williams", brandon.get("lastName")); 377 | assert.equal(false, brandon.get("isDirty")); 378 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 379 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 380 | assert.equal(undefined, brandon.get("lastNameIsDirty")); 381 | assert.equal(undefined, brandon.get("lastNameIsPrimed")); 382 | }); 383 | 384 | test("saving a model with no changes is a no-op", function(assert){ 385 | brandon = Person.create(data); 386 | assert.equal("Brandon", brandon.get("firstName")); 387 | assert.equal("Williams", brandon.get("lastName")); 388 | assert.equal(false, brandon.get("isDirty")); 389 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 390 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 391 | assert.equal(undefined, brandon.get("lastNameIsDirty")); 392 | assert.equal(undefined, brandon.get("lastNameIsPrimed")); 393 | 394 | brandon.save(); 395 | assert.equal("Brandon", brandon.get("firstName")); 396 | assert.equal("Williams", brandon.get("lastName")); 397 | assert.equal(false, brandon.get("isDirty")); 398 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 399 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 400 | assert.equal(undefined, brandon.get("lastNameIsDirty")); 401 | assert.equal(undefined, brandon.get("lastNameIsPrimed")); 402 | }); 403 | 404 | test("rolling back a model after initial state is modified should revert to original value", function(assert){ 405 | brandon = Person.create(data); 406 | assert.equal("Brandon", brandon.get("firstName")); 407 | brandon.set("firstName", ""); 408 | assert.equal("", brandon.get("firstName")); 409 | brandon.rollback(); 410 | assert.equal("Brandon", brandon.get("firstName")); 411 | brandon.set("firstName", undefined); 412 | assert.equal(undefined, brandon.get("firstName")); 413 | brandon.rollback(); 414 | assert.equal("Brandon", brandon.get("firstName")); 415 | brandon.set("firstName", null); 416 | assert.equal(null, brandon.get("firstName")); 417 | brandon.rollback(); 418 | assert.equal("Brandon", brandon.get("firstName")); 419 | assert.equal("Williams", brandon.get("lastName")); 420 | assert.equal(false, brandon.get("isDirty")); 421 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 422 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 423 | assert.equal(undefined, brandon.get("lastNameIsDirty")); 424 | assert.equal(undefined, brandon.get("lastNameIsPrimed")); 425 | }); 426 | 427 | test("rolling back a model after initial state is modified should revert to original value when array", function(assert){ 428 | let Beast = Model.extend({things: attr()}); 429 | let beast = Beast.create({id: 1, things: [1, 2]}); 430 | assert.deepEqual([1, 2], beast.get("things")); 431 | beast.set("things", [1]); 432 | assert.equal(true, beast.get("isDirty")); 433 | assert.deepEqual([1], beast.get("things")); 434 | assert.equal(true, beast.get("thingsIsDirty")); 435 | assert.equal(true, beast.get("thingsIsPrimed")); 436 | beast.rollback(); 437 | assert.equal(false, beast.get("isDirty")); 438 | assert.deepEqual([1, 2], beast.get("things")); 439 | assert.equal(undefined, beast.get("thingsIsDirty")); 440 | assert.equal(undefined, beast.get("thingsIsPrimed")); 441 | beast.set("things", undefined); 442 | assert.equal(true, beast.get("isDirty")); 443 | assert.equal(undefined, beast.get("things")); 444 | assert.equal(true, beast.get("thingsIsDirty")); 445 | assert.equal(true, beast.get("thingsIsPrimed")); 446 | beast.rollback(); 447 | assert.equal(false, beast.get("isDirty")); 448 | assert.deepEqual([1, 2], beast.get("things")); 449 | assert.equal(undefined, beast.get("thingsIsDirty")); 450 | assert.equal(undefined, beast.get("thingsIsPrimed")); 451 | beast.set("things", null); 452 | assert.equal(true, beast.get("isDirty")); 453 | assert.equal(null, beast.get("things")); 454 | assert.equal(true, beast.get("thingsIsDirty")); 455 | assert.equal(true, beast.get("thingsIsPrimed")); 456 | beast.rollback(); 457 | assert.equal(false, beast.get("isDirty")); 458 | assert.deepEqual([1, 2], beast.get("things")); 459 | assert.equal(false, beast.get("isDirty")); 460 | assert.equal(undefined, beast.get("thingsIsDirty")); 461 | assert.equal(undefined, beast.get("thingsIsPrimed")); 462 | }); 463 | 464 | test("rolling back and saving a new model with no changes is a no-op", function(assert){ 465 | brandon = Person.create(); 466 | assert.equal(undefined, brandon.get("firstName")); 467 | assert.equal(undefined, brandon.get("lastName")); 468 | assert.equal(false, brandon.get("isDirty")); 469 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 470 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 471 | assert.equal(undefined, brandon.get("lastNameIsDirty")); 472 | assert.equal(undefined, brandon.get("lastNameIsPrimed")); 473 | brandon.set("firstName", "wat"); 474 | 475 | brandon.rollback(); 476 | assert.equal(undefined, brandon.get("firstName")); 477 | assert.equal(undefined, brandon.get("lastName")); 478 | assert.equal(false, brandon.get("isDirty")); 479 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 480 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 481 | assert.equal(undefined, brandon.get("lastNameIsDirty")); 482 | assert.equal(undefined, brandon.get("lastNameIsPrimed")); 483 | 484 | brandon.save(); 485 | assert.equal(undefined, brandon.get("firstName")); 486 | assert.equal(undefined, brandon.get("lastName")); 487 | assert.equal(false, brandon.get("isDirty")); 488 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 489 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 490 | assert.equal(undefined, brandon.get("lastNameIsDirty")); 491 | assert.equal(undefined, brandon.get("lastNameIsPrimed")); 492 | 493 | brandon.set("firstName", "wat"); 494 | brandon.save(); 495 | assert.equal("wat", brandon.get("firstName")); 496 | assert.equal(undefined, brandon.get("lastName")); 497 | assert.equal(false, brandon.get("isDirty")); 498 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 499 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 500 | assert.equal(undefined, brandon.get("lastNameIsDirty")); 501 | assert.equal(undefined, brandon.get("lastNameIsPrimed")); 502 | }); 503 | 504 | test("saving and rolling back a new model immediately is a no-op", function(assert){ 505 | brandon = Person.create(); 506 | assert.equal(undefined, brandon.get("firstName")); 507 | assert.equal(undefined, brandon.get("lastName")); 508 | assert.equal(false, brandon.get("isDirty")); 509 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 510 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 511 | assert.equal(undefined, brandon.get("lastNameIsDirty")); 512 | assert.equal(undefined, brandon.get("lastNameIsPrimed")); 513 | 514 | brandon.set("firstName", "Brandon"); 515 | brandon.set("lastName", "Williams"); 516 | brandon.save(); 517 | brandon.rollback(); 518 | assert.equal("Brandon", brandon.get("firstName")); 519 | assert.equal("Williams", brandon.get("lastName")); 520 | assert.equal(false, brandon.get("isDirty")); 521 | assert.equal(undefined, brandon.get("firstNameIsDirty")); 522 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 523 | assert.equal(undefined, brandon.get("lastNameIsDirty")); 524 | assert.equal(undefined, brandon.get("lastNameIsPrimed")); 525 | }); 526 | 527 | test("isDirty and isPrimed are true if the values are cleared out", function(assert){ 528 | brandon = Person.create({id: 1, firstName: "x"}); 529 | assert.equal(false, brandon.get("isDirty")); 530 | brandon.set("firstName", ""); 531 | assert.equal(true, brandon.get("isDirty")); 532 | assert.equal(true, brandon.get("firstNameIsDirty")); 533 | assert.equal(true, brandon.get("firstNameIsPrimed")); 534 | }); 535 | 536 | test("isPrimed is undefined when attr is undefined and later set to empty string", function(assert){ 537 | brandon = Person.create({id: 1, firstName: undefined, lastName: "Williams"}); 538 | assert.equal(undefined, brandon.get("firstName")); 539 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 540 | brandon.set("firstName", ""); 541 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 542 | }); 543 | 544 | test("isPrimed is true when attr is empty string and later set to undefined", function(assert){ 545 | brandon = Person.create({id: 1, firstName: "", lastName: "Williams"}); 546 | assert.equal("", brandon.get("firstName")); 547 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 548 | brandon.set("firstName", undefined); 549 | assert.equal(true, brandon.get("firstNameIsPrimed")); 550 | assert.equal(true, brandon.get("isDirty")); 551 | }); 552 | 553 | test("isPrimed is true when attr is undefined and later set to valid string", function(assert){ 554 | brandon = Person.create({id: 1, firstName: undefined, lastName: "Williams"}); 555 | assert.equal(undefined, brandon.get("firstName")); 556 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 557 | brandon.set("firstName", ""); 558 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 559 | brandon.set("firstName", "x"); 560 | assert.equal(true, brandon.get("firstNameIsPrimed")); 561 | }); 562 | 563 | test("isPrimed is true when attr is undefined then string and again undefined", function(assert){ 564 | brandon = Person.create({id: 1, firstName: undefined, lastName: "Williams"}); 565 | assert.equal(undefined, brandon.get("firstName")); 566 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 567 | brandon.set("firstName", ""); 568 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 569 | brandon.set("firstName", "x"); 570 | assert.equal(true, brandon.get("firstNameIsPrimed")); 571 | brandon.set("firstName", undefined); 572 | assert.equal(true, brandon.get("firstNameIsPrimed")); 573 | }); 574 | 575 | test("isPrimed is undefined after save and rollback", function(assert){ 576 | brandon = Person.create({id: 1, firstName: undefined, lastName: "Williams"}); 577 | assert.equal(undefined, brandon.get("firstName")); 578 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 579 | brandon.set("firstName", ""); 580 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 581 | brandon.set("firstName", "x"); 582 | assert.equal(true, brandon.get("firstNameIsPrimed")); 583 | brandon.save(); 584 | assert.equal("x", brandon.get("firstName")); 585 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 586 | brandon.set("firstName", "xe"); 587 | assert.equal(true, brandon.get("firstNameIsPrimed")); 588 | brandon.rollback(); 589 | assert.equal("x", brandon.get("firstName")); 590 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 591 | }); 592 | 593 | test("isPrimed is undefined after save and rollback on new object", function(assert){ 594 | brandon = Person.create(); 595 | assert.equal(undefined, brandon.get("firstName")); 596 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 597 | brandon.rollback(); 598 | assert.equal(undefined, brandon.get("firstName")); 599 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 600 | brandon.save(); 601 | assert.equal(undefined, brandon.get("firstName")); 602 | assert.equal(undefined, brandon.get("firstNameIsPrimed")); 603 | }); 604 | 605 | test("isPrimed is null after save and rollback (starting with null value)", function(assert){ 606 | brandon = Person.create({id: 1, firstName: null, lastName: "Williams"}); 607 | assert.equal(null, brandon.get("firstName")); 608 | assert.equal(null, brandon.get("firstNameIsPrimed")); 609 | brandon.set("firstName", ""); 610 | assert.equal(null, brandon.get("firstNameIsPrimed")); 611 | brandon.set("firstName", "x"); 612 | assert.equal(true, brandon.get("firstNameIsPrimed")); 613 | brandon.save(); 614 | assert.equal("x", brandon.get("firstName")); 615 | assert.equal(null, brandon.get("firstNameIsPrimed")); 616 | brandon.set("firstName", "xe"); 617 | assert.equal(true, brandon.get("firstNameIsPrimed")); 618 | brandon.rollback(); 619 | assert.equal("x", brandon.get("firstName")); 620 | assert.equal(null, brandon.get("firstNameIsPrimed")); 621 | }); 622 | -------------------------------------------------------------------------------- /tests/unit/record-array-test.js: -------------------------------------------------------------------------------- 1 | import EmberObject from '@ember/object'; 2 | import { run } from '@ember/runloop'; 3 | import { moduleFor, test } from 'ember-qunit'; 4 | import FilteredRecordArray from 'ember-cli-simple-store/models/filtered-record-array'; 5 | 6 | function createFilter(simpleStore) { 7 | simpleStore.push("foo", { id: 1, name: "toran" }); 8 | 9 | return simpleStore.find('foo', (foo) => { 10 | return foo.name === 'toran'; 11 | }); 12 | } 13 | 14 | moduleFor('service:simple-store', "unit: user model test", { 15 | beforeEach() { 16 | this.register('model:foo', EmberObject.extend({})); 17 | } 18 | }); 19 | 20 | test("returns an instance of filtered record array", function(assert) { 21 | assert.expect(1); 22 | 23 | const simpleStore = this.subject(); 24 | 25 | run(() => { 26 | const recordArray = createFilter(simpleStore); 27 | assert.ok(recordArray instanceof FilteredRecordArray); 28 | }); 29 | }); 30 | 31 | test("manually calling unsubscribe removes from filtersMap", function(assert) { 32 | assert.expect(2); 33 | 34 | const simpleStore = this.subject(); 35 | 36 | run(() => { 37 | const recordArray = createFilter(simpleStore); 38 | const filters = simpleStore.get('filtersMap'); 39 | 40 | assert.equal(filters.foo.length, 1); 41 | simpleStore._unsubscribe(recordArray); 42 | assert.equal(filters.foo.length, 0); 43 | }); 44 | }); 45 | 46 | test("the record array being destroy triggers an unsubscribe", function(assert) { 47 | assert.expect(3); 48 | 49 | const done = assert.async(); 50 | const simpleStore = this.subject(); 51 | 52 | run(() => { 53 | const recordArray = createFilter(simpleStore); 54 | const filters = simpleStore.get('filtersMap'); 55 | 56 | assert.equal(filters.foo.length, 1); 57 | recordArray.destroy(); 58 | assert.equal(recordArray.isDestroying, true); 59 | 60 | run.next(() => { 61 | assert.equal(filters.foo.length, 0); 62 | done(); 63 | }); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /tests/unit/relationship-test.js: -------------------------------------------------------------------------------- 1 | import { run } from '@ember/runloop'; 2 | import { getOwner } from '@ember/application'; 3 | import { test } from "ember-cli-simple-store/test-support/qunit"; 4 | import { moduleFor } from 'ember-qunit'; 5 | import registration from "dummy/tests/helpers/registration"; 6 | 7 | let store, user, role; 8 | 9 | moduleFor('service:simple-store', "unit: user model test", { 10 | beforeEach() { 11 | const owner = getOwner(this); 12 | store = registration(owner, ["model:user", "model:role"]); 13 | } 14 | }); 15 | 16 | test("role property returns associated model or undefined", function(assert) { 17 | user = store.push("user", {id: 1, name: "toran"}); 18 | store.push("role", {id: 9, name: "Guest", users: [1]}); 19 | role = user.get("role"); 20 | assert.equal(role.get("id"), 9); 21 | assert.equal(role.get("name"), "Guest"); 22 | // role.set("users", []); in v3 this worked but v4 requires a push 23 | run(() => { 24 | store.push("role", {id: 9, users: []}); 25 | }); 26 | role = user.get("role"); 27 | assert.equal(role, undefined); 28 | }); 29 | 30 | test("change_role will append user id to the (new) role users array", function(assert) { 31 | user = store.push("user", {id: 1, name: "toran"}); 32 | store.push("role", {id: 8, name: "Admin", users: [9, 8]}); 33 | store.push("role", {id: 9, name: "Guest", users: [1]}); 34 | role = user.get("role"); 35 | assert.equal(role.get("id"), 9); 36 | run(() => { 37 | user.change_role(8); 38 | }); 39 | role = user.get("role"); 40 | assert.equal(role.get("id"), 8); 41 | assert.deepEqual(role.get("users"), [9, 8, 1]); 42 | }); 43 | 44 | test("change_role will remove user id from the (old) role users array", function(assert) { 45 | let guest; 46 | user = store.push("user", {id: 1, name: "toran"}); 47 | guest = store.push("role", {id: 9, name: "Guest", users: [9, 1, 8]}); 48 | store.push("role", {id: 8, name: "Admin", users: []}); 49 | role = user.get("role"); 50 | assert.equal(role.get("id"), 9); 51 | run(() => { 52 | user.change_role(8); 53 | }); 54 | role = user.get("role"); 55 | assert.equal(role.get("id"), 8); 56 | assert.deepEqual(guest.get("users"), [9, 8]); 57 | }); 58 | -------------------------------------------------------------------------------- /tests/unit/service-test.js: -------------------------------------------------------------------------------- 1 | import { inject as service } from '@ember/service'; 2 | import Controller from '@ember/controller'; 3 | import SimpleStore from 'ember-cli-simple-store/store'; 4 | import { moduleFor, test } from 'ember-qunit'; 5 | 6 | moduleFor('service:simple-store', 'Unit | Service | simple-store'); 7 | 8 | test("it exists", function(assert) { 9 | assert.ok(this.subject(), 'a service exists'); 10 | }); 11 | 12 | test("it is an instance of ember-cli-simple-store/store", function(assert) { 13 | assert.ok(this.subject() instanceof SimpleStore, 'a service is an instance of SimpleStore'); 14 | }); 15 | 16 | test("it is injectable", function(assert) { 17 | this.register('controller:injection-subject', Controller.extend({ 18 | simpleStore: service() 19 | })); 20 | 21 | let subject = this.container.lookup('controller:injection-subject'); 22 | assert.ok(subject.get('simpleStore') instanceof SimpleStore); 23 | }); 24 | -------------------------------------------------------------------------------- /tests/unit/store-test.js: -------------------------------------------------------------------------------- 1 | import EmberObject from '@ember/object'; 2 | import { run } from '@ember/runloop'; 3 | import { getOwner } from '@ember/application'; 4 | import { moduleFor } from "ember-qunit"; 5 | import { test } from "ember-cli-simple-store/test-support/qunit"; 6 | 7 | let store, Person, Toran, Cat; 8 | 9 | moduleFor("service:simple-store", "store unit tests", { 10 | beforeEach: function() { 11 | Person = EmberObject.extend({ 12 | firstName: "", 13 | lastName: "", 14 | cat_id: null, 15 | demo: function() { 16 | let firstName = this.get("firstName"); 17 | return firstName + " 777"; 18 | } 19 | }); 20 | Toran = Person.extend({ 21 | fake: function() { 22 | let firstName = this.get("firstName"); 23 | return firstName + " 999"; 24 | } 25 | }); 26 | Cat = EmberObject.extend({ 27 | color: "" 28 | }); 29 | const owner = getOwner(this); 30 | store = this.subject(); 31 | owner.register("model:person", Person); 32 | owner.register("model:toran", Toran); 33 | owner.register("model:cat", Cat); 34 | } 35 | }); 36 | 37 | test("records can be pushed into the store", function(assert) { 38 | store.push("person", { 39 | id: "toranb", 40 | firstName: "Toran", 41 | lastName: "Billups" 42 | }); 43 | 44 | let toranb = store.find("person", "toranb"); 45 | assert.ok(toranb, "The toranb record was found"); 46 | 47 | assert.equal(toranb.get("firstName"), "Toran", "the firstName property is correct"); 48 | assert.equal(toranb.get("lastName"), "Billups", "the lastName property is correct"); 49 | assert.equal(toranb.get("id"), "toranb", "the id property is correct"); 50 | }); 51 | 52 | test("multiple records can be pushed into the store", function(assert) { 53 | let person1 = { 54 | id: "johndoe", 55 | firstName: "john", 56 | lastName: "doe" 57 | }; 58 | 59 | let person2 = { 60 | id: "janedoe", 61 | firstName: "jane", 62 | lastName: "doe" 63 | }; 64 | 65 | store.pushArray("person", [person1, person2]); 66 | 67 | let foundPerson1 = store.find("person", "johndoe"); 68 | let foundPerson2 = store.find("person", "janedoe"); 69 | 70 | assert.equal(foundPerson1.get("id"), person1.id, "person 1 id not found"); 71 | assert.equal(foundPerson2.get("id"), person2.id, "person 2 id not found"); 72 | }); 73 | 74 | test("push returns the created record", function(assert) { 75 | let pushedToranb = store.push("person", { 76 | id: "toranb", 77 | firstName: "Toran", 78 | lastName: "Billups" 79 | }); 80 | 81 | let gottenToranb = store.find("person", "toranb"); 82 | 83 | assert.strictEqual(pushedToranb, gottenToranb.get("content"), "both records are identical"); 84 | }); 85 | 86 | test("push array returns the created records", function(assert) { 87 | let person1 = { 88 | id: "johndoe", 89 | firstName: "john", 90 | lastName: "doe" 91 | }; 92 | 93 | let person2 = { 94 | id: "janedoe", 95 | firstName: "jane", 96 | lastName: "doe" 97 | }; 98 | 99 | let data = [person1, person2]; 100 | 101 | let pushedRecords = store.pushArray("person", data); 102 | 103 | assert.equal(pushedRecords.length, data.length); 104 | assert.equal(pushedRecords[0].get("id"), data[0].id); 105 | assert.equal(pushedRecords[1].get("id"), data[1].id); 106 | }); 107 | 108 | test("willDestroy sets refs to complex objects to null", function(assert) { 109 | store.push("person", { 110 | id: "gambler", 111 | firstName: "Wyatt", 112 | lastName: "Earp" 113 | }); 114 | assert.equal(store.get('array')['person'][0].id, 'gambler', 'gambler in store'); 115 | run(function() { 116 | store.destroy(); 117 | }); 118 | assert.equal(store.get('array'), null, 'store#array set to null'); 119 | assert.equal(store.get('identityMap'), null, 'store#identityMap set to null'); 120 | assert.equal(store.get('filtersMap'), null, 'store#filtersMap set to null'); 121 | assert.equal(store.get('recompute'), null, 'store#recompute set to null'); 122 | }); 123 | 124 | test("pushing a record into the store twice updates the original record", function(assert) { 125 | store.push("person", { 126 | id: "toranb", 127 | firstName: "Toran", 128 | lastName: "Billups" 129 | }); 130 | 131 | let toranb = store.find("person", "toranb"); 132 | assert.ok(toranb, "The toranb record was found"); 133 | 134 | assert.equal(toranb.get("firstName"), "Toran", "the firstName property is correct"); 135 | assert.equal(toranb.get("lastName"), "Billups", "the lastName property is correct"); 136 | assert.equal(toranb.get("id"), "toranb", "the id property is correct"); 137 | 138 | store.push("person", { 139 | id: "toranb", 140 | firstName: "X", 141 | lastName: "Y" 142 | }); 143 | 144 | assert.equal(toranb.get("firstName"), "X", "the firstName property is correct"); 145 | assert.equal(toranb.get("lastName"), "Y", "the lastName property is correct"); 146 | assert.equal(toranb.get("id"), "toranb", "the id property is is correct"); 147 | }); 148 | 149 | test("pushing doesn't mangle string ids", function(assert) { 150 | store.push("person", { 151 | id: "toranb", 152 | firstName: "Toran", 153 | lastName: "Billups" 154 | }); 155 | 156 | let toranb = store.find("person", "toranb"); 157 | assert.strictEqual(toranb.get("id"), "toranb"); 158 | }); 159 | 160 | test("models with int based ids must be lookedup by int and string value", function(assert) { 161 | store.push("person", { 162 | id: 123, 163 | firstName: "Toran", 164 | lastName: "Billups" 165 | }); 166 | 167 | let toranbByNum = store.find("person", 123); 168 | assert.strictEqual(toranbByNum.get("id"), 123); 169 | assert.ok(toranbByNum.get("content") instanceof Person); 170 | 171 | toranbByNum = store.find("person", "123"); 172 | assert.strictEqual(toranbByNum.get("id"), 123); 173 | assert.ok(toranbByNum.get("content") instanceof Person); 174 | }); 175 | 176 | test("models with str based ids must be lookedup by str value", function(assert) { 177 | store.push("person", { 178 | id: "abc", 179 | firstName: "Toran", 180 | lastName: "Billups" 181 | }); 182 | 183 | let toranbByStr = store.find("person", "abc"); 184 | assert.strictEqual(toranbByStr.get("id"), "abc"); 185 | assert.ok(toranbByStr.get("content") instanceof Person); 186 | }); 187 | 188 | test("models with int !0 id in string must be lookedup by int and string value", function(assert) { 189 | store.push("person", { 190 | id: "1234", 191 | firstName: "Guillaume", 192 | lastName: "Gérard" 193 | }); 194 | 195 | let guillaumeByNum = store.find("person", "1234"); 196 | assert.strictEqual(guillaumeByNum.get("id"), 1234); 197 | assert.ok(guillaumeByNum.get("content") instanceof Person); 198 | 199 | guillaumeByNum = store.find("person", 1234); 200 | assert.strictEqual(guillaumeByNum.get("id"), 1234); 201 | assert.ok(guillaumeByNum.get("content") instanceof Person); 202 | }); 203 | 204 | test("pushArray - models with int !0 id in string must be lookedup by int and string value", function(assert) { 205 | store.pushArray("person", [{ 206 | id: "1234", 207 | firstName: "Guillaume", 208 | lastName: "Gérard" 209 | }]); 210 | 211 | let guillaumeByNum = store.find("person", "1234"); 212 | assert.strictEqual(guillaumeByNum.get("id"), 1234); 213 | assert.ok(guillaumeByNum.get("content") instanceof Person); 214 | 215 | guillaumeByNum = store.find("person", 1234); 216 | assert.strictEqual(guillaumeByNum.get("id"), 1234); 217 | assert.ok(guillaumeByNum.get("content") instanceof Person); 218 | }); 219 | 220 | test("models with int 0 id must be lookedup by int and string value", function(assert) { 221 | store.push("person", { 222 | id: 0, 223 | firstName: "Guillaume", 224 | lastName: "Gérard" 225 | }); 226 | 227 | let guillaumeByNum = store.find("person", "0"); 228 | assert.strictEqual(guillaumeByNum.get("id"), 0); 229 | assert.ok(guillaumeByNum.get("content") instanceof Person); 230 | 231 | guillaumeByNum = store.find("person", 0); 232 | assert.strictEqual(guillaumeByNum.get("id"), 0); 233 | assert.ok(guillaumeByNum.get("content") instanceof Person); 234 | }); 235 | 236 | test("models with int 0 id in string must be lookedup by int and string value", function(assert) { 237 | store.push("person", { 238 | id: "0", 239 | firstName: "Guillaume", 240 | lastName: "Gérard" 241 | }); 242 | 243 | let guillaumeByNum = store.find("person", "0"); 244 | assert.strictEqual(guillaumeByNum.get("id"), 0); 245 | assert.ok(guillaumeByNum.get("content") instanceof Person); 246 | 247 | guillaumeByNum = store.find("person", 0); 248 | assert.strictEqual(guillaumeByNum.get("id"), 0); 249 | assert.ok(guillaumeByNum.get("content") instanceof Person); 250 | }); 251 | 252 | test("models with float id in string must be lookedup by int float and string value", function(assert) { 253 | store.push("person", { 254 | id: "3.14159265359", 255 | firstName: "Guillaume", 256 | lastName: "Gérard" 257 | }); 258 | 259 | let guillaumeByNum = store.find("person", "3.14159265359"); 260 | assert.strictEqual(guillaumeByNum.get("id"), 3.14159265359); 261 | assert.ok(guillaumeByNum.get("content") instanceof Person); 262 | 263 | guillaumeByNum = store.find("person", 3.14159265359); 264 | assert.strictEqual(guillaumeByNum.get("id"), 3.14159265359); 265 | assert.ok(guillaumeByNum.get("content") instanceof Person); 266 | }); 267 | 268 | test("find should return array of bound models", function(assert) { 269 | store.push("person", { 270 | id: 1, 271 | firstName: "Toran", 272 | lastName: "Billups" 273 | }); 274 | 275 | store.push("person", { 276 | id: 2, 277 | firstName: "Brandon", 278 | lastName: "Williams" 279 | }); 280 | 281 | let found_data = store.find("person"); 282 | assert.equal(found_data.get("length"), 2); 283 | assert.equal(found_data.objectAt(0).get("firstName"), "Toran"); 284 | assert.equal(found_data.objectAt(1).get("firstName"), "Brandon"); 285 | 286 | store.push("person", { 287 | id: 3, 288 | firstName: "Scott", 289 | lastName: "Newcomer" 290 | }); 291 | 292 | assert.equal(found_data.get("length"), 3); 293 | assert.equal(found_data.objectAt(0).get("firstName"), "Toran"); 294 | assert.equal(found_data.objectAt(1).get("firstName"), "Brandon"); 295 | assert.equal(found_data.objectAt(2).get("firstName"), "Scott"); 296 | }); 297 | 298 | test("remove should destroy the item by type", function(assert) { 299 | assert.expect(7); 300 | 301 | let first = store.push("person", { 302 | id: 1, 303 | firstName: "Toran", 304 | lastName: "Billups" 305 | }); 306 | 307 | let last = store.push("person", { 308 | id: 2, 309 | firstName: "Brandon", 310 | lastName: "Williams" 311 | }); 312 | 313 | assert.equal(store.find("person").get("length"), 2); 314 | store.remove("person", first.get("id")); 315 | assert.equal(store.find("person").get("length"), 1); 316 | 317 | let first_person = store.find("person", first.id); 318 | assert.ok(!first_person.get("content"), "The toran record was still found"); 319 | assert.equal(first_person.get("firstName"), undefined); 320 | 321 | let last_person = store.find("person", last.id); 322 | assert.ok(last_person.get("content"), "The brandon record was not found"); 323 | assert.equal(last_person.get("firstName"), "Brandon"); 324 | 325 | run.next(() => { 326 | try { 327 | first.set("firstName", "X"); 328 | } catch (e) { 329 | assert.ok(e.message.indexOf("Assertion Failed: calling set on destroyed object") !== -1); 330 | } 331 | }); 332 | }); 333 | 334 | test("find with filter should return array of models filtered by value", function(assert) { 335 | store.push("person", { 336 | id: 9, 337 | firstName: "Jarrod", 338 | lastName: "Taylor", 339 | cat_id: 1 340 | }); 341 | 342 | store.push("person", { 343 | id: 8, 344 | firstName: "Brandon", 345 | lastName: "Williams", 346 | cat_id: 2 347 | }); 348 | 349 | store.push("person", { 350 | id: 7, 351 | firstName: "Toran", 352 | lastName: "Billups", 353 | cat_id: 1 354 | }); 355 | 356 | store.push("cat", { 357 | id: 1, 358 | color: "red" 359 | }); 360 | 361 | store.push("cat", { 362 | id: 2, 363 | color: "blue" 364 | }); 365 | 366 | assert.equal(store.find("person").get("length"), 3); 367 | assert.equal(store.find("person").objectAt(0).get("cat_id"), 1); 368 | assert.equal(store.find("person").objectAt(1).get("cat_id"), 2); 369 | assert.equal(store.find("person").objectAt(2).get("cat_id"), 1); 370 | 371 | assert.equal(store.find("person", {cat_id: 1}).get("length"), 2); 372 | assert.equal(store.find("person", {cat_id: 2}).get("length"), 1); 373 | 374 | store.push("person", { 375 | id: 14, 376 | firstName: "wat", 377 | lastName: "hat", 378 | cat_id: 1 379 | }); 380 | assert.equal(store.find("person", {cat_id: 1}).get("length"), 3); 381 | assert.equal(store.find("person", {cat_id: 2}).get("length"), 1); 382 | 383 | store.push("person", { 384 | id: 15, 385 | firstName: "xor", 386 | lastName: "nope", 387 | cat_id: 2 388 | }); 389 | assert.equal(store.find("person", {cat_id: 2}).get("length"), 2); 390 | assert.equal(store.find("person", {cat_id: 1}).get("length"), 3); 391 | 392 | assert.equal(store.find("person").get("length"), 5); 393 | }); 394 | 395 | test("find with filter does not trump type property of object", function(assert) { 396 | store.push("person", { 397 | id: 9, 398 | firstName: "Jarrod", 399 | lastName: "Taylor", 400 | source: "Jarrod's Mom", 401 | store: "Walmart", 402 | type: "developer" 403 | }); 404 | 405 | 406 | let found = store.find("person", 9); 407 | assert.equal(found.get("type"), "developer"); 408 | assert.equal(found.get("store"), "Walmart"); 409 | assert.equal(found.get("source"), "Jarrod's Mom"); 410 | }); 411 | 412 | test("find with filter should return array of models that tracks changes without asking for an update", function(assert) { 413 | store.push("person", { 414 | id: 9, 415 | firstName: "Brandon", 416 | lastName: "Williams", 417 | cat_id: 1 418 | }); 419 | 420 | store.push("person", { 421 | id: 8, 422 | firstName: "Toran", 423 | lastName: "Billups", 424 | cat_id: 1 425 | }); 426 | 427 | store.push("cat", { 428 | id: 1, 429 | color: "red" 430 | }); 431 | 432 | let firstBoundProperty = store.find("person", {cat_id: 1}); 433 | assert.equal(firstBoundProperty.get("length"), 2); 434 | 435 | run(() => { 436 | store.push("person", { 437 | id: 14, 438 | firstName: "Another", 439 | lastName: "Person", 440 | cat_id: 1 441 | }); 442 | }); 443 | 444 | assert.equal(firstBoundProperty.get("length"), 3); 445 | }); 446 | 447 | test("find with filter works with string based values", function(assert) { 448 | store.push("person", { 449 | id: 9, 450 | firstName: "Jarrod", 451 | lastName: "Taylor", 452 | nickname: "foo" 453 | }); 454 | 455 | store.push("person", { 456 | id: 8, 457 | firstName: "Brandon", 458 | lastName: "Williams", 459 | nickname: "bar" 460 | }); 461 | 462 | store.push("person", { 463 | id: 7, 464 | firstName: "Toran", 465 | lastName: "Billups", 466 | nickname: "foo" 467 | }); 468 | 469 | let foo_data = store.find("person", {nickname: "foo"}); 470 | let bar_data = store.find("person", {nickname: "bar"}); 471 | 472 | assert.equal(foo_data.get("length"), 2); 473 | assert.equal(bar_data.get("length"), 1); 474 | assert.equal(foo_data.objectAt(0).get("firstName"), "Jarrod"); 475 | assert.equal(foo_data.objectAt(1).get("firstName"), "Toran"); 476 | assert.equal(bar_data.objectAt(0).get("firstName"), "Brandon"); 477 | }); 478 | 479 | test("clear will destroy everything for a given type", function(assert) { 480 | assert.expect(16); 481 | 482 | let firstPerson = store.push("person", { 483 | id: 9, 484 | firstName: "Brandon", 485 | lastName: "Williams", 486 | cat_id: 1 487 | }); 488 | 489 | let lastPerson = store.push("person", { 490 | id: 8, 491 | firstName: "Toran", 492 | lastName: "Billups", 493 | cat_id: 1 494 | }); 495 | 496 | let firstCat = store.push("cat", { 497 | id: 1, 498 | color: "red" 499 | }); 500 | 501 | let catBefore = store.find("cat", 1); 502 | assert.equal(catBefore.get("color"), "red"); 503 | 504 | let catsBefore = store.find("cat"); 505 | assert.equal(catsBefore.get("length"), 1); 506 | 507 | let firstBoundProperty = store.find("person", {cat_id: 1}); 508 | assert.equal(firstBoundProperty.get("length"), 2); 509 | 510 | let individualFirstBefore = store.find("person", 9); 511 | assert.equal(individualFirstBefore.get("firstName"), "Brandon"); 512 | 513 | let individualLastBefore = store.find("person", 8); 514 | assert.equal(individualLastBefore.get("firstName"), "Toran"); 515 | 516 | run(() => { 517 | store.clear("person"); 518 | }); 519 | 520 | assert.equal(firstBoundProperty.get("length"), 0); 521 | 522 | let all = store.find("person"); 523 | assert.equal(all.get("length"), 0); 524 | 525 | let individualFirstAfter = store.find("person", 9); 526 | assert.equal(individualFirstAfter.get("content"), null); 527 | assert.equal(individualFirstAfter.get("firstName"), undefined); 528 | 529 | let individualLastAfter = store.find("person", 8); 530 | assert.equal(individualLastAfter.get("content"), null); 531 | assert.equal(individualLastAfter.get("firstName"), undefined); 532 | 533 | let catAfter = store.find("cat", 1); 534 | assert.equal(catAfter.get("color"), "red"); 535 | 536 | let catsAfter = store.find("cat"); 537 | assert.equal(catsAfter.get("length"), 1); 538 | 539 | run.next(() => { 540 | try { 541 | firstPerson.set("firstName", "X"); 542 | } catch (e) { 543 | assert.ok(e.message.indexOf("Assertion Failed: calling set on destroyed object") !== -1); 544 | } 545 | 546 | try { 547 | lastPerson.set("firstName", "X"); 548 | } catch (e) { 549 | assert.ok(e.message.indexOf("Assertion Failed: calling set on destroyed object") !== -1); 550 | } 551 | 552 | firstCat.set("color", "purple"); 553 | assert.equal(firstCat.get("color"), "purple"); 554 | }); 555 | }); 556 | 557 | test("clear without type will destroy everything", function(assert) { 558 | assert.expect(10); 559 | 560 | let firstPerson = store.push("person", { 561 | id: 9, 562 | firstName: "Brandon", 563 | lastName: "Williams", 564 | cat_id: 1 565 | }); 566 | 567 | let lastPerson = store.push("person", { 568 | id: 8, 569 | firstName: "Toran", 570 | lastName: "Billups", 571 | cat_id: 1 572 | }); 573 | 574 | let firstCat = store.push("cat", { 575 | id: 1, 576 | color: "red" 577 | }); 578 | 579 | firstPerson.set("firstName", "R"); 580 | lastPerson.set("firstName", "M"); 581 | firstCat.set("color", "purple"); 582 | 583 | assert.equal(firstPerson.get("firstName"), "R"); 584 | assert.equal(lastPerson.get("firstName"), "M"); 585 | assert.equal(firstCat.get("color"), "purple"); 586 | 587 | assert.equal(store.find("person").get("length"), 2); 588 | assert.equal(store.find("cat").get("length"), 1); 589 | 590 | run(() => { 591 | store.clear(); 592 | }); 593 | 594 | assert.equal(store.find("person").get("length"), 0); 595 | assert.equal(store.find("cat").get("length"), 0); 596 | 597 | run.next(() => { 598 | try { 599 | firstPerson.set("firstName", "X"); 600 | } catch (e) { 601 | assert.ok(e.message.indexOf("Assertion Failed: calling set on destroyed object") !== -1); 602 | } 603 | 604 | try { 605 | lastPerson.set("firstName", "X"); 606 | } catch (e) { 607 | assert.ok(e.message.indexOf("Assertion Failed: calling set on destroyed object") !== -1); 608 | } 609 | 610 | try { 611 | firstCat.set("color", "rain"); 612 | } catch (e) { 613 | assert.ok(e.message.indexOf("Assertion Failed: calling set on destroyed object") !== -1); 614 | } 615 | }); 616 | }); 617 | 618 | test("invoking clear multiple times will only schedule a recompute once per type", function(assert) { 619 | run(() => { 620 | store.clear("foo"); 621 | store.clear("foo"); 622 | store.clear("bar"); 623 | store.clear("foo"); 624 | assert.equal(store.get("recompute.length"), 2); 625 | }); 626 | }); 627 | 628 | test("find with filter should raise clear exception when invalid options are passed", function(assert) { 629 | try { 630 | store.find("person", {}); 631 | assert.ok(false, "filter did not fail with clear exception message"); 632 | } catch(e) { 633 | assert.equal(e.message, "Assertion Failed: No key was found in the filter options"); 634 | } 635 | }); 636 | 637 | test("pushing a model that does not exist should raise clear exception", function(assert) { 638 | try { 639 | store.push("goat", {id: 4, name: "billy"}); 640 | assert.ok(false, "model lookup did not fail with clear exception message"); 641 | } catch(e) { 642 | assert.equal(e.message, "Assertion Failed: No model was found for type: goat"); 643 | } 644 | }); 645 | 646 | test("findOne will return the first record", function(assert) { 647 | store.push("toran", { 648 | id: 1, 649 | firstName: "Jake", 650 | lastName: "Good" 651 | }); 652 | 653 | store.push("toran", { 654 | id: 2, 655 | firstName: "Brandon", 656 | lastName: "Williams" 657 | }); 658 | 659 | assert.equal(store.find("toran").get("length"), 2); 660 | 661 | let toranb = store.findOne("toran"); 662 | assert.equal(toranb.get("firstName"), "Jake", "the firstName property is correct"); 663 | assert.equal(toranb.get("lastName"), "Good", "the lastName property is correct"); 664 | assert.equal(toranb.get("id"), "1", "the id property is correct"); 665 | assert.equal(toranb.get("content").fake(), "Jake 999"); 666 | assert.equal(toranb.get("content").demo(), "Jake 777"); 667 | assert.equal(toranb.fake(), "Jake 999"); 668 | assert.equal(toranb.demo(), "Jake 777"); 669 | }); 670 | 671 | test("findOne should return null when no objects exist in the cache for given type", function(assert) { 672 | assert.equal(store.find("person").get("length"), 0); 673 | let person = store.findOne("person"); 674 | assert.equal(person.get("content"), null); 675 | }); 676 | 677 | test("find with filter function will return bound array", function(assert) { 678 | store.push("person", { 679 | id: 9, 680 | firstName: "Jarrod", 681 | lastName: "Taylor", 682 | nickname: "foo", 683 | group: 2 684 | }); 685 | 686 | store.push("person", { 687 | id: 8, 688 | firstName: "Brandon", 689 | lastName: "Williams", 690 | nickname: "bar", 691 | group: 3 692 | }); 693 | 694 | store.push("person", { 695 | id: 7, 696 | firstName: "Toran", 697 | lastName: "Billups", 698 | nickname: "foo", 699 | group: 8 700 | }); 701 | 702 | let filter = function(person) { 703 | return person.get("group") > 2 || person.get("nickname") === "bar"; 704 | }; 705 | 706 | let filtered_data = store.find("person", filter); 707 | 708 | assert.equal(filtered_data.get("length"), 2); 709 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Brandon"); 710 | assert.equal(filtered_data.objectAt(1).get("firstName"), "Toran"); 711 | 712 | run(() => { 713 | store.push("person", { 714 | id: 6, 715 | firstName: "Taylor", 716 | lastName: "Hobbs", 717 | nickname: "zzz", 718 | group: 8 719 | }); 720 | }); 721 | 722 | assert.equal(filtered_data.get("length"), 3); 723 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Brandon"); 724 | assert.equal(filtered_data.objectAt(1).get("firstName"), "Toran"); 725 | assert.equal(filtered_data.objectAt(2).get("firstName"), "Taylor"); 726 | 727 | run(() => { 728 | store.push("person", { 729 | id: 5, 730 | firstName: "Matt", 731 | lastName: "Morrison", 732 | nickname: "bar", 733 | group: 0 734 | }); 735 | }); 736 | 737 | assert.equal(filtered_data.get("length"), 4); 738 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Brandon"); 739 | assert.equal(filtered_data.objectAt(1).get("firstName"), "Toran"); 740 | assert.equal(filtered_data.objectAt(2).get("firstName"), "Taylor"); 741 | assert.equal(filtered_data.objectAt(3).get("firstName"), "Matt"); 742 | 743 | let taylor = store.find("person", 6); 744 | assert.equal(taylor.get("firstName"), "Taylor"); 745 | // taylor.set("group", 1); in v3 this worked but v4 requires a push 746 | run(() => { 747 | store.push("person", {id: taylor.get("id"), group: 1}); 748 | }); 749 | 750 | assert.equal(filtered_data.get("length"), 3); 751 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Brandon"); 752 | assert.equal(filtered_data.objectAt(1).get("firstName"), "Toran"); 753 | assert.equal(filtered_data.objectAt(2).get("firstName"), "Matt"); 754 | 755 | let brandon = store.find("person", 8); 756 | assert.equal(brandon.get("firstName"), "Brandon"); 757 | // brandon.set("group", 1); in v3 this worked but v4 requires a push 758 | run(() => { 759 | store.push("person", {id: brandon.get("id"), group: 1}); 760 | }); 761 | 762 | assert.equal(filtered_data.get("length"), 3); 763 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Brandon"); 764 | assert.equal(filtered_data.objectAt(1).get("firstName"), "Toran"); 765 | assert.equal(filtered_data.objectAt(2).get("firstName"), "Matt"); 766 | 767 | // brandon.set("nickname", "x"); in v3 this worked but v4 requires a push 768 | run(() => { 769 | store.push("person", {id: brandon.get("id"), nickname: "x"}); 770 | }); 771 | 772 | assert.equal(filtered_data.get("length"), 2); 773 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Toran"); 774 | assert.equal(filtered_data.objectAt(1).get("firstName"), "Matt"); 775 | }); 776 | 777 | test("findByIdComputed result will be computed property that updates as records are pushed into the store", function(assert) { 778 | let done = assert.async(); 779 | let toranb = store.find("person", 123); 780 | assert.equal(toranb.get("id"), undefined); 781 | assert.equal(toranb.get("firstName"), undefined); 782 | assert.equal(toranb.get("lastName"), undefined); 783 | 784 | setTimeout(function() { 785 | run(() => { 786 | store.push("person", { 787 | id: 123, 788 | firstName: "Toran", 789 | lastName: "Billups" 790 | }); 791 | }); 792 | setTimeout(function() { 793 | assert.equal(toranb.get("id"), 123); 794 | assert.equal(toranb.get("firstName"), "Toran"); 795 | assert.equal(toranb.get("lastName"), "Billups"); 796 | done(); 797 | }, 0); 798 | }, 0); 799 | }); 800 | 801 | test("findByIdComputed also works with string based ids", function(assert) { 802 | let done = assert.async(); 803 | let toranb = store.find("person", "abc123"); 804 | assert.equal(toranb.get("id"), undefined); 805 | assert.equal(toranb.get("firstName"), undefined); 806 | assert.equal(toranb.get("lastName"), undefined); 807 | 808 | setTimeout(function() { 809 | run(() => { 810 | store.push("person", { 811 | id: "abc123", 812 | firstName: "Toran", 813 | lastName: "Billups" 814 | }); 815 | }); 816 | setTimeout(function() { 817 | assert.equal(toranb.get("id"), "abc123"); 818 | assert.equal(toranb.get("firstName"), "Toran"); 819 | assert.equal(toranb.get("lastName"), "Billups"); 820 | done(); 821 | }, 0); 822 | }, 0); 823 | }); 824 | 825 | test("findByIdComputed truly works with guid based ids", function(assert) { 826 | let done = assert.async(); 827 | let toranb = store.find("person", "55555555-ca0d-4126-8929-afdsaf789883"); 828 | assert.equal(toranb.get("id"), undefined); 829 | assert.equal(toranb.get("firstName"), undefined); 830 | assert.equal(toranb.get("lastName"), undefined); 831 | 832 | setTimeout(function() { 833 | run(() => { 834 | store.push("person", { 835 | id: "55555555-ca0d-4126-8929-afdsaf789883", 836 | firstName: "Toran", 837 | lastName: "Billups" 838 | }); 839 | }); 840 | setTimeout(function() { 841 | assert.equal(toranb.get("id"), "55555555-ca0d-4126-8929-afdsaf789883"); 842 | assert.equal(toranb.get("firstName"), "Toran"); 843 | assert.equal(toranb.get("lastName"), "Billups"); 844 | done(); 845 | }, 0); 846 | }, 0); 847 | }); 848 | 849 | test("findByIdComputed will return result with int based id using string", function(assert) { 850 | let done = assert.async(); 851 | let toranb = store.find("person", "4"); 852 | assert.equal(toranb.get("id"), undefined); 853 | assert.equal(toranb.get("firstName"), undefined); 854 | assert.equal(toranb.get("lastName"), undefined); 855 | 856 | setTimeout(function() { 857 | run(() => { 858 | store.push("person", { 859 | id: 4, 860 | firstName: "Toran", 861 | lastName: "Billups" 862 | }); 863 | }); 864 | setTimeout(function() { 865 | assert.equal(toranb.get("id"), 4); 866 | assert.equal(toranb.get("firstName"), "Toran"); 867 | assert.equal(toranb.get("lastName"), "Billups"); 868 | done(); 869 | }, 0); 870 | }, 0); 871 | }); 872 | 873 | test("findById will proxy each method for the given type when updated", function(assert) { 874 | let done = assert.async(); 875 | let toranb = store.find("toran", 6); 876 | assert.equal(toranb.get("id"), undefined); 877 | 878 | setTimeout(function() { 879 | run(() => { 880 | store.push("toran", { 881 | id: 6, 882 | firstName: "Toran", 883 | lastName: "Billups" 884 | }); 885 | }); 886 | setTimeout(function() { 887 | assert.equal(toranb.get("id"), 6); 888 | assert.equal(toranb.get("firstName"), "Toran"); 889 | assert.equal(toranb.get("content").fake(), "Toran 999"); 890 | assert.equal(toranb.get("content").demo(), "Toran 777"); 891 | assert.equal(toranb.fake(), "Toran 999"); 892 | assert.equal(toranb.demo(), "Toran 777"); 893 | 894 | done(); 895 | }, 0); 896 | }, 0); 897 | }); 898 | 899 | test("findById will proxy each method for the given type when already in the store", function(assert) { 900 | store.push("toran", { 901 | id: 5, 902 | firstName: "Toran", 903 | lastName: "Billups" 904 | }); 905 | store.push("toran", { 906 | id: 3, 907 | firstName: "Other", 908 | lastName: "Person" 909 | }); 910 | 911 | let toranb = store.find("toran", 5); 912 | assert.ok(toranb, "The toranb record was found"); 913 | 914 | assert.equal(toranb.get("firstName"), "Toran"); 915 | assert.equal(toranb.get("content").fake(), "Toran 999"); 916 | assert.equal(toranb.get("content").demo(), "Toran 777"); 917 | assert.equal(toranb.fake(), "Toran 999"); 918 | assert.equal(toranb.demo(), "Toran 777"); 919 | 920 | let other = store.find("toran", 3); 921 | assert.ok(other, "The other record was found"); 922 | 923 | assert.equal(other.get("firstName"), "Other"); 924 | assert.equal(other.get("content").fake(), "Other 999"); 925 | assert.equal(other.get("content").demo(), "Other 777"); 926 | assert.equal(other.fake(), "Other 999"); 927 | assert.equal(other.demo(), "Other 777"); 928 | 929 | toranb = store.find("toran", 5); 930 | assert.ok(toranb, "The toranb record was found"); 931 | 932 | assert.equal(toranb.get("firstName"), "Toran"); 933 | assert.equal(toranb.get("content").fake(), "Toran 999"); 934 | assert.equal(toranb.get("content").demo(), "Toran 777"); 935 | assert.equal(toranb.fake(), "Toran 999"); 936 | assert.equal(toranb.demo(), "Toran 777"); 937 | }); 938 | 939 | test("findOne result will be computed property that updates as records are pushed into the store", function(assert) { 940 | let done = assert.async(); 941 | let toran = store.findOne("toran"); 942 | assert.equal(toran.get("id"), undefined); 943 | assert.equal(toran.get("firstName"), undefined); 944 | assert.equal(toran.get("lastName"), undefined); 945 | setTimeout(function() { 946 | run(() => { 947 | store.push("toran", { 948 | id: 123, 949 | firstName: "Toran", 950 | lastName: "Billups" 951 | }); 952 | }); 953 | 954 | setTimeout(function() { 955 | assert.equal(toran.get("id"), 123); 956 | assert.equal(toran.get("firstName"), "Toran"); 957 | assert.equal(toran.get("lastName"), "Billups"); 958 | assert.equal(toran.get("content").fake(), "Toran 999"); 959 | assert.equal(toran.get("content").demo(), "Toran 777"); 960 | assert.equal(toran.fake(), "Toran 999"); 961 | assert.equal(toran.demo(), "Toran 777"); 962 | 963 | done(); 964 | }, 0); 965 | }, 0); 966 | }); 967 | 968 | test("store will not update object with id of undefined", function(assert) { 969 | store.push("person", {}); 970 | assert.equal(store.find("person").get("length"), 1); 971 | store.push("person", {id: 1, name: "foo"}); 972 | assert.equal(store.find("person").get("length"), 2); 973 | let people = store.find("person"); 974 | assert.equal(people.objectAt(0).id, undefined); 975 | assert.equal(people.objectAt(1).id, 1); 976 | }); 977 | 978 | test("store will update object with id of undefined when setting properties", function(assert) { 979 | let person = store.push("person", {}); 980 | assert.equal(store.find("person").get("length"), 1); 981 | person.setProperties({id: 1, name: "foo"}); 982 | assert.equal(store.find("person").get("length"), 1); 983 | let people = store.find("person"); 984 | assert.equal(people.objectAt(0).id, 1); 985 | }); 986 | 987 | test("find with filter returns array proxy with push function that adds record", function(assert) { 988 | store.push("person", { 989 | id: 9, 990 | firstName: "Jarrod", 991 | lastName: "Taylor", 992 | nickname: "foo", 993 | group: 2 994 | }); 995 | 996 | store.push("person", { 997 | id: 8, 998 | firstName: "Toran", 999 | lastName: "Billups", 1000 | nickname: "wat", 1001 | group: 3 1002 | }); 1003 | 1004 | let filtered_data = store.find("person", {group: 2}); 1005 | 1006 | assert.equal(filtered_data.get("length"), 1); 1007 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Jarrod"); 1008 | 1009 | let created; 1010 | run(() => { 1011 | created = filtered_data.push({ 1012 | id: 6, 1013 | firstName: "Taylor", 1014 | lastName: "Hobbs", 1015 | nickname: "zzz", 1016 | group: 2 1017 | }); 1018 | }); 1019 | 1020 | assert.equal(created.get("id"), 6); 1021 | assert.equal(created.get("firstName"), "Taylor"); 1022 | 1023 | assert.equal(filtered_data.get("length"), 2); 1024 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Jarrod"); 1025 | assert.equal(filtered_data.objectAt(1).get("firstName"), "Taylor"); 1026 | 1027 | assert.equal(store.find("person", 6).get("firstName"), "Taylor"); 1028 | assert.equal(store.find("person", 9).get("firstName"), "Jarrod"); 1029 | }); 1030 | 1031 | test("find with filter function returns array proxy with push function that adds record", function(assert) { 1032 | store.push("person", { 1033 | id: 9, 1034 | firstName: "Jarrod", 1035 | lastName: "Taylor", 1036 | nickname: "foo", 1037 | group: 2 1038 | }); 1039 | 1040 | store.push("person", { 1041 | id: 8, 1042 | firstName: "Brandon", 1043 | lastName: "Williams", 1044 | nickname: "bar", 1045 | group: 3 1046 | }); 1047 | 1048 | store.push("person", { 1049 | id: 7, 1050 | firstName: "Toran", 1051 | lastName: "Billups", 1052 | nickname: "foo", 1053 | group: 8 1054 | }); 1055 | 1056 | let filter = function(person) { 1057 | return person.get("group") > 2 || person.get("nickname") === "bar"; 1058 | }; 1059 | 1060 | let filtered_data = store.find("person", filter); 1061 | 1062 | assert.equal(filtered_data.get("length"), 2); 1063 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Brandon"); 1064 | assert.equal(filtered_data.objectAt(1).get("firstName"), "Toran"); 1065 | 1066 | run(() => { 1067 | store.push("person", { 1068 | id: 6, 1069 | firstName: "Taylor", 1070 | lastName: "Hobbs", 1071 | nickname: "zzz", 1072 | group: 8 1073 | }); 1074 | }); 1075 | 1076 | assert.equal(filtered_data.get("length"), 3); 1077 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Brandon"); 1078 | assert.equal(filtered_data.objectAt(1).get("firstName"), "Toran"); 1079 | assert.equal(filtered_data.objectAt(2).get("firstName"), "Taylor"); 1080 | 1081 | let created; 1082 | run(() => { 1083 | created = filtered_data.push({ 1084 | id: 5, 1085 | firstName: "Scott", 1086 | lastName: "Newcomer", 1087 | nickname: "bar", 1088 | group: 1 1089 | }); 1090 | }); 1091 | 1092 | assert.equal(filtered_data.get("length"), 4); 1093 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Brandon"); 1094 | assert.equal(filtered_data.objectAt(1).get("firstName"), "Toran"); 1095 | assert.equal(filtered_data.objectAt(2).get("firstName"), "Taylor"); 1096 | assert.equal(filtered_data.objectAt(3).get("firstName"), "Scott"); 1097 | 1098 | assert.equal(created.get("id"), 5); 1099 | assert.equal(created.get("firstName"), "Scott"); 1100 | 1101 | assert.equal(store.find("person", 8).get("firstName"), "Brandon"); 1102 | assert.equal(store.find("person", 7).get("firstName"), "Toran"); 1103 | assert.equal(store.find("person", 6).get("firstName"), "Taylor"); 1104 | assert.equal(store.find("person", 5).get("firstName"), "Scott"); 1105 | }); 1106 | 1107 | test("find returns array proxy with push function that adds record", function(assert) { 1108 | store.push("person", { 1109 | id: 1, 1110 | firstName: "Toran", 1111 | lastName: "Billups" 1112 | }); 1113 | 1114 | store.push("person", { 1115 | id: 2, 1116 | firstName: "Brandon", 1117 | lastName: "Williams" 1118 | }); 1119 | 1120 | let found_data = store.find("person"); 1121 | assert.equal(found_data.get("length"), 2); 1122 | assert.equal(found_data.objectAt(0).get("firstName"), "Toran"); 1123 | assert.equal(found_data.objectAt(1).get("firstName"), "Brandon"); 1124 | 1125 | let created; 1126 | run(() => { 1127 | created = found_data.push({ 1128 | id: 3, 1129 | firstName: "Scott", 1130 | lastName: "Newcomer" 1131 | }); 1132 | }); 1133 | 1134 | assert.equal(found_data.get("length"), 3); 1135 | assert.equal(found_data.objectAt(0).get("firstName"), "Toran"); 1136 | assert.equal(found_data.objectAt(1).get("firstName"), "Brandon"); 1137 | assert.equal(found_data.objectAt(2).get("firstName"), "Scott"); 1138 | 1139 | assert.equal(created.get("id"), 3); 1140 | assert.equal(created.get("firstName"), "Scott"); 1141 | 1142 | assert.equal(store.find("person", 1).get("firstName"), "Toran"); 1143 | assert.equal(store.find("person", 2).get("firstName"), "Brandon"); 1144 | assert.equal(store.find("person", 3).get("firstName"), "Scott"); 1145 | }); 1146 | 1147 | test("bound array proxy from find will correctly respect the push of different types", function(assert) { 1148 | store.push("person", { 1149 | id: 1, 1150 | firstName: "Toran", 1151 | lastName: "Billups" 1152 | }); 1153 | 1154 | store.push("person", { 1155 | id: 2, 1156 | firstName: "Brandon", 1157 | lastName: "Williams" 1158 | }); 1159 | 1160 | store.push("cat", { 1161 | id: 1, 1162 | color: "red" 1163 | }); 1164 | 1165 | store.push("cat", { 1166 | id: 2, 1167 | color: "green" 1168 | }); 1169 | 1170 | let found_data = store.find("person"); 1171 | assert.equal(found_data.get("length"), 2); 1172 | assert.equal(found_data.objectAt(0).get("firstName"), "Toran"); 1173 | assert.equal(found_data.objectAt(1).get("firstName"), "Brandon"); 1174 | 1175 | run(() => { 1176 | found_data.push({ 1177 | id: 3, 1178 | firstName: "Scott", 1179 | lastName: "Newcomer" 1180 | }); 1181 | }); 1182 | 1183 | assert.equal(found_data.get("length"), 3); 1184 | assert.equal(found_data.objectAt(0).get("firstName"), "Toran"); 1185 | assert.equal(found_data.objectAt(1).get("firstName"), "Brandon"); 1186 | assert.equal(found_data.objectAt(2).get("firstName"), "Scott"); 1187 | 1188 | assert.equal(store.find("person", 1).get("firstName"), "Toran"); 1189 | assert.equal(store.find("person", 2).get("firstName"), "Brandon"); 1190 | assert.equal(store.find("person", 3).get("firstName"), "Scott"); 1191 | 1192 | let found_cats = store.find("cat"); 1193 | assert.equal(found_cats.get("length"), 2); 1194 | assert.equal(found_cats.objectAt(0).get("color"), "red"); 1195 | assert.equal(found_cats.objectAt(1).get("color"), "green"); 1196 | 1197 | run(() => { 1198 | found_cats.push({ 1199 | id: 3, 1200 | color: "yellow" 1201 | }); 1202 | }); 1203 | 1204 | assert.equal(found_cats.get("length"), 3); 1205 | assert.equal(found_cats.objectAt(0).get("color"), "red"); 1206 | assert.equal(found_cats.objectAt(1).get("color"), "green"); 1207 | assert.equal(found_cats.objectAt(2).get("color"), "yellow"); 1208 | 1209 | assert.equal(store.find("cat", 1).get("color"), "red"); 1210 | assert.equal(store.find("cat", 2).get("color"), "green"); 1211 | assert.equal(store.find("cat", 3).get("color"), "yellow"); 1212 | 1213 | run(() => { 1214 | found_data.push({ 1215 | id: 4, 1216 | firstName: "Matt", 1217 | lastName: "Morrison" 1218 | }); 1219 | }); 1220 | 1221 | assert.equal(found_cats.get("length"), 3); 1222 | assert.equal(found_data.get("length"), 4); 1223 | }); 1224 | 1225 | test("find returns array proxy that has a remove function that removes the record", function(assert) { 1226 | store.push("person", { 1227 | id: 1, 1228 | firstName: "Toran", 1229 | lastName: "Billups" 1230 | }); 1231 | 1232 | store.push("person", { 1233 | id: 2, 1234 | firstName: "Brandon", 1235 | lastName: "Williams" 1236 | }); 1237 | 1238 | let found_data = store.find("person"); 1239 | assert.equal(found_data.get("length"), 2); 1240 | assert.equal(found_data.objectAt(0).get("firstName"), "Toran"); 1241 | assert.equal(found_data.objectAt(1).get("firstName"), "Brandon"); 1242 | 1243 | run(() => { 1244 | found_data.remove(1); 1245 | }); 1246 | 1247 | assert.equal(found_data.get("length"), 1); 1248 | assert.equal(found_data.objectAt(0).get("firstName"), "Brandon"); 1249 | assert.equal(store.find("person", 2).get("firstName"), "Brandon"); 1250 | 1251 | run(() => { 1252 | found_data.push({ 1253 | id: 1, 1254 | lastName: "new" 1255 | }); 1256 | }); 1257 | 1258 | assert.equal(store._findById("person", 1).get("firstName"), ""); 1259 | assert.equal(store._findById("person", 1).get("lastName"), "new"); 1260 | }); 1261 | 1262 | test("find with filter returns array proxy that has a remove function that removes the record", function(assert) { 1263 | store.push("person", { 1264 | id: 9, 1265 | firstName: "Jarrod", 1266 | lastName: "Taylor", 1267 | nickname: "foo", 1268 | group: 2 1269 | }); 1270 | 1271 | store.push("person", { 1272 | id: 8, 1273 | firstName: "Toran", 1274 | lastName: "Billups", 1275 | nickname: "wat", 1276 | group: 3 1277 | }); 1278 | 1279 | store.push("person", { 1280 | id: 7, 1281 | firstName: "Scott", 1282 | lastName: "Newcomer", 1283 | nickname: "barz", 1284 | group: 2 1285 | }); 1286 | 1287 | let filtered_data = store.find("person", {group: 2}); 1288 | 1289 | assert.equal(filtered_data.get("length"), 2); 1290 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Jarrod"); 1291 | 1292 | run(() => { 1293 | filtered_data.remove(9); 1294 | }); 1295 | 1296 | assert.equal(filtered_data.get("length"), 1); 1297 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Scott"); 1298 | assert.equal(store.find("person", 7).get("firstName"), "Scott"); 1299 | assert.equal(store.find("person", 8).get("firstName"), "Toran"); 1300 | 1301 | run(() => { 1302 | filtered_data.push({ 1303 | id: 9, 1304 | lastName: "new" 1305 | }); 1306 | }); 1307 | 1308 | assert.equal(store._findById("person", 9).get("firstName"), ""); 1309 | assert.equal(store._findById("person", 9).get("lastName"), "new"); 1310 | }); 1311 | 1312 | test("find with filter function returns array proxy that has a remove function that removes the record", function(assert) { 1313 | store.push("person", { 1314 | id: 9, 1315 | firstName: "Jarrod", 1316 | lastName: "Taylor", 1317 | nickname: "foo", 1318 | group: 2 1319 | }); 1320 | 1321 | store.push("person", { 1322 | id: 8, 1323 | firstName: "Brandon", 1324 | lastName: "Williams", 1325 | nickname: "bar", 1326 | group: 3 1327 | }); 1328 | 1329 | store.push("person", { 1330 | id: 7, 1331 | firstName: "Toran", 1332 | lastName: "Billups", 1333 | nickname: "foo", 1334 | group: 8 1335 | }); 1336 | 1337 | let filter = function(person) { 1338 | return person.get("group") > 2 || person.get("nickname") === "bar"; 1339 | }; 1340 | 1341 | let filtered_data = store.find("person", filter); 1342 | 1343 | assert.equal(filtered_data.get("length"), 2); 1344 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Brandon"); 1345 | assert.equal(filtered_data.objectAt(1).get("firstName"), "Toran"); 1346 | 1347 | run(() => { 1348 | filtered_data.remove(8); 1349 | }); 1350 | 1351 | assert.equal(filtered_data.get("length"), 1); 1352 | assert.equal(filtered_data.objectAt(0).get("firstName"), "Toran"); 1353 | 1354 | run(() => { 1355 | filtered_data.push({ 1356 | id: 8, 1357 | lastName: "new" 1358 | }); 1359 | }); 1360 | 1361 | assert.equal(store._findById("person", 8).get("firstName"), ""); 1362 | assert.equal(store._findById("person", 8).get("lastName"), "new"); 1363 | }); 1364 | 1365 | let Listing; 1366 | 1367 | moduleFor("service:simple-store", "store unit tests -- custom primary key", { 1368 | beforeEach: function() { 1369 | Listing = EmberObject.extend({ 1370 | listing_id: null, 1371 | description: null 1372 | }); 1373 | 1374 | Listing.reopenClass({ 1375 | primaryKey: "listing_id" 1376 | }); 1377 | const owner = getOwner(this); 1378 | store = this.subject(); 1379 | owner.register("model:listing", Listing); 1380 | } 1381 | }); 1382 | 1383 | test("store#push and store#find use the specified primary key", function(assert) { 1384 | store.push("listing", { 1385 | listing_id: 5, 1386 | description: "A lovely vase" 1387 | }); 1388 | 1389 | let listing = store.find("listing", 5); 1390 | assert.ok(listing, "The listing record was found"); 1391 | 1392 | assert.equal(listing.get("description"), "A lovely vase", "the description property is correct"); 1393 | }); 1394 | 1395 | test("remove should destory the item by type", function(assert) { 1396 | let first = store.push("listing", { 1397 | listing_id: 1, 1398 | description: "Ugly vase" 1399 | }); 1400 | 1401 | let last = store.push("listing", { 1402 | listing_id: 2, 1403 | description: "A 'Sorcier des Glaces' CD" 1404 | }); 1405 | 1406 | assert.equal(store.find("listing").get("length"), 2); 1407 | store.remove("listing", first.listing_id); 1408 | assert.equal(store.find("listing").get("length"), 1); 1409 | 1410 | let first_listing = store.find("listing", first.listing_id); 1411 | assert.ok(!first_listing.get("content"), "The ugly vase record was not found"); 1412 | assert.equal(first_listing.get("description"), undefined); 1413 | 1414 | let last_listing = store.find("listing", last.listing_id); 1415 | assert.ok(last_listing.get("content"), "The 'Sorcier des Glaces' record was still found"); 1416 | assert.equal(last_listing.get("description"), "A 'Sorcier des Glaces' CD"); 1417 | }); 1418 | -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toranb/ember-cli-simple-store/ac6955430e26a7cd833aa3764228f4e3f8f96748/vendor/.gitkeep --------------------------------------------------------------------------------