From 1ff3de840db229ceaaa65836a5fab6aa630c63e7 Mon Sep 17 00:00:00 2001 From: Florian Pichler Date: Wed, 28 Aug 2019 15:32:14 +0200 Subject: [PATCH] Run Prettier on Markdown content (#694) * Update markdown for events and talks * Update markdown in README * Add scripts for running Prettier on Markdown files * Format Markdown in calendar entries * Blog posts * Add run commands * Add test for Prettier Markdown --- .travis.yml | 3 + README.md | 30 +-- _calendar/2019-05-25-emberginners.md | 6 +- _calendar/2019-06-01-jsconfeu.md | 6 +- _calendar/2019-06-22-commitporto.md | 4 +- _calendar/2019-07-10-fullstacklondon.md | 6 +- _calendar/2019-09-19-hiveconf.md | 4 +- _calendar/2019-10-17-emberfest.md | 6 +- _calendar/2019-10-30-reactiveconf.md | 6 +- .../2013-06-15-authentication-in-emberjs.md | 44 ++-- _posts/2013-06-27-excellent-172.md | 16 +- _posts/2013-06-28-excellent-200.md | 18 +- ...-08-08-better-authentication-in-emberjs.md | 24 +- _posts/2013-10-09-embersimpleauth.md | 14 +- ...simpleauth-implements-rfc-6749-oauth-20.md | 12 +- _posts/2014-01-20-embersimpleauth-010.md | 10 +- _posts/2014-03-11-embersimpleauth-020.md | 8 +- _posts/2014-04-06-embersimpleauth-021.md | 10 +- _posts/2014-04-10-embersimpleauth-030.md | 18 +- ...2014-04-24-embersimpleauth-needs-a-logo.md | 10 +- ...-using-ember-simple-auth-with-ember-cli.md | 10 +- ...ng-with-ember-simple-auth-and-ember-cli.md | 14 +- ...rtant-command-when-working-with-xcode-6.md | 8 +- .../2015-06-03-emberjs-workshop-in-munich.md | 38 ++-- ...07-29-ember-simple-auth-10-a-first-look.md | 28 +-- _posts/2015-08-07-rails-api-auth.md | 11 +- ...11-27-updating-to-ember-simple-auth-1.0.md | 24 +- ...015-12-3-ember-cli-deploy-notifications.md | 52 ++--- _posts/2016-03-04-ember-test-selectors.md | 18 +- ...x-fastboot-support-in-ember-simple-auth.md | 19 +- _posts/2017-01-13-ember-test-selectors.md | 19 +- ...7-02-01-class-based-computed-properties.md | 27 +-- _posts/2017-02-13-npm-libs-in-ember-cli.md | 28 +-- ...03-21-on-computed-properties-vs-helpers.md | 106 ++++----- ...28-creating-web-components-with-glimmer.md | 55 ++--- _posts/2017-09-17-magic-test-data.md | 45 ++-- ...24-high-level-assertions-with-qunit-dom.md | 35 ++- ...-11-17-ember-test-selectors-road-to-1-0.md | 15 +- _posts/2017-12-04-enginification.md | 21 +- _posts/2018-01-24-ember-freestyle.md | 25 +-- ...2018-02-14-handling-webhooks-in-phoenix.md | 22 +- ...e-encouragement-goes-a-long-way-in-2018.md | 9 +- .../2018-06-05-ember-component-playground.md | 22 +- _posts/2018-06-11-actix.md | 83 ++++--- _posts/2018-06-18-intl-polyfill-loading.md | 29 ++- _posts/2018-06-27-actix-tcp-client.md | 50 ++--- ...18-07-03-building-a-pwa-with-glimmer-js.md | 18 +- _posts/2018-07-24-from-spa-to-pwa.md | 79 +++---- _posts/2018-11-27-open-source-maintenance.md | 48 ++-- _posts/2018-12-10-assert-your-style.md | 17 +- _posts/2018-12-20-factories-best-practices.md | 84 ++++--- _posts/2019-02-08-ember-js-film-release.md | 22 +- _posts/2019-02-11-ember-js-film-berlin.md | 20 +- _posts/2019-03-07-march-monthly-update.md | 10 +- _posts/2019-03-13-elixir-umbrella-mox.md | 210 +++++++++--------- _posts/2019-03-18-emberconf-update.md | 6 +- _posts/2019-03-29-qonto-project.md | 6 +- _posts/2019-04-05-april-monthly-update.md | 8 +- _posts/2019-04-05-spas-pwas-and-ssr.md | 23 +- ...019-04-24-dependency-updates-for-gitlab.md | 59 +++-- _posts/2019-05-10-may-monthly-update.md | 6 +- _posts/2019-07-15-sentry-and-ember.md | 105 ++++----- _talks/2015-11-13-rubyday-2015/conference.md | 6 +- .../2015-11-13-rubyday-2015/talks/json-api.md | 6 +- .../2016-07-12-embercamp-2016/conference.md | 6 +- ...tion-and-session-management-in-fastboot.md | 6 +- .../2017-03-28-emberconf-2017/conference.md | 6 +- .../talks/animate-the-web-with-ember.md | 6 +- _talks/2017-05-24-ember-munich/conference.md | 6 +- .../talks/feel-the-glimmer.md | 6 +- .../2017-07-11-embercamp-2017/conference.md | 6 +- ...-leveraging-the-complete-ember-toolbelt.md | 6 +- .../the-modern-state-of-web-components.md | 6 +- _talks/2017-09-18-apicon-2017/conference.md | 4 +- .../talks/the-json-api-spec.md | 6 +- .../conference.md | 6 +- ...ting-modern-web-components-with-glimmer.md | 6 +- _talks/2018-02-22-assertjs-2018/conference.md | 6 +- ...against-time-in-javascript-applications.md | 6 +- .../2018-03-13-emberconf-2018/conference.md | 6 +- ...idnt-tell-you-about-the-ember-community.md | 6 +- .../talks/the-next-generation-of-testing.md | 6 +- .../2018-09-21-embercamp-2018/conference.md | 6 +- .../internationalization-its-easy-in-ember.md | 4 +- .../2018-10-11-emberfest-2018/conference.md | 6 +- .../talks/deliver-fast-apps-even-faster.md | 6 +- .../talks/els-the-ember-language-server.md | 6 +- .../talks/ember-ghost.md | 6 +- .../conference.md | 6 +- .../talks/ssr-spas-and-pwas.md | 6 +- .../conference.md | 6 +- .../talks/crafting-web-comics-with-ember.md | 6 +- .../conference.md | 6 +- ...-whats-going-on-from-a-plane-wno-engine.md | 6 +- .../2019-03-16-emberconf-2019/conference.md | 6 +- ...log-engine-in-15m-with-ember-and-nodejs.md | 6 +- .../2019-06-01-jsconf-eu-2019/conference.md | 4 +- .../crafting-comics-for-literally-everyone.md | 4 +- package.json | 6 +- 99 files changed, 996 insertions(+), 1007 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7377ea0d97..83517e0fc6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,6 +45,9 @@ jobs: - stage: test name: "Lint - CSS" script: yarn lint:css + - stage: test + name: "Lint - Markdown" + script: yarn lint:md - stage: test name: "Crawler" script: yarn build && yarn crawl diff --git a/README.md b/README.md index 1b2872a8ae..8555a9a50a 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,15 @@ using [Puppeteer](https://pptr.dev). ## Installation -* `git clone ` this repository -* `cd simplabs` -* `yarn --pure-lockfile` +- `git clone ` this repository +- `cd simplabs` +- `yarn --pure-lockfile` ## Running / Development -* `ember serve` -* open [http://localhost:4200](http://localhost:4200). -* `yarn format` (format source files) +- `ember serve` +- open [http://localhost:4200](http://localhost:4200). +- `yarn format` (format source files) **This project registers a service worker which you'll likely want to disable for development. Check _"Bypass for network"_ in the _"Application"_ tab in the @@ -27,21 +27,21 @@ Chrome Inspector to do so.** ### Testing -* `ember test` (headless) -* `ember test -s` (headful) -* `yarn lint` (check formatting) +- `ember test` (headless) +- `ember test -s` (headful) +- `yarn lint` (check formatting) ### Building -* `ember build` (development) -* `yarn build` (production, with static pre-rendering) +- `ember build` (development) +- `yarn build` (production, with static pre-rendering) ## Further Reading / Useful Links -* [Glimmer.js](https://glimmerjs.com) -* [navigo](https://github.com/krasimir/navigo) -* [CSS Blocks](https://css-blocks.com) -* [Puppeteer](https://pptr.dev) +- [Glimmer.js](https://glimmerjs.com) +- [navigo](https://github.com/krasimir/navigo) +- [CSS Blocks](https://css-blocks.com) +- [Puppeteer](https://pptr.dev) ## Copyright diff --git a/_calendar/2019-05-25-emberginners.md b/_calendar/2019-05-25-emberginners.md index ce58ba72cd..beccce3ded 100644 --- a/_calendar/2019-05-25-emberginners.md +++ b/_calendar/2019-05-25-emberginners.md @@ -1,6 +1,6 @@ --- -title: "Emberginners Workshop" -image: "/assets/images/calendar/2019-05-25-emberginners/tomster.png" +title: 'Emberginners Workshop' +image: '/assets/images/calendar/2019-05-25-emberginners/tomster.png' location: Berlin url: https://www.meetup.com/Ember-js-Berlin/events/260668987/ kind: meetup @@ -8,4 +8,4 @@ kind: meetup simplabs holds a JavaScript beginner workshop for women and non-binary people that will guide attendees through building their first web application using -EmberJS. \ No newline at end of file +EmberJS. diff --git a/_calendar/2019-06-01-jsconfeu.md b/_calendar/2019-06-01-jsconfeu.md index 83b7ef96bb..385bb5d255 100644 --- a/_calendar/2019-06-01-jsconfeu.md +++ b/_calendar/2019-06-01-jsconfeu.md @@ -1,10 +1,10 @@ --- -title: "JSConf.eu" -image: "/assets/images/calendar/2019-06-01-jsconfeu/logo.png" +title: 'JSConf.eu' +image: '/assets/images/calendar/2019-06-01-jsconfeu/logo.png' location: Berlin url: https://2019.jsconf.eu kind: conference --- Our own [@jjordan_dev](https://twitter.com/jjordan_dev) will speak about -crafting accessible web comics for everyone. \ No newline at end of file +crafting accessible web comics for everyone. diff --git a/_calendar/2019-06-22-commitporto.md b/_calendar/2019-06-22-commitporto.md index f61839ecfa..2b2f82f1dd 100644 --- a/_calendar/2019-06-22-commitporto.md +++ b/_calendar/2019-06-22-commitporto.md @@ -1,10 +1,10 @@ --- title: "Commit Porto '19" -image: "/assets/images/calendar/2019-06-22-commitporto/logo.png" +image: '/assets/images/calendar/2019-06-22-commitporto/logo.png' location: Porto url: https://commitporto.com kind: conference --- Ricardo Mendes will tell a story about the JavaScript hype cycles and how -Ember.js has managed to keep up with them. \ No newline at end of file +Ember.js has managed to keep up with them. diff --git a/_calendar/2019-07-10-fullstacklondon.md b/_calendar/2019-07-10-fullstacklondon.md index 546e229d6d..036117ddf3 100644 --- a/_calendar/2019-07-10-fullstacklondon.md +++ b/_calendar/2019-07-10-fullstacklondon.md @@ -1,10 +1,10 @@ --- -title: "FullStack London 2019" -image: "/assets/images/calendar/2019-07-10-fullstacklondon/logo.png" +title: 'FullStack London 2019' +image: '/assets/images/calendar/2019-07-10-fullstacklondon/logo.png' location: London url: https://skillsmatter.com/conferences/11213-fullstack-london-2019-the-conference-on-javascript-node-and-internet-of-things#program kind: conference --- Jessica Jordan will give a talk about crafting web comics for literally -everyone. \ No newline at end of file +everyone. diff --git a/_calendar/2019-09-19-hiveconf.md b/_calendar/2019-09-19-hiveconf.md index d260caf105..f5863b0ff0 100644 --- a/_calendar/2019-09-19-hiveconf.md +++ b/_calendar/2019-09-19-hiveconf.md @@ -1,10 +1,10 @@ --- title: "HiveConf '19" -image: "/assets/images/calendar/2019-09-19-hiveconf/logo.png" +image: '/assets/images/calendar/2019-09-19-hiveconf/logo.png' location: Berlin url: http://hiveconf19.eventbrite.com kind: conference --- Jessica Jordan will be joining the panel to talk about topics around Tech and -HR. \ No newline at end of file +HR. diff --git a/_calendar/2019-10-17-emberfest.md b/_calendar/2019-10-17-emberfest.md index 9806760605..b36ebc5862 100644 --- a/_calendar/2019-10-17-emberfest.md +++ b/_calendar/2019-10-17-emberfest.md @@ -1,10 +1,10 @@ --- -title: "EmberFest 2019" -image: "/assets/images/calendar/2019-10-17-emberfest/logo.png" +title: 'EmberFest 2019' +image: '/assets/images/calendar/2019-10-17-emberfest/logo.png' location: Berlin url: https://emberfest.eu kind: conference --- We are co-organizing EmberFest and the team is looking forward to meeting up -with the Ember.js community in Copenhagen. \ No newline at end of file +with the Ember.js community in Copenhagen. diff --git a/_calendar/2019-10-30-reactiveconf.md b/_calendar/2019-10-30-reactiveconf.md index cbb87cd790..7cafa8f731 100644 --- a/_calendar/2019-10-30-reactiveconf.md +++ b/_calendar/2019-10-30-reactiveconf.md @@ -1,9 +1,9 @@ --- -title: "ReactiveConf 2019" -image: "/assets/images/calendar/2019-10-30-reactiveconf/logo.jpg" +title: 'ReactiveConf 2019' +image: '/assets/images/calendar/2019-10-30-reactiveconf/logo.jpg' location: Prague url: https://reactiveconf.com kind: conference --- -Jessica Jordan will deliver a talk at ReactiveConf in Prague this October. \ No newline at end of file +Jessica Jordan will deliver a talk at ReactiveConf in Prague this October. diff --git a/_posts/2013-06-15-authentication-in-emberjs.md b/_posts/2013-06-15-authentication-in-emberjs.md index c462286d61..e86e0bae5a 100755 --- a/_posts/2013-06-15-authentication-in-emberjs.md +++ b/_posts/2013-06-15-authentication-in-emberjs.md @@ -1,16 +1,16 @@ --- -title: "Authentication in ember.js" -author: "Marco Otte-Witte" +title: 'Authentication in ember.js' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" +bio: 'Founding Director of simplabs, author of Ember Simple Auth' topic: ember -description: "Marco Otte-Witte describes an approach for implementing a session mechanism, authentication and authorization in Ember.js applications." +description: 'Marco Otte-Witte describes an approach for implementing a session mechanism, authentication and authorization in Ember.js applications.' --- **Update:**_I released an Ember.js plugin that makes it very easy to implement an authentication system as described in this post: [Ember.SimpleAuth](/blog/2013/10/09/embersimpleauth)._ -**Update:** _After I wrote this I found out that it’s actually not the best approach to implement authentication in Ember.js… There are some things missing and some other things can be done in a much simpler way. [I wrote a summary of the (better) authentication mechanism we moved to.](/blog/2013/08/08/better-authentication-in-emberjs "(better) authnetication with ember.js")_ +**Update:** _After I wrote this I found out that it’s actually not the best approach to implement authentication in Ember.js… There are some things missing and some other things can be done in a much simpler way. [I wrote a summary of the (better) authentication mechanism we moved to.](/blog/2013/08/08/better-authentication-in-emberjs '(better) authnetication with ember.js')_ _I’m using the latest (as of mid June 2013) [ember](https://github.com/emberjs/ember.js)/[ember-data](https://github.com/emberjs/data)/[handlebars](https://github.com/wycats/handlebars.js) code directly from the respective github repositories in this example._ @@ -36,7 +36,7 @@ The general route to go with authentication in ember.js is to use **token based That template is backed by a route that handles the submission event and posts the data to the /session route on the server - which then responds with either status 401 or 200 and a JSON containing the authentication token and the id of the authenticated user: - + ```js App.SessionsNewRoute = Ember.Route.extend({ events: { @@ -46,19 +46,20 @@ App.SessionsNewRoute = Ember.Route.extend({ var password = this.controller.get('password'); if (!Ember.isEmpty(loginOrEmail) && !Ember.isEmpty(password)) { $.post('/session', { - session: { login_or_email: loginOrEmail, password: password } - }, function(data) { + session: { login_or_email: loginOrEmail, password: password }, + }, + function(data) { var authToken = data.session.auth_token; App.Store.authToken = authToken; App.Auth = Ember.Object.create({ authToken: data.session.auth_token, - accountId: data.session.account_id + accountId: data.session.account_id, }); router.transitionTo('index'); }); } - } - } + }, + }, }); ``` @@ -68,9 +69,9 @@ The response JSON from the server would look somehow like this in the successful ```json { - session: { - auth_token: '', - account_id: '' + "session": { + "auth_token": "", + "account_id": "" } } ``` @@ -89,11 +90,11 @@ The next step is to actually send the authentication token to the server. As the ```js App.AuthenticatedRESTAdapter = DS.RESTAdapter.extend({ ajax: function(url, type, hash) { - hash = hash || {}; + hash = hash || {}; hash.headers = hash.headers || {}; hash.headers['X-AUTHENTICATION-TOKEN'] = this.authToken; return this._super(url, type, hash); - } + }, }); ``` @@ -118,7 +119,7 @@ App.AuthenticatedRoute = Ember.Route.extend({ if (!Ember.isEmpty(App.Auth.get('authToken')) && !Ember.isEmpty(App.Auth.get('accountId'))) { this.transitionTo('sessions.new'); } - } + }, }); ``` @@ -139,17 +140,18 @@ As the code is now spread up into a number of files and classes, I added a `Sess ```js App.Session = DS.Model.extend({ authToken: DS.attr('string'), - account: DS.belongsTo('App.Account') + account: DS.belongsTo('App.Account'), }); ``` alongside an `App.AuthManager` accompanied by a custom initializer to clean it up: + ```js App.AuthManager = Ember.Object.extend({ init: function() { this._super(); - var authToken = $.cookie('auth_token'); + var authToken = $.cookie('auth_token'); var authAccountId = $.cookie('auth_account'); if (!Ember.isEmpty(authToken) && !Ember.isEmpty(authAccountId)) { this.authenticate(authToken, authAccountId); @@ -164,7 +166,7 @@ App.AuthManager = Ember.Object.extend({ var account = App.Account.find(accountId); this.set('session', App.Session.createRecord({ authToken: authToken, - account: account + account: account, })); }, @@ -181,7 +183,7 @@ App.AuthManager = Ember.Object.extend({ $.cookie('auth_token', this.get('session.authToken')); $.cookie('auth_account', this.get('session.account.id')); } - }.observes('session') + }.observes('session'), }); ``` diff --git a/_posts/2013-06-27-excellent-172.md b/_posts/2013-06-27-excellent-172.md index 1e5b197da8..49cee9a240 100755 --- a/_posts/2013-06-27-excellent-172.md +++ b/_posts/2013-06-27-excellent-172.md @@ -1,10 +1,10 @@ --- -title: "excellent 1.7.2" -author: "Marco Otte-Witte" +title: 'excellent 1.7.2' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte announces excellent 1.7.2 with 3 new checks and a more forgiving and stable parser mechanism." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte announces excellent 1.7.2 with 3 new checks and a more forgiving and stable parser mechanism.' topic: ruby --- @@ -12,7 +12,7 @@ We just released excellent 1.7.2 which includes the following fixes: -* fixed `Simplabs::Excellent::Checks::Rails::CustomInitializeMethodCheck` -* fixed `Simplabs::Excellent::Checks::MethodNameCheck` so it allows method names that exist in Ruby itself -* fixed `Simplabs::Excellent::Checks::GlobalVariableCheck` so it doesn't report false positives for rescue bodies -* made the parser more forgiving/stable in some situations +- fixed `Simplabs::Excellent::Checks::Rails::CustomInitializeMethodCheck` +- fixed `Simplabs::Excellent::Checks::MethodNameCheck` so it allows method names that exist in Ruby itself +- fixed `Simplabs::Excellent::Checks::GlobalVariableCheck` so it doesn't report false positives for rescue bodies +- made the parser more forgiving/stable in some situations diff --git a/_posts/2013-06-28-excellent-200.md b/_posts/2013-06-28-excellent-200.md index 23a6abf6ce..74b51d0e69 100755 --- a/_posts/2013-06-28-excellent-200.md +++ b/_posts/2013-06-28-excellent-200.md @@ -1,10 +1,10 @@ --- -title: "excellent 2.0.0" -author: "Marco Otte-Witte" +title: 'excellent 2.0.0' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte announces excellent 2.0.0 with support for a configuration file, a whitelist of allowed global variables and re-enabled standard checks." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte announces excellent 2.0.0 with support for a configuration file, a whitelist of allowed global variables and re-enabled standard checks.' topic: ruby --- @@ -12,8 +12,8 @@ We just released excellent 2.0.0 which has some big improvements: -* now supporting config file `.excellent.yml` in current working directory to configure which specs to run/ not to run with thresholds, patterns etc. -* predefined globals will not be reported anymore (`$!`, `$@`, `$&`, `$```, `$’`, `$+`, `$1`, `$2..`, `$~`, `$=`, `$/`, `$\`, `$,`, `$;`, `$.`, `$`, `$_`, `$0`, `$*`, `$$`, `$?`, `$:`, `$"`, `$DEBUG`, `$FILENAME`, `$LOAD_PATH`, `$stdin`, `$stdout`, `$stderr`, `$VERBOSE`, `$-0`, `$-a`, `$-d`, `$-F`, `$-i`, `$-I`, `$-l`, `$-p`, `$-v`) -* enabled previously disable checks again: `AbcMetricMethodCheck`, `ControlCouplingCheck`, `CyclomaticComplexityBlockCheck`, `CyclomaticComplexityMethodCheck`, `ForLoopCheck`, `FlogBlockCheck`, `FlogClassCheck`, `FlogMethodCheck` -* testing now uses Rspec 2 -* internal cleanups/simplifications +- now supporting config file `.excellent.yml` in current working directory to configure which specs to run/ not to run with thresholds, patterns etc. +- predefined globals will not be reported anymore (`$!`, `$@`, `$&`, ` $```, `\$’`,`\$+`,`\$1`,`\$2..`,`\$~`,`\$=`,`\$/`,`\$\`,`\$,`,`\$;`,`\$.`,`\$`,`\$\_`,`\$0`,`\$\*`,`\$\$`,`\$?`,`\$:`,`\$"`,`\$DEBUG`,`\$FILENAME`,`\$LOAD_PATH`,`\$stdin`,`\$stdout`,`\$stderr`,`\$VERBOSE`,`\$-0`,`\$-a`,`\$-d`,`\$-F`,`\$-i`,`\$-I`,`\$-l`,`\$-p`,`\$-v`) +- enabled previously disable checks again: `AbcMetricMethodCheck`, `ControlCouplingCheck`, `CyclomaticComplexityBlockCheck`, `CyclomaticComplexityMethodCheck`, `ForLoopCheck`, `FlogBlockCheck`, `FlogClassCheck`, `FlogMethodCheck` +- testing now uses Rspec 2 +- internal cleanups/simplifications diff --git a/_posts/2013-08-08-better-authentication-in-emberjs.md b/_posts/2013-08-08-better-authentication-in-emberjs.md index cdf0b49a7c..ab20740afc 100755 --- a/_posts/2013-08-08-better-authentication-in-emberjs.md +++ b/_posts/2013-08-08-better-authentication-in-emberjs.md @@ -1,10 +1,10 @@ --- -title: "(better) Authentication in ember.js" -author: "Marco Otte-Witte" +title: '(better) Authentication in ember.js' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte introduces an update to the mechanism for implementing a session, authentication and authorization in Ember.js applications." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte introduces an update to the mechanism for implementing a session, authentication and authorization in Ember.js applications.' topic: ember --- @@ -16,7 +16,7 @@ When we started our first [ember.js](http://emberjs.com) project in June 2013, o _I’m using the latest (as of early August 2013) [ember.js](http://emberjs.com) and [handlebars](http://handlebarsjs.com) releases in this example._ -_**Update:**I changed the section on actually using the token to use [$.ajaxPrefilter](http://api.jquery.com/jQuery.ajaxPrefilter/) instead of a custom ember-data adapter_ +_**Update:**I changed the section on actually using the token to use [\$.ajaxPrefilter](http://api.jquery.com/jQuery.ajaxPrefilter/) instead of a custom ember-data adapter_ ## The basics @@ -28,6 +28,7 @@ This data is stored in a _"session"_ object on the client side (while technicall The _"session"_ object on the client side is a **plain `Ember.Object` that simply keeps the data that is received from the server** on session creation. It also stores the authentication token and the user’s account ID in cookies so the user doesn’t have to login again after a page reload (As Ed points out in a comment on the old post it’s a security issue to store the authentication token in a cookie without the user’s permission - I think using a session cookie like I do should be ok as it’s deleted when the browser window is closed. Of course you could also use sth. like localStorage like Marc points out. I’m creating this object in an initializer so I can be sure it exists (of course it might be empty) when the application starts. + ```js Ember.Application.initializer({ name: 'session', @@ -89,6 +90,7 @@ end As described above, the login API is a simple `/session` route on the server side that accepts the user’s login and password and **responds with either HTTP status 401 when the credentials are invalid or a session JSON when the authentication was successful**. On the client side we have routes for creating and destroying the session: + ```js App.Router.map(function() { this.resource('session', function() { @@ -100,6 +102,7 @@ App.Router.map(function() { The `SessionNewController` only needs one action `login` that sends the entered credentials and acts according to the server’s response - if the server responds successfully **it reads the session data from the response and updates the `App.Session` object accordingly**. It also checks whether there is an attempted transition that was intercepted due to missing authentication and retries that if it exists (This is the case where the user tries to access a certain page without having authenticated, is redirected to the login form, logs in and is redirected again to the initially requested page). + ```js App.SessionNewController = Ember.Controller.extend({ login: function() { @@ -108,9 +111,9 @@ App.SessionNewController = Ember.Controller.extend({ if (!Ember.isEmpty(data.loginOrEmail) && !Ember.isEmpty(data.password)) { var postData = { session: { login_or_email: data.loginOrEmail, password: data.password } }; $.post('/session', postData).done(function(response) { - var sessionData = (response.session || {}) + var sessionData = response.session || {}; App.Session.setProperties({ - authToken: sessionData.auth_token, + authToken: sessionData.auth_token, authAccountId: sessionData.account_id }); var attemptedTransition = App.Session.get('attemptedTransition'); @@ -142,16 +145,17 @@ The template is just a simple form (actual elements, classes etc. of course depe Logging out is actually pretty simple as well. The **client just sends a `DELETE` to the same `/session` route** that makes the server reset the authentication token in the database so that the token on the client side is invalidated. The client also deletes the saved session information in `App.Session` so there’s no stale data. + ```js App.SessionDestroyController = Ember.Controller.extend({ logout: function() { var self = this; $.ajax({ - url: '/session', + url: '/session', type: 'DELETE' }).always(function(response) { App.Session.setProperties({ - authToken: '', + authToken: '', authAccountId: '' }); self.transitionToRoute('session.new'); @@ -162,6 +166,7 @@ App.SessionDestroyController = Ember.Controller.extend({ As this action should be triggered as soon as the user enters the `/#/session/destroy` route, we have a simple route implementation that **triggers the action upon route activation**: + ```js App.SessionDestroyRoute = Ember.Route.extend({ renderTemplate: function(controller, model) { @@ -174,6 +179,7 @@ App.SessionDestroyRoute = Ember.Route.extend({ To easily enable authentication for any route in the application, I created an **`App.AuthenticatedRoute` that extends `Ember.Route`** and that all routes that need to enforce user authentication can extend again: + ```js App.AuthenticatedRoute = Ember.Route.extend({ redirectToLogin: function(transition) { diff --git a/_posts/2013-10-09-embersimpleauth.md b/_posts/2013-10-09-embersimpleauth.md index 2da759f918..ec19a38e60 100755 --- a/_posts/2013-10-09-embersimpleauth.md +++ b/_posts/2013-10-09-embersimpleauth.md @@ -1,16 +1,16 @@ --- -title: "Ember.SimpleAuth" -author: "Marco Otte-Witte" +title: 'Ember.SimpleAuth' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte announces Ember.SimpleAuth, an addon for implementing a session mechanism, authentication and authorization for Ember.js applications." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte announces Ember.SimpleAuth, an addon for implementing a session mechanism, authentication and authorization for Ember.js applications.' topic: ember --- **Update: [Ember.SimpleAuth 0.1.0 has been released!](/blog/2014/01/20/embersimpleauth-010)** The information in this is (partially) outdated. -After I wrote 2 [blog](/blog/2013/06/15/authentication-in-emberjs "the initial post") [posts](/blog/2013/08/08/better-authentication-in-emberjs "the second post with a refined implementation") on implementing token based authentication in [Ember.js](http://emberjs.com) applications and got quite some feedback, good suggestions etc., I thought it **would be nice to pack all these ideas in an Ember.js plugin** so everybody could easily integrate that into their applications. Now **I finally managed to release version 0.0.1 of that plugin**: [Ember.SimpleAuth](https://github.com/simplabs/ember-simple-auth). +After I wrote 2 [blog](/blog/2013/06/15/authentication-in-emberjs 'the initial post') [posts](/blog/2013/08/08/better-authentication-in-emberjs 'the second post with a refined implementation') on implementing token based authentication in [Ember.js](http://emberjs.com) applications and got quite some feedback, good suggestions etc., I thought it **would be nice to pack all these ideas in an Ember.js plugin** so everybody could easily integrate that into their applications. Now **I finally managed to release version 0.0.1 of that plugin**: [Ember.SimpleAuth](https://github.com/simplabs/ember-simple-auth). @@ -25,7 +25,7 @@ Ember.Application.initializer({ name: 'authentication', initialize: function(container, application) { Ember.SimpleAuth.setup(application); - } + }, }); ``` @@ -42,7 +42,7 @@ Then, the generated **controller and route must implement the mixins provided by ```js App.LoginController = Ember.Controller.extend(Ember.SimpleAuth.LoginControllerMixin); -App.LogoutRoute = Ember.Route.extend(Ember.SimpleAuth.LogoutRouteMixin); +App.LogoutRoute = Ember.Route.extend(Ember.SimpleAuth.LogoutRouteMixin); ``` Of course the application also needs a template that renders the login form: diff --git a/_posts/2013-10-28-embersimpleauth-implements-rfc-6749-oauth-20.md b/_posts/2013-10-28-embersimpleauth-implements-rfc-6749-oauth-20.md index 9e36e054aa..018d41abad 100755 --- a/_posts/2013-10-28-embersimpleauth-implements-rfc-6749-oauth-20.md +++ b/_posts/2013-10-28-embersimpleauth-implements-rfc-6749-oauth-20.md @@ -1,10 +1,10 @@ --- -title: "Ember.SimpleAuth implements RFC 6749 (OAuth 2.0)" -author: "Marco Otte-Witte" +title: 'Ember.SimpleAuth implements RFC 6749 (OAuth 2.0)' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte announces support for OAuth 2.0 in Ember.SimpleAuth, the addon for implementing a session and authentication/authorization for Ember.js." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte announces support for OAuth 2.0 in Ember.SimpleAuth, the addon for implementing a session and authentication/authorization for Ember.js.' topic: ember --- @@ -25,7 +25,7 @@ Ember.Application.initializer({ name: 'authentication', initialize: function(container, application) { Ember.SimpleAuth.setup(container, application); - } + }, }); App.Router.map(function() { @@ -34,7 +34,7 @@ App.Router.map(function() { }); App.ApplicationRoute = Ember.Route.extend(Ember.SimpleAuth.ApplicationRouteMixin); -App.LoginController = Ember.Controller.extend(Ember.SimpleAuth.LoginControllerMixin); +App.LoginController = Ember.Controller.extend(Ember.SimpleAuth.LoginControllerMixin); ``` ## Future plans diff --git a/_posts/2014-01-20-embersimpleauth-010.md b/_posts/2014-01-20-embersimpleauth-010.md index 87536e99a6..7f18a303eb 100755 --- a/_posts/2014-01-20-embersimpleauth-010.md +++ b/_posts/2014-01-20-embersimpleauth-010.md @@ -1,10 +1,10 @@ --- -title: "Ember.SimpleAuth 0.1.0" -author: "Marco Otte-Witte" +title: 'Ember.SimpleAuth 0.1.0' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte announces Ember.SimpleAuth 0.1.0 with an improved architecture that allows for arbitrary authentication and authorization strategies." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte announces Ember.SimpleAuth 0.1.0 with an improved architecture that allows for arbitrary authentication and authorization strategies.' topic: ember --- @@ -18,7 +18,7 @@ The most significant change is the **extraction of everything specific to specif ```js App.LoginController = Ember.Controller.extend(Ember.SimpleAuth.LoginControllerMixin, { - authenticator: App.CustomAuthenticator + authenticator: App.CustomAuthenticator, }); ``` diff --git a/_posts/2014-03-11-embersimpleauth-020.md b/_posts/2014-03-11-embersimpleauth-020.md index afffe1fd93..c320c689d0 100755 --- a/_posts/2014-03-11-embersimpleauth-020.md +++ b/_posts/2014-03-11-embersimpleauth-020.md @@ -1,10 +1,10 @@ --- -title: "Ember.SimpleAuth 0.2.0" -author: "Marco Otte-Witte" +title: 'Ember.SimpleAuth 0.2.0' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte announces Ember.SimpleAuth 0.2.0 with a completely rebuilt build and testing infrastructure as well as important bug fixes." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte announces Ember.SimpleAuth 0.2.0 with a completely rebuilt build and testing infrastructure as well as important bug fixes.' topic: ember --- diff --git a/_posts/2014-04-06-embersimpleauth-021.md b/_posts/2014-04-06-embersimpleauth-021.md index aa3f6c1500..8fc7ed2f76 100755 --- a/_posts/2014-04-06-embersimpleauth-021.md +++ b/_posts/2014-04-06-embersimpleauth-021.md @@ -1,10 +1,10 @@ --- -title: "Ember.SimpleAuth 0.2.1" -author: "Marco Otte-Witte" +title: 'Ember.SimpleAuth 0.2.1' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte announces Ember.SimpleAuth 0.2.1 with some minor improvements." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte announces Ember.SimpleAuth 0.2.1 with some minor improvements.' topic: ember --- @@ -12,4 +12,4 @@ We released Ember.SimpleAuth 0.2.1. -Ember.SimpleAuth 0.2.1 was released with some minor improvements: [https://github.com/simplabs/ember-simple-auth/releases/tag/0.2.1](https://github.com/simplabs/ember-simple-auth/releases/tag/0.2.1) \ No newline at end of file +Ember.SimpleAuth 0.2.1 was released with some minor improvements: [https://github.com/simplabs/ember-simple-auth/releases/tag/0.2.1](https://github.com/simplabs/ember-simple-auth/releases/tag/0.2.1) diff --git a/_posts/2014-04-10-embersimpleauth-030.md b/_posts/2014-04-10-embersimpleauth-030.md index c9bea2ab3c..6bfea92a7e 100755 --- a/_posts/2014-04-10-embersimpleauth-030.md +++ b/_posts/2014-04-10-embersimpleauth-030.md @@ -1,10 +1,10 @@ --- -title: "Ember.SimpleAuth 0.3.0" -author: "Marco Otte-Witte" +title: 'Ember.SimpleAuth 0.3.0' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte announces Ember.SimpleAuth 0.3.0, splitting the addon into a core and extensions for specific authentication/authorization mechanisms." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte announces Ember.SimpleAuth 0.3.0, splitting the addon into a core and extensions for specific authentication/authorization mechanisms.' topic: ember --- @@ -14,9 +14,9 @@ Ember.SimpleAuth 0.3.0 was just released. The **main change in this release is t These extension libraries are: -* **ember-simple-auth-oauth2**: includes the OAuth 2.0 authenticator and authorizer which are just one option out of many and probably not needed in many projects (e.g. when using a custom authenticator) -* **ember-simple-auth-devise**: new authenticator/authorizer package that is compatible with the Ruby gem [devise](https://github.com/plataformatec/devise). -* **ember-simple-auth-cookie-store**: The cookie store which is probably only used in few projects. +- **ember-simple-auth-oauth2**: includes the OAuth 2.0 authenticator and authorizer which are just one option out of many and probably not needed in many projects (e.g. when using a custom authenticator) +- **ember-simple-auth-devise**: new authenticator/authorizer package that is compatible with the Ruby gem [devise](https://github.com/plataformatec/devise). +- **ember-simple-auth-cookie-store**: The cookie store which is probably only used in few projects. There are hopefully more extension libraries to come as people start to provide more authenticators, authorizers and other components and now there’s a (more or less, pre 1.0) stable API for these libraries as well as a set of tasks to build and test them @@ -27,9 +27,9 @@ Ember.Application.initializer({ name: 'authentication', initialize: function(container, application) { Ember.SimpleAuth.setup(container, application, { - authorizerFactory: 'authorizer:oauth2-bearer' + authorizerFactory: 'authorizer:oauth2-bearer', }); - } + }, }); ``` diff --git a/_posts/2014-04-24-embersimpleauth-needs-a-logo.md b/_posts/2014-04-24-embersimpleauth-needs-a-logo.md index 30d93137ef..db1e085573 100755 --- a/_posts/2014-04-24-embersimpleauth-needs-a-logo.md +++ b/_posts/2014-04-24-embersimpleauth-needs-a-logo.md @@ -1,10 +1,10 @@ --- -title: "Ember.SimpleAuth needs a logo!" -author: "Marco Otte-Witte" +title: 'Ember.SimpleAuth needs a logo!' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte posts a request for the community to contribute a logo to Ember.SimpleAuth." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte posts a request for the community to contribute a logo to Ember.SimpleAuth.' topic: ember --- @@ -12,4 +12,4 @@ The project needs a logo. -Every good open source project needs a nice and shiny logo these days. It would be great if Ember.SimpleAuth had one as well. As I’m not really an expert in these things it would be great if some of the more artistic advanced contributors/followers could [contribute something](https://github.com/simplabs/ember-simple-auth/issues/152)! \ No newline at end of file +Every good open source project needs a nice and shiny logo these days. It would be great if Ember.SimpleAuth had one as well. As I’m not really an expert in these things it would be great if some of the more artistic advanced contributors/followers could [contribute something](https://github.com/simplabs/ember-simple-auth/issues/152)! diff --git a/_posts/2014-06-30-using-ember-simple-auth-with-ember-cli.md b/_posts/2014-06-30-using-ember-simple-auth-with-ember-cli.md index 69e62a82b7..0d06c20b73 100755 --- a/_posts/2014-06-30-using-ember-simple-auth-with-ember-cli.md +++ b/_posts/2014-06-30-using-ember-simple-auth-with-ember-cli.md @@ -1,10 +1,10 @@ --- -title: "Using Ember Simple Auth with ember-cli" -author: "Marco Otte-Witte" +title: 'Using Ember Simple Auth with ember-cli' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte announces the release of ember-cli-simple-auth as an Ember CLI addon." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte announces the release of ember-cli-simple-auth as an Ember CLI addon.' topic: ember --- @@ -110,7 +110,7 @@ import Ember from 'ember'; import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin'; export default Ember.Controller.extend(LoginControllerMixin, { - authenticator: 'simple-auth-authenticator:oauth2-password-grant' + authenticator: 'simple-auth-authenticator:oauth2-password-grant', }); ``` diff --git a/_posts/2014-07-25-testing-with-ember-simple-auth-and-ember-cli.md b/_posts/2014-07-25-testing-with-ember-simple-auth-and-ember-cli.md index 28ce486bcf..e931ad2744 100755 --- a/_posts/2014-07-25-testing-with-ember-simple-auth-and-ember-cli.md +++ b/_posts/2014-07-25-testing-with-ember-simple-auth-and-ember-cli.md @@ -1,14 +1,14 @@ --- -title: "Testing with Ember Simple Auth and Ember CLI" -author: "Marco Otte-Witte" +title: 'Testing with Ember Simple Auth and Ember CLI' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte explains how to test Ember CLI applications using ember-cli-simple-auth with the testing package ember-cli-simple-auth-testing." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte explains how to test Ember CLI applications using ember-cli-simple-auth with the testing package ember-cli-simple-auth-testing.' topic: ember --- -[The last blog post](/blog/2014/06/30/using-ember-simple-auth-with-ember-cli "Using Ember Simple Auth with ember-cli") showed how to use [Ember Simple Auth](https://github.com/simplabs/ember-simple-auth) with [Ember CLI](https://github.com/ember-cli/ember-cli) to implement session handling and authentication. **This post shows how to test that code**. +[The last blog post](/blog/2014/06/30/using-ember-simple-auth-with-ember-cli 'Using Ember Simple Auth with ember-cli') showed how to use [Ember Simple Auth](https://github.com/simplabs/ember-simple-auth) with [Ember CLI](https://github.com/ember-cli/ember-cli) to implement session handling and authentication. **This post shows how to test that code**. @@ -40,8 +40,8 @@ The next step is to configure the `test` environment. As the tests should be iso // config/environment.js if (environment === 'test') { ENV['simple-auth'] = { - store: 'simple-auth-session-store:ephemeral' - } + store: 'simple-auth-session-store:ephemeral', + }; } ``` diff --git a/_posts/2014-10-15-the-most-important-command-when-working-with-xcode-6.md b/_posts/2014-10-15-the-most-important-command-when-working-with-xcode-6.md index ee97bf8b2f..c91da8e584 100755 --- a/_posts/2014-10-15-the-most-important-command-when-working-with-xcode-6.md +++ b/_posts/2014-10-15-the-most-important-command-when-working-with-xcode-6.md @@ -1,9 +1,9 @@ --- -title: "The most important command when working with XCode 6" -author: "Marco Otte-Witte" +title: 'The most important command when working with XCode 6' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" +bio: 'Founding Director of simplabs, author of Ember Simple Auth' description: "Marco Otte-Witte shows how to disable XCode 6's crash reporter dialog for a less annoying development experience." topic: misc --- @@ -16,4 +16,4 @@ Switching off XCode's Crash Reporter saved my day. defaults write com.apple.CrashReporter DialogType none ``` -This disables the Crash Reporter window that shows when SourceKitService crashes (which is all the time). So while it doesn’t prevent SourceKitService from crashing at least you don’t have to click that window away anymore. \ No newline at end of file +This disables the Crash Reporter window that shows when SourceKitService crashes (which is all the time). So while it doesn’t prevent SourceKitService from crashing at least you don’t have to click that window away anymore. diff --git a/_posts/2015-06-03-emberjs-workshop-in-munich.md b/_posts/2015-06-03-emberjs-workshop-in-munich.md index b2d8e56ef4..18eec2bf76 100755 --- a/_posts/2015-06-03-emberjs-workshop-in-munich.md +++ b/_posts/2015-06-03-emberjs-workshop-in-munich.md @@ -1,32 +1,32 @@ --- -title: "Ember.js Workshop in Munich, July 22nd-24th" -author: "Marco Otte-Witte" +title: 'Ember.js Workshop in Munich, July 22nd-24th' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" +bio: 'Founding Director of simplabs, author of Ember Simple Auth' description: "Marco Otte-Witte announces simplabs' 3-day Ember.js workshop in Munich, introducing participants to all aspects of the framework." topic: ember --- -We are organizing a [three day Ember.js Workshop in Munich from July 22nd to 24th](http://ember-workshop.simplabs.com), **taking participants through building a full Ember.js application**. +We are organizing a [three day Ember.js Workshop in Munich from July 22nd to 24th](http://ember-workshop.simplabs.com), **taking participants through building a full Ember.js application**. The topics we will cover are: -* Ember Basics - understanding the core concepts -* Ember CLI and the container -* The Run-Loop, Promises and ES2015 -* Routing from the outside in -* Ember Data - connecting an application to an API -* JSON API -* Components - data down, actions up -* Services and managing State -* Testing Ember apps -* Debugging Ember apps and the Ember Inspector -* Authentication -* Deployment -* Animations and Effects with Liquid Fire -* Realtime Data with Websockets +- Ember Basics - understanding the core concepts +- Ember CLI and the container +- The Run-Loop, Promises and ES2015 +- Routing from the outside in +- Ember Data - connecting an application to an API +- JSON API +- Components - data down, actions up +- Services and managing State +- Testing Ember apps +- Debugging Ember apps and the Ember Inspector +- Authentication +- Deployment +- Animations and Effects with Liquid Fire +- Realtime Data with Websockets -**There will also be a Welcome Party** at the first evening and luch served at the Workshop Venue! [Register now](http://ember-workshop.simplabs.com "Ember.js Workshop in Munich, July 22nd-24th") as we only have limited capacities. \ No newline at end of file +**There will also be a Welcome Party** at the first evening and luch served at the Workshop Venue! [Register now](http://ember-workshop.simplabs.com 'Ember.js Workshop in Munich, July 22nd-24th') as we only have limited capacities. diff --git a/_posts/2015-07-29-ember-simple-auth-10-a-first-look.md b/_posts/2015-07-29-ember-simple-auth-10-a-first-look.md index 8d1680dc77..f1a0b104ab 100755 --- a/_posts/2015-07-29-ember-simple-auth-10-a-first-look.md +++ b/_posts/2015-07-29-ember-simple-auth-10-a-first-look.md @@ -1,10 +1,10 @@ --- -title: "Ember Simple Auth 1.0 - a first look" -author: "Marco Otte-Witte" +title: 'Ember Simple Auth 1.0 - a first look' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte gives an outlook on Ember Simple Auth 1.0 with Ember.js 2.0 support, a session service and discontinued support for non-Ember CLI projects." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte gives an outlook on Ember Simple Auth 1.0 with Ember.js 2.0 support, a session service and discontinued support for non-Ember CLI projects.' topic: ember --- @@ -20,6 +20,7 @@ The biggest improvement of course is that the **library will be compatible with While previous versions of Ember Simple Auth injected the session into routes, controllers and components, **Ember Simple Auth 1.0 drops that and instead provides a new `Session` service** that encapsulates all access to the actual session and can simply be injected wherever necessary, e.g.: + ```js import Ember from 'ember'; @@ -43,6 +44,7 @@ It provides a similar UI as the old session object, including `authenticate` and Also support for defining a custom session class has been dropped in favor of defining a service that uses the session service to provide functionality based on that. E.g. the typical use case of providing access to the authenticated account can now easily be implemented with a `SessionAccount` service that in turn uses the `Session` service: + ```js import Ember from 'ember'; @@ -55,10 +57,10 @@ export default Ember.Service.extend({ const accountId = this.get('session.content.secure.account_id'); if (!isEmpty(accountId)) { return DS.PromiseObject.create({ - promise: this.container.lookup('store:main').find('account', accountId) + promise: this.container.lookup('store:main').find('account', accountId), }); } - }) + }), }); ``` @@ -66,17 +68,17 @@ export default Ember.Service.extend({ While previous versions of Ember Simple Auth provided 3 different versions (AMD, globals and the Ember CLI addons) where the Ember CLI addon was also split up into several sub addons, **1.0 will come as one single Ember CLI addon**. This offers 3 main advantages: -* The project structure is the standard Ember CLI addon structure and no longer a custom set of grunt tasks etc. This not only makes the development and release process much simpler but also promotes contributions from others. -* It is now much simpler to install Ember CLI as it’s now only `ember install ember-simple-auth` command instead of install at least 2 packages or potentially more. -* Since all the source is now transpiled with Babel as part of the Ember CLI build process, all the source code has been updated to use thinks like template strings, function literals, deconstruction etc., making the code far more concise and readable. +- The project structure is the standard Ember CLI addon structure and no longer a custom set of grunt tasks etc. This not only makes the development and release process much simpler but also promotes contributions from others. +- It is now much simpler to install Ember CLI as it’s now only `ember install ember-simple-auth` command instead of install at least 2 packages or potentially more. +- Since all the source is now transpiled with Babel as part of the Ember CLI build process, all the source code has been updated to use thinks like template strings, function literals, deconstruction etc., making the code far more concise and readable. ## Getting to 1.0 1.0 is still not fully finished and ready for release. While I plan to release a first beta until next week latest, getting to the final release still requires some work: -* The docs need to be updated to fit the new API and modules. Existing documentation is still based on classes and namespaces and we need to find a way to convert that so we document modules instead. -* The docs are also not complete for new features like the **Session** service and existing documentation needs to be reviewed for whether it’s actually still correct. -* The code should be reviewed and cleaned up before the final 1.0 release - much of it is still what I wrote almost 2 years ago when I was still relatively new to Ember and could probably be greatly improved. -* The setup of the AJAX prefilter should be decoupled from the library so that it’s opt-in and could be replaced with a different approach (e.g. sth. that sets a property on the application’s Ember Data adapter instead) - see [#522](https://github.com/simplabs/ember-simple-auth/pull/522) and [#270](https://github.com/simplabs/ember-simple-auth/issues/270). A compatibility Addon should be extracted that implements the current behavior of setting up the prefilter automatically. +- The docs need to be updated to fit the new API and modules. Existing documentation is still based on classes and namespaces and we need to find a way to convert that so we document modules instead. +- The docs are also not complete for new features like the **Session** service and existing documentation needs to be reviewed for whether it’s actually still correct. +- The code should be reviewed and cleaned up before the final 1.0 release - much of it is still what I wrote almost 2 years ago when I was still relatively new to Ember and could probably be greatly improved. +- The setup of the AJAX prefilter should be decoupled from the library so that it’s opt-in and could be replaced with a different approach (e.g. sth. that sets a property on the application’s Ember Data adapter instead) - see [#522](https://github.com/simplabs/ember-simple-auth/pull/522) and [#270](https://github.com/simplabs/ember-simple-auth/issues/270). A compatibility Addon should be extracted that implements the current behavior of setting up the prefilter automatically. You can track the progress in the `jj-abrams` branch and if you want to submit a PR for any of the above mentioned tasks that would be greatly appreciated of course! diff --git a/_posts/2015-08-07-rails-api-auth.md b/_posts/2015-08-07-rails-api-auth.md index 9bdd64e4ac..aaa6e335e6 100755 --- a/_posts/2015-08-07-rails-api-auth.md +++ b/_posts/2015-08-07-rails-api-auth.md @@ -1,13 +1,14 @@ --- -title: "Rails API Auth" -author: "Marco Otte-Witte" +title: 'Rails API Auth' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte announces rails_api_auth, a Rails engine that implements the \"Resource Owner Password Credentials Grant\" OAuth 2.0 flow." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte announces rails_api_auth, a Rails engine that implements the "Resource Owner Password Credentials Grant" OAuth 2.0 flow.' topic: ruby --- -We are happy to announce the first public release of the [rails_api_auth gem](https://github.com/simplabs/rails_api_auth). rails_api_auth is a **lightweight Rails Engine that implements the _"Resource Owner Password Credentials Grant"_ OAuth 2.0 flow** as well as Facebook authentication and is **built for usage in API projects**. If you’re building a client side application with e.g. a browser MVC like [Ember.js](http://emberjs.com) (where you might be using [Ember Simple Auth](https://github.com/simplabs/ember-simple-auth) which works great with rails_api_auth of course), a mobile app or anything else that’s backed by a Rails-based API, rails_api_auth is for you. + +We are happy to announce the first public release of the [rails_api_auth gem](https://github.com/simplabs/rails_api_auth). rails*api_auth is a \*\*lightweight Rails Engine that implements the *"Resource Owner Password Credentials Grant"\_ OAuth 2.0 flow** as well as Facebook authentication and is **built for usage in API projects\*\*. If you’re building a client side application with e.g. a browser MVC like [Ember.js](http://emberjs.com) (where you might be using [Ember Simple Auth](https://github.com/simplabs/ember-simple-auth) which works great with rails_api_auth of course), a mobile app or anything else that’s backed by a Rails-based API, rails_api_auth is for you. diff --git a/_posts/2015-11-27-updating-to-ember-simple-auth-1.0.md b/_posts/2015-11-27-updating-to-ember-simple-auth-1.0.md index 0de7d7feeb..8d78312c89 100755 --- a/_posts/2015-11-27-updating-to-ember-simple-auth-1.0.md +++ b/_posts/2015-11-27-updating-to-ember-simple-auth-1.0.md @@ -1,10 +1,10 @@ --- -title: "Updating to Ember Simple Auth 1.0" -author: "Marco Otte-Witte" +title: 'Updating to Ember Simple Auth 1.0' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte gives an update on the upcoming Ember Simple Auth 1.0 release and shows how to use it in Ember CLI applications." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte gives an update on the upcoming Ember Simple Auth 1.0 release and shows how to use it in Ember CLI applications.' topic: ember --- @@ -50,7 +50,7 @@ With Ember Simple Auth 1.0.0 the **session is no longer automatically injected i import Ember from 'ember'; export default Ember.Controller.extend({ - session: Ember.inject.service() + session: Ember.inject.service(), }); ``` @@ -63,7 +63,7 @@ Ember Simple Auth 1.0.0 does not automatically merge all the predefined authenti import OAuth2PasswordGrant from 'ember-simple-auth/authenticators/oauth2-password-grant'; export default OAuth2PasswordGrant.extend({ - serverTokenRevocationEndpoint: '/revoke' + serverTokenRevocationEndpoint: '/revoke', }); ``` @@ -101,7 +101,7 @@ import DS from 'ember-data'; import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin'; export default DS.JSONAPIAdapter.extend(DataAdapterMixin, { - authorizer: 'authorizer:application' + authorizer: 'authorizer:application', }); ``` @@ -126,10 +126,10 @@ export default SessionService.extend({ const accountId = this.get('data.authenticated.account_id'); if (!Ember.isEmpty(accountId)) { return DS.PromiseObject.create({ - promise: this.get('store').find('account', accountId) + promise: this.get('store').find('account', accountId), }); } - }) + }), }); ``` @@ -149,10 +149,10 @@ export default Ember.Service.extend({ const accountId = this.get('session.data.authenticated.account_id'); if (!Ember.isEmpty(accountId)) { return DS.PromiseObject.create({ - promise: this.get('store').find('account', accountId) + promise: this.get('store').find('account', accountId), }); } - }) + }), }); ``` @@ -169,7 +169,7 @@ Previous versions of Ember Simple Auth allowed the session store to be configure import CookieStore from 'ember-simple-auth/session-stores/cookie'; export default CookieStore.extend({ - cookieName: 'my_custom_cookie_name' + cookieName: 'my_custom_cookie_name', }); ``` diff --git a/_posts/2015-12-3-ember-cli-deploy-notifications.md b/_posts/2015-12-3-ember-cli-deploy-notifications.md index 61832545d0..183225f34c 100755 --- a/_posts/2015-12-3-ember-cli-deploy-notifications.md +++ b/_posts/2015-12-3-ember-cli-deploy-notifications.md @@ -1,10 +1,10 @@ --- -title: "ember-cli-deploy-notifications" -author: "Michael Klein" +title: 'ember-cli-deploy-notifications' +author: 'Michael Klein' twitter: LevelbossMike github: LevelbossMike -bio: "Full-Stack Engineer, Ember CLI Deploy core team member" -description: "Michael Klein introduces ember-cli-deploy-notifications, an ember-cli-deploy plugin for invoking arbitrary webhooks during the deployment process." +bio: 'Full-Stack Engineer, Ember CLI Deploy core team member' +description: 'Michael Klein introduces ember-cli-deploy-notifications, an ember-cli-deploy plugin for invoking arbitrary webhooks during the deployment process.' topic: ember --- @@ -38,15 +38,15 @@ module.exports = function(deployTarget) { services: { bugsnag: { didActivate: { - apiKey: '' - } - } - } - } + apiKey: '', + }, + }, + }, + }, }; return ENV; -} +}; ``` Every time a new revision gets activated now by `ember-cli-deploy`, `ember-cli-deploy-notifications` will sent a `POST` request to the bugsnag service to notify it of the newly activated deployment revision. @@ -80,13 +80,13 @@ module.exports = function(deployTarget) { return { secret: 'supersecret', - deployer: deployer - } + deployer: deployer, + }; }, - didActivate: true - } - } - } + didActivate: true, + }, + }, + }, }; return ENV; @@ -136,17 +136,17 @@ module.exports = function(deployTarget) { slack: { url: '', body: { - text: 'A new revision was deployed!' + text: 'A new revision was deployed!', }, didActivate: true, didFail: { body: { - text: 'Deployment failed!' - } - } - } - } - } + text: 'Deployment failed!', + }, + }, + }, + }, + }, }; return ENV; @@ -170,9 +170,9 @@ module.exports = function(deployTarget) { bugsnag: { apiKey: '', didActivate: true, - } - } - } + }, + }, + }, }; return ENV; diff --git a/_posts/2016-03-04-ember-test-selectors.md b/_posts/2016-03-04-ember-test-selectors.md index c62a3f8319..a5f0aff5a9 100755 --- a/_posts/2016-03-04-ember-test-selectors.md +++ b/_posts/2016-03-04-ember-test-selectors.md @@ -1,20 +1,20 @@ --- title: Using better element selectors in Ember.js tests -author: "Marco Otte-Witte" +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte announces the release of ember-test-selectors, an addon that enables better element selectors in Ember.js tests." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte announces the release of ember-test-selectors, an addon that enables better element selectors in Ember.js tests.' topic: ember --- -We just released [ember-test-selectors](https://github.com/simplabs/ember-test-selectors), an __Ember Addon that enables better element selectors in Ember.js tests__. It removes all data attributes starting with `data-test-` from the application's templates in the `production` environment so that these attributes can be used to select elements with in acceptance and integration tests without polluting the markup that is delivered to the end user. +We just released [ember-test-selectors](https://github.com/simplabs/ember-test-selectors), an **Ember Addon that enables better element selectors in Ember.js tests**. It removes all data attributes starting with `data-test-` from the application's templates in the `production` environment so that these attributes can be used to select elements with in acceptance and integration tests without polluting the markup that is delivered to the end user. ## Why even use `data` attributes as test selectors? -Integration and acceptance tests usually __interact with and assert on the presence of certain elements__ in the markup that an application renders. These elements are identified using CSS selectors. Most projects use one of three approaches for CSS selectors in tests: +Integration and acceptance tests usually **interact with and assert on the presence of certain elements** in the markup that an application renders. These elements are identified using CSS selectors. Most projects use one of three approaches for CSS selectors in tests: ### Selectors based on HTML structure @@ -55,7 +55,7 @@ This approach uses HTML 5 `data` attributes to select elements. For the followin ``` -one would select the post's title with the selector `*[data-test-selector="post-title"]` (which selects any element with a `data-test-selector` attribute that has the value `"post-title"`). While the selector is arguably a bit longer this approach __clearly separates the test selectors from the rest of the markup and is resilient to change__ as the attribute can be applied to any element rendering the post's title, regardless of the HTML structure, CSS classes etc. Also it easily allows encoding more data in the markup like e.g. the post's id: +one would select the post's title with the selector `*[data-test-selector="post-title"]` (which selects any element with a `data-test-selector` attribute that has the value `"post-title"`). While the selector is arguably a bit longer this approach **clearly separates the test selectors from the rest of the markup and is resilient to change** as the attribute can be applied to any element rendering the post's title, regardless of the HTML structure, CSS classes etc. Also it easily allows encoding more data in the markup like e.g. the post's id: ```hbs
@@ -64,7 +64,7 @@ one would select the post's title with the selector `*[data-test-selector="post-
``` -`ember-test-selectors` makes sure to remove all these `data` attributes in the `production` environment so that __users will have perfectly clean HTML delivered__: +`ember-test-selectors` makes sure to remove all these `data` attributes in the `production` environment so that **users will have perfectly clean HTML delivered**: ```html
@@ -77,5 +77,5 @@ one would select the post's title with the selector `*[data-test-selector="post- We have some future plans for ember-test-selectors to make working with data attributes as test selectors even more convenient: -* custom test helpers that find elements by data attributes so that you don't have to write the quite long selectors yourselves; probably sth. like `findViaTestSelector('selector', 'post-title')`, [https://github.com/simplabs/ember-test-selectors/issues/8](https://github.com/simplabs/ember-test-selectors/issues/8) -* template helpers that generate data attributes for elements, e.g. `

{{post.title}}

` which would result in `

{{post.title}}

` or `
` which would result in `
`, [https://github.com/simplabs/ember-test-selectors/issues/9](https://github.com/simplabs/ember-test-selectors/issues/9) +- custom test helpers that find elements by data attributes so that you don't have to write the quite long selectors yourselves; probably sth. like `findViaTestSelector('selector', 'post-title')`, [https://github.com/simplabs/ember-test-selectors/issues/8](https://github.com/simplabs/ember-test-selectors/issues/8) +- template helpers that generate data attributes for elements, e.g. `

{{post.title}}

` which would result in `

{{post.title}}

` or `
` which would result in `
`, [https://github.com/simplabs/ember-test-selectors/issues/9](https://github.com/simplabs/ember-test-selectors/issues/9) diff --git a/_posts/2016-12-06-out-of-the-box-fastboot-support-in-ember-simple-auth.md b/_posts/2016-12-06-out-of-the-box-fastboot-support-in-ember-simple-auth.md index 246f32008e..915774cfa0 100755 --- a/_posts/2016-12-06-out-of-the-box-fastboot-support-in-ember-simple-auth.md +++ b/_posts/2016-12-06-out-of-the-box-fastboot-support-in-ember-simple-auth.md @@ -1,10 +1,10 @@ --- title: Out-of-the-box FastBoot support in Ember Simple Auth -author: "Marco Otte-Witte" +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte announces out-of-the-box support for FastBoot in Ember Simple Auth and explains how it works using ember-cookies." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte announces out-of-the-box support for FastBoot in Ember Simple Auth and explains how it works using ember-cookies.' topic: ember --- @@ -24,6 +24,7 @@ The main challenge with enabling the cookie session store to run both in the bro ember-cookies exposes a cookies service with `read`, `write` and `clear` methods: + ```js // app/controllers/application.js import Ember from 'ember'; @@ -37,7 +38,7 @@ export default Ember.Controller.extend({ beforeModel() { let locale = this.get('cookies').read('locale'); this.get('intl').set('locale', locale); - } + }, }); ``` @@ -58,14 +59,14 @@ If you're interested in learning more about the underlying concepts check out th Out-of-the-box support for FastBoot is likely the most prominent addition in this release but there are quite some other changes as well, including: -* The default cookie names that the cookie session store uses are now compliant with RFC 2616, see #978 -* Server responses are now validated in authenticators, preventing successful logins when required data is actually missing, see #957 -* The OAuth 2.0 Password Grant authenticator can now send custom headers along with authentication requests, see #1018 +- The default cookie names that the cookie session store uses are now compliant with RFC 2616, see #978 +- Server responses are now validated in authenticators, preventing successful logins when required data is actually missing, see #957 +- The OAuth 2.0 Password Grant authenticator can now send custom headers along with authentication requests, see #1018 In addition to these changes, fixed issues include: -* Routes like the login route can now be configured inline in routes using the respective mixins as opposed to `config/environment.js`, see #1041 -* The cookie session store will now rewrite its cookies when any of its configurable properties (like cookie name) change, see #1056 +- Routes like the login route can now be configured inline in routes using the respective mixins as opposed to `config/environment.js`, see #1041 +- The cookie session store will now rewrite its cookies when any of its configurable properties (like cookie name) change, see #1056 ## A Community Effort diff --git a/_posts/2017-01-13-ember-test-selectors.md b/_posts/2017-01-13-ember-test-selectors.md index 8f37b5549c..48b66ea962 100755 --- a/_posts/2017-01-13-ember-test-selectors.md +++ b/_posts/2017-01-13-ember-test-selectors.md @@ -1,10 +1,10 @@ --- title: New features for ember-test-selectors -author: "Tobias Bieniek" +author: 'Tobias Bieniek' github: Turbo87 twitter: tobiasbieniek -bio: "Senior Frontend Engineer, Ember CLI core team member" -description: "Tobias Bieniek announces new features in ember-test-selectors such as automatic binding of data-test-* properties and how these are stripped in production." +bio: 'Senior Frontend Engineer, Ember CLI core team member' +description: 'Tobias Bieniek announces new features in ember-test-selectors such as automatic binding of data-test-* properties and how these are stripped in production.' topic: ember --- @@ -17,7 +17,7 @@ gained a lot of new functionality and should be considered our release candidate for `1.0.0`. This blog post will highlight the major changes in this release, and will -give you a short introduction into *how* we have implemented these new +give you a short introduction into _how_ we have implemented these new features. @@ -104,7 +104,7 @@ module.exports = { }; ``` -__UPDATE:__ After releasing `0.1.0` we were notified that this feature was +**UPDATE:** After releasing `0.1.0` we were notified that this feature was not working for component integration tests, which was actually a pretty obvious problem as initializers are not running for these kinds of tests. After thinking about the issue for a few hours we came up with a @@ -115,7 +115,6 @@ solution that seems to work even better now. Instead of calling We have released `0.1.1` including this change and are now also warning you if you try to use `data-test-*` attributes on tagless components. - ## Stripping out `data-test-*` attributes in templates Our initial goal with this library was stripping our `data-test-*` attributes @@ -130,6 +129,7 @@ registering a Handlebars AST plugin in the [`setupPreprocessorRegistry()`](https://ember-cli.com/api/classes/Addon.html#method_setupPreprocessorRegistry) hook of the addon: + ```js module.exports = { // ... @@ -189,7 +189,6 @@ If you try the same example template in the transform code, you will notice that the `data-test-comment-id=comment.id` part of the `some-component` invocation is now gone. - ## Stripping out `data-test-*` properties in JS files While one way of assigning data attributes to a component is in the template, @@ -265,7 +264,6 @@ module.exports = { }; ``` - ## Testing in `production` mode In our previous releases we had offered an `environments` option to let you @@ -295,15 +293,14 @@ described above: ```js var app = new EmberApp({ 'ember-test-selectors': { - strip: false - } + strip: false, + }, }); ``` Note that using the `environments` option still works, but is deprecated and will be removed by the time we release `1.0.0`. - ## Simplified `testSelector()` import The `testSelector()` helper function can be used to simplify building the diff --git a/_posts/2017-02-01-class-based-computed-properties.md b/_posts/2017-02-01-class-based-computed-properties.md index 63e256168e..a67e95280e 100755 --- a/_posts/2017-02-01-class-based-computed-properties.md +++ b/_posts/2017-02-01-class-based-computed-properties.md @@ -1,32 +1,32 @@ --- title: Class based Computed Properties -author: "Marco Otte-Witte" +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte introduces a mechanism for class based computed properties in Ember.js and how those can be used instead of helpers." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte introduces a mechanism for class based computed properties in Ember.js and how those can be used instead of helpers.' topic: ember --- -We think Computed Properties in Ember are awesome. We also think they [are in many cases the better alternative to template helpers](https://speakerdeck.com/marcoow/templates-and-logic-in-ember) as they allow for cleaner separation of where a computation is triggered and the implementation of that computation. In some cases though it is currently very hard to do things in Computed Properties (and Computed Property macros in particular) that are possible with Class based helpers. With the __introduction of Class based Computed Properties__ we're aiming at making these scenarios solvable easily. +We think Computed Properties in Ember are awesome. We also think they [are in many cases the better alternative to template helpers](https://speakerdeck.com/marcoow/templates-and-logic-in-ember) as they allow for cleaner separation of where a computation is triggered and the implementation of that computation. In some cases though it is currently very hard to do things in Computed Properties (and Computed Property macros in particular) that are possible with Class based helpers. With the **introduction of Class based Computed Properties** we're aiming at making these scenarios solvable easily. ## Computed Properties and Computed Property Macros -Computed Properties are among the first things developers new to Ember learn. They are a great way of defining dependencies between data points in the application and __ensuring the UI stays consistent as these data points change__. +Computed Properties are among the first things developers new to Ember learn. They are a great way of defining dependencies between data points in the application and **ensuring the UI stays consistent as these data points change**. Ember comes with a set of macros that implement property logic that most applications need and allow for short and expressive definitions like ```js -isActive: Ember.computed.equal('state', 'isActive') +isActive: Ember.computed.equal('state', 'isActive'); ``` There are addons that provide even more macros for common use cases like [ember-cpm](https://github.com/cibernox/ember-cpm) or [ember-awesome-macros](https://github.com/kellyselden/ember-awesome-macros). ## Where Computed Property Macros fall short today -__Computed Properties are very similar to template helpers__ in the way that both are [pure functions](https://en.wikipedia.org/wiki/Pure_function) that can only depend on their inputs. While a template helpers receives its inputs as arguments, __Computed Properties define their inputs as dependent keys__. +**Computed Properties are very similar to template helpers** in the way that both are [pure functions](https://en.wikipedia.org/wiki/Pure_function) that can only depend on their inputs. While a template helpers receives its inputs as arguments, **Computed Properties define their inputs as dependent keys**. In some cases pure functions are not sufficient though as the computation in the template helper or computed property also depends on global state or the inputs cannot statically be listed in the helper or property definition. This is the case for example for computations on collections when it is unknown upfront on which property of each element in the collection the computation depends, e.g. @@ -46,11 +46,11 @@ With template helpers and the [ember-composable-helpers](https://github.com/Dock and because the [`filter-by` helper is a Class based helper](https://github.com/DockYard/ember-composable-helpers/blob/master/addon/helpers/filter-by.js) this actually works and the DOM updates correctly whenever the value of the `filter` property or e.g. the `isActive` property of any user changes. -With Computed Properties __it is not currently possible to implement something like this__ (at least not as a reusable macro). +With Computed Properties **it is not currently possible to implement something like this** (at least not as a reusable macro). ## Enter Class based Computed Properties -With the Class based Computed Properties that [ember-classy-computed](https://github.com/simplabs/ember-classy-computed) introduces it is __actually possible now to implement something like the above mentioned `filterByProperty` macro__. The computed property returned by that macro can now correctly be invalidated when any of the user's `isActive`, `isBlocked` etc. properties change although it is not actually possible to know what these properties might be upfront. This __allows keeping the filtering logic in JavaScript as opposed to in the template__ when using a Class based template helper: +With the Class based Computed Properties that [ember-classy-computed](https://github.com/simplabs/ember-classy-computed) introduces it is **actually possible now to implement something like the above mentioned `filterByProperty` macro**. The computed property returned by that macro can now correctly be invalidated when any of the user's `isActive`, `isBlocked` etc. properties change although it is not actually possible to know what these properties might be upfront. This **allows keeping the filtering logic in JavaScript as opposed to in the template** when using a Class based template helper: ```js import filterByProperty from 'app/computeds/filter-by'; @@ -68,6 +68,7 @@ filteredUsers: filterByProperty('users' 'filter') The implementation for the Computed Property macro looks like this: + ```js // app/computeds/filter-by.js import Ember from 'ember'; @@ -85,7 +86,7 @@ const DynamicFilterByComputed = ClassBasedComputedProperty.extend({ filterPropertyDidChange: observer('filterProperty', function() { let filterProperty = this.get('filterProperty'); - let property = filter(`collection.@each.${filterProperty}`, (item) => item.get(filterProperty)); + let property = filter(`collection.@each.${filterProperty}`, item => item.get(filterProperty)); defineProperty(this, 'content', property); }), @@ -98,14 +99,14 @@ const DynamicFilterByComputed = ClassBasedComputedProperty.extend({ this.set('filterProperty', filterProperty); return this.get('content'); - } + }, }); export default ClassBasedComputedProperty.property(DynamicFilterByComputed); ``` -Comparing this code to the implementation of the [`filter-by` helper](https://github.com/DockYard/ember-composable-helpers/blob/master/addon/helpers/filter-by.js) mentioned above you will recognize that both are almost identical. This illustrates very well what Class based Computed Properties are: a way to __use the same mechanisms that are already established for Class based template helpers for Computed Properties__ as well. +Comparing this code to the implementation of the [`filter-by` helper](https://github.com/DockYard/ember-composable-helpers/blob/master/addon/helpers/filter-by.js) mentioned above you will recognize that both are almost identical. This illustrates very well what Class based Computed Properties are: a way to **use the same mechanisms that are already established for Class based template helpers for Computed Properties** as well. ## Notice -__[ember-classy-computed](https://github.com/simplabs/ember-classy-computed) is currently at a very early stage__ and we haven't thoroughly tested the implementation just yet. We have also not done any benchmarking to get a better understanding of what the performance implications are. That is to say, __while we encourage everyone to try this out, be aware you're currently doing so at your own risk__ as this is most likely not production ready (yet). We have the feeling though that this will be a valuable addition to Computed Properties in the future and can close the gap that currently exists between Computed Properties and template helpers. +**[ember-classy-computed](https://github.com/simplabs/ember-classy-computed) is currently at a very early stage** and we haven't thoroughly tested the implementation just yet. We have also not done any benchmarking to get a better understanding of what the performance implications are. That is to say, **while we encourage everyone to try this out, be aware you're currently doing so at your own risk** as this is most likely not production ready (yet). We have the feeling though that this will be a valuable addition to Computed Properties in the future and can close the gap that currently exists between Computed Properties and template helpers. diff --git a/_posts/2017-02-13-npm-libs-in-ember-cli.md b/_posts/2017-02-13-npm-libs-in-ember-cli.md index 1522725b29..f4d755ed92 100755 --- a/_posts/2017-02-13-npm-libs-in-ember-cli.md +++ b/_posts/2017-02-13-npm-libs-in-ember-cli.md @@ -1,10 +1,10 @@ --- title: Using npm libraries in Ember CLI -author: "Tobias Bieniek" +author: 'Tobias Bieniek' github: Turbo87 twitter: tobiasbieniek -bio: "Senior Frontend Engineer, Ember CLI core team member" -description: "Tobias Bieniek introduces a mechanism for using arbitrary npm libraries in Ember CLI applications and explains how that works under the hood." +bio: 'Senior Frontend Engineer, Ember CLI core team member' +description: 'Tobias Bieniek introduces a mechanism for using arbitrary npm libraries in Ember CLI applications and explains how that works under the hood.' topic: ember og: image: /assets/images/posts/2017-02-13-npm-libs-in-ember-cli/og-image.png @@ -13,7 +13,7 @@ og: tl;dr Use npm instead of Bower whenever you can! With Ember 2.11 we are now using the [`ember-source`][ember-source] module and -no longer the `ember` [Bower][Bower] package. In the upcoming Ember CLI 2.12 +no longer the `ember` [Bower][bower] package. In the upcoming Ember CLI 2.12 release, Bower will also no longer be installed by default and only install lazily when an addon requests it. All this indicates that from now on we should try to use npm packages instead of Bower whenever possible. This blog post @@ -25,7 +25,7 @@ will explain how we can do that and what options are available to us. The way to import Bower libraries into your app consists of three major steps. First we will have to install the library. We will use the popular -[Moment.js][Moment.js] library as an example here: +[Moment.js][moment.js] library as an example here: ``` bower install --save moment @@ -58,7 +58,7 @@ like this: function vendorModule() { 'use strict'; - return { 'default': self['moment'] }; + return { default: self['moment'] }; } define('moment', [], vendorModule); @@ -69,7 +69,7 @@ This looks a little cryptic at first, but once you understand the individual parts it starts to make sense. The files that the Ember CLI build pipeline generates use the -[Asynchronous Module Definition][AMD] (or shorter: AMD). The `define()` call in +[Asynchronous Module Definition][amd] (or shorter: AMD). The `define()` call in the code above defines a new AMD module with the name `moment` and the return value of the `vendorModule` function describes what the module exports, or what we can import from that module in our own code. In this case we export @@ -86,7 +86,6 @@ app.import('vendor/shims/moment.js'); We are now able to use `import moment from 'moment';` in our Ember code. - ## App vs. Addon The above instructions work great for apps, but what about addons? The @@ -161,7 +160,6 @@ available only inside of the addon. Since the `addBowerPackageToProject()` method returns a `Promise` we have to return it from the `afterInstall` hook to make sure that Ember CLI waits for the installation to finish. - ## npm vs. Bower

"What's bower?"
"A package manager, install it with npm."
"What's npm?"
"A package manager, you can install it with brew"
"What's brew?"
...

— Stefan Baumgartner (@ddprrt) 5. November 2014
@@ -256,7 +254,6 @@ talk: [Dissecting an Ember CLI Build](https://youtu.be/hNwgp9alwKg). Now that we've imported the `moment.js` file into our `vendor` tree we can finally `import()` it in the `included()` hook and everything works again. - ## npm dependencies As we are using npm now we can also take advantage of the way that npm @@ -298,7 +295,6 @@ addon and have a look at their `index.js` file. They do support fastboot too though which makes the code a little more complicated compared to our simplified example here. - ## App vs. Addon again Now that we have converted our `ember-moment` addon to use npm instead of @@ -315,8 +311,7 @@ ember generate in-repo-addon ember-moment and use the same code as above inside the `lib/ember-moment/index.js` file. - -## Next: CommonJS and ES6 modules +## Next: CommonJS and ES6 modules Things get a little more complicated when you want to use npm packages that are not distributed in a prebuilt form like Moment.js. If they instead export @@ -324,11 +319,10 @@ only CommonJS modules or ES6 modules we will need to add a few more plugins to the build pipeline to make this work and we will explain how to do that in a future blog post. - [ember-source]: https://www.npmjs.com/package/ember-source -[Bower]: https://bower.io/ -[Moment.js]: https://momentjs.com/ -[AMD]: https://github.com/amdjs/amdjs-api/blob/master/AMD.md +[bower]: https://bower.io/ +[moment.js]: https://momentjs.com/ +[amd]: https://github.com/amdjs/amdjs-api/blob/master/AMD.md [included-hook]: https://ember-cli.com/api/classes/Addon.html#method_included diff --git a/_posts/2017-03-21-on-computed-properties-vs-helpers.md b/_posts/2017-03-21-on-computed-properties-vs-helpers.md index 0950d587b1..974c06b1f0 100755 --- a/_posts/2017-03-21-on-computed-properties-vs-helpers.md +++ b/_posts/2017-03-21-on-computed-properties-vs-helpers.md @@ -1,10 +1,10 @@ --- title: On Computed Properties vs. Helpers -author: "Marco Otte-Witte" +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte discusses differences between computed properties and helpers and explains pros and cons of each alternative." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte discusses differences between computed properties and helpers and explains pros and cons of each alternative.' topic: ember og: image: /assets/images/posts/2017-03-21-on-computed-properties-vs-helpers/og-image.png @@ -33,7 +33,7 @@ property is at least `65`: ```js isSenior: computed('age', function() { return this.get('age') >= 65; -}) +}); ``` The `age` property is listed as a dependency of the `isSenior` property, thus @@ -56,8 +56,8 @@ defined on the `user` here): ## Template Helpers -The above example __could just as easily be implemented using a template -helper__. Instead of defining the `isSenior` property with its dependents +The above example **could just as easily be implemented using a template +helper**. Instead of defining the `isSenior` property with its dependents upfront, one could define an `lte` helper like this: ```js @@ -82,10 +82,10 @@ and move the comparison into the template: Both alternatives are obviously very similar - both compare the user's `age` property with a comparison value and depending on the outcome of that comparison render some nodes to the DOM (or don't render them). The main -difference is that __with the computed property that comparison lives in the -template context__ and is only invoked from the template (via the `isSenior` -identifier) while __when using a helper the comparison is defined inline right -in the template__ itself. +difference is that **with the computed property that comparison lives in the +template context** and is only invoked from the template (via the `isSenior` +identifier) while **when using a helper the comparison is defined inline right +in the template** itself. This might not seem like a big thing but there actually are some differences between the two alternatives that have consequences on certain aspects of the @@ -94,14 +94,14 @@ application. ### Separation vs. Unification The main difference is that with the computed property solution, the logic and -its internals are separated from the template. The __logic lives in the -template context and appears as a black box to the template__ which merely uses -some named properties - this is actually quite __similar to a class exposing -named methods__ where the caller of these methods does not have to worry about +its internals are separated from the template. The **logic lives in the +template context and appears as a black box to the template** which merely uses +some named properties - this is actually quite **similar to a class exposing +named methods** where the caller of these methods does not have to worry about their internals as long as it's clear what they are supposed to be used for. -With the __template helpers solution though both the rendering as well as the -logic are merged in the template__. That means that understanding and +With the **template helpers solution though both the rendering as well as the +logic are merged in the template**. That means that understanding and maintaining the template includes understanding all of the logic encoded in the helper invocations. @@ -115,7 +115,7 @@ property returning such a user could look like this: userToDisplay: computed('users.@each.state', function() { let activeUsers = this.get('users').filterBy('state', 'active'); return shuffle(activeUsers)[0]; -}) +}); ``` Rendering that user's `name` then is as easy as using the `userToDisplay` @@ -141,21 +141,21 @@ property. ### Testability -Another consequence of __moving logic into the template is that the logic -itself becomes harder to test__. While logic that lives in the template context -in the form of computed properties is easily unit-testable, __logic that lives -in the template can only be tested by actually rendering the template__ which +Another consequence of **moving logic into the template is that the logic +itself becomes harder to test**. While logic that lives in the template context +in the form of computed properties is easily unit-testable, **logic that lives +in the template can only be tested by actually rendering the template** which again leads to a few other consequences: -* Tests become slower to execute as it's obviously slower to render than not to +- Tests become slower to execute as it's obviously slower to render than not to render. -* Each test case becomes more complex. While unit tests can assert on the value +- Each test case becomes more complex. While unit tests can assert on the value of the computed property (e.g. `asser.equal(user.get('isSenior') true)`), (integration) tests for logic inside of templates have to assert on the presence or absence of DOM nodes (e.g. `assert.ok(find(testSelector('senior-flag')))`) which obviously is a much more indirect way of testing the same thing. -* Test cases (potentially) require more and unrelated context to be set up. +- Test cases (potentially) require more and unrelated context to be set up. While unit tests for computed properties only require the dependent properties of the computed property under test to be set up, rendering a template requires the full context for that template to be set up which might @@ -165,13 +165,13 @@ again leads to a few other consequences: One of the main reasons that we hear why people prefer template helpers over computed properties is the fact that computed properties need to list their -dependent keys. __Especially newcomers to Ember.js are afraid of forgetting a -key or making a mistake__ (especially when it comes to collections) which can +dependent keys. **Especially newcomers to Ember.js are afraid of forgetting a +key or making a mistake** (especially when it comes to collections) which can lead to hard to track down errors (see below for a potential fix for the need -to specify dependent keys at all). __Template helpers instead don't need to -list dependencies and are automatically re-executed__ when any of their +to specify dependent keys at all). **Template helpers instead don't need to +list dependencies and are automatically re-executed** when any of their arguments change. While that is true in most cases there are some edge cases -where __helpers might actually not work as expected__. +where **helpers might actually not work as expected**. Consider a slightly modified version of the above example that compares the user's `age` property with a comparison value: @@ -193,19 +193,19 @@ export default Ember.Helper.helper(function([user]) { The problem with this solution is that the DOM does not get updated when the user's `age` property changes. While arguments that are passed to helpers are -observed by Ember automatically and the __helper will be re-executed when any +observed by Ember automatically and the **helper will be re-executed when any of these arguments change to a different value, the same is not true when any -property on any of the arguments changes__. In this case changes of the user's +property on any of the arguments changes**. In this case changes of the user's `age` property would go unnoticed and the DOM would get out of sync with the underlying data. ### Performance -In general, __performance should be more or less on par for computed properties -and template helpers__ in comparable scenarios. There's one slight difference +In general, **performance should be more or less on par for computed properties +and template helpers** in comparable scenarios. There's one slight difference though in the way that helpers and computed properties work that can have -relevant performance implications though: __all arguments passed to helpers are -eagerly evaluated before the helper function is invoked__. This is a major +relevant performance implications though: **all arguments passed to helpers are +eagerly evaluated before the helper function is invoked**. This is a major difference compared to computed properties actually. Consider this example: @@ -223,7 +223,7 @@ property makes the difference clear: ```js areBothTruthy: computed('propA', 'propB', function() { return this.get('propA') && this.get('propB'); -}) +}); ``` As the `&&` operator will actually short-circuit execution when @@ -237,8 +237,8 @@ helpers. As shown above there are many cases in which either computed properties or template helpers can be used to implement the same functionality. There are -__some drawbacks though that template helpers come with while not actually -providing any additional value__ in these scenarios. +**some drawbacks though that template helpers come with while not actually +providing any additional value** in these scenarios. We at simplabs prefer computed properties over template helpers in all of our projects and are putting some effort in making computed properties better. @@ -250,7 +250,7 @@ which would obviously remove a lot of complexity and eliminate people's fear to accidentally omit a dependency or specify a wrong one (besides that there could even be performance improvements as there could be less invalidations). -There is also a __bunch of great computed property macro addons__ like +There is also a **bunch of great computed property macro addons** like [ember-cpm](https://github.com/cibernox/ember-cpm) by [Miguel Camba](https://github.com/cibernox) and [ember-awesome-macros](https://github.com/kellyselden/ember-awesome-macros) and @@ -263,16 +263,16 @@ out ### Pure Functions One thing I hear a lot is that template helpers are better than computed -properties because helpers are (supposed to be) pure functions. While __pure -functions are great__ and helpers are (unless you make a mistake) pure -functions, __computed properties are actually pure functions as well__ so this +properties because helpers are (supposed to be) pure functions. While **pure +functions are great** and helpers are (unless you make a mistake) pure +functions, **computed properties are actually pure functions as well** so this is not something that speaks for either of the alternatives. For some context: a pure function is a function that only depends on its inputs and will return the same value for the same inputs each time it is called. A pure function also has no side effects. While this is true for helpers it is -obviously true for computed properties with the only distinction that __a -computed property's inputs are called its dependent keys__. Of course there's +obviously true for computed properties with the only distinction that **a +computed property's inputs are called its dependent keys**. Of course there's also class based helpers whose whole purpose is to enable template helpers that can depend on global state and thus are not pure functions (and the same concept exists for computed properties with ember-classy-computed now). @@ -281,8 +281,8 @@ concept exists for computed properties with ember-classy-computed now). The intention of this post is not to say template helpers are bad and computed properties should be used for everything. That would be a pretty ignorant -statement to make. __There are original concerns of the template layer that are -best implemented in helpers__ - typical examples being translating strings, +statement to make. **There are original concerns of the template layer that are +best implemented in helpers** - typical examples being translating strings, formatting numbers or dates etc. We use template helpers almost exclusively for this kind of functionality which is usually only a small part part of any application though - we often only have a few helpers defined even in pretty @@ -292,8 +292,8 @@ big and involved applications. There is one slightly more evolved scenario where computed properties cannot easily be used instead of template helpers to achieve the same functionality. -That is when there is __no context the computed property could be defined on or -the context it would be needed on is unrelated to the template context__. One +That is when there is **no context the computed property could be defined on or +the context it would be needed on is unrelated to the template context**. One example for this is a component that renders a list of items and keeps track of which of these items is currently selected. The easiest way to do something like that using template helpers would look something like this: @@ -338,16 +338,16 @@ collection then: ``` -This pattern - __defining a wrapper context that combines two or more other -contexts__ - should work in similar scenarios as well. +This pattern - **defining a wrapper context that combines two or more other +contexts** - should work in similar scenarios as well. ## Conclusion Computed properties and template helpers are both valuable parts of the Ember -framework and __both have their use cases__. Being aware of which one is best +framework and **both have their use cases**. Being aware of which one is best suited for which use case and what the consequences and drawbacks are when -picking either one is crucial for __preventing long term maintainability -issues__. +picking either one is crucial for **preventing long term maintainability +issues**. Computed properties are powerful already and will continue to improve with tools like [ember-cpm](https://github.com/cibernox/ember-cpm), diff --git a/_posts/2017-08-28-creating-web-components-with-glimmer.md b/_posts/2017-08-28-creating-web-components-with-glimmer.md index be3922faba..cdea36f390 100755 --- a/_posts/2017-08-28-creating-web-components-with-glimmer.md +++ b/_posts/2017-08-28-creating-web-components-with-glimmer.md @@ -1,10 +1,10 @@ --- title: Creating Web Components with Glimmer -author: "Jessica Jordan" +author: 'Jessica Jordan' github: jessica-jordan twitter: jjordan_dev -bio: "Senior Frontend Engineer, Ember Learning core team member" -description: "Jessica Jordan explains what web components (aka custom elements) are and shows how they can be built using Glimmer.js." +bio: 'Senior Frontend Engineer, Ember Learning core team member' +description: 'Jessica Jordan explains what web components (aka custom elements) are and shows how they can be built using Glimmer.js.' topic: ember --- @@ -19,7 +19,7 @@ In addition to building standalone Glimmer applications, the library allows the The **Custom Elements spec** describes a technology which is - alongside of HTML templates, Shadow DOM and HTML imports - an integral part of the [current Web Component specification](https://www.w3.org/wiki/WebComponents/). The Custom Elements spec describes capabilities for creating custom HTML elements which can be used just like native HTML elements: ``. -The [current version of this specification](https://www.w3.org/TR/custom-elements/) defines a `CustomElementRegistry` interface global which is available as the `customElements` global in the browser. Custom elements are based on extensions of the native `HTMLElement` base class: +The [current version of this specification](https://www.w3.org/TR/custom-elements/) defines a `CustomElementRegistry` interface global which is available as the `customElements` global in the browser. Custom elements are based on extensions of the native `HTMLElement` base class: ```js class CustomElementClass extends HTMLElement { @@ -63,7 +63,6 @@ will bring in all the dependencies needed for its core functionality and will be Third, using web components is very easy as only **knowledge of the HTML markup language is required** to do so. Using the basic and likely most well-known language of the web alone, it **empowers an even larger community of developers** to build their websites and web apps with and upon web components. - Let’s now dive into how we can create our own custom elements using Glimmer. ## Glimmer Web Component Example: A Reusable Open Street Map @@ -74,7 +73,6 @@ In the following we will **create a simple street map** based on [Leaflet.js](ht ![Screenshot of Final Glimmer Map Component Example - Open Street Map View Centered on Munich](/assets/images/posts/2017-08-30-creating-web-components-with-glimmer/glimmer-map-screenshot-final.jpg) - You can also check out the project on [Github](https://github.com/jessica-jordan/glimmer-map). ### Starting a new Glimmer Web Component Project @@ -84,14 +82,14 @@ To get started with an initial, running project setup, we will be using [Ember C ```bash ember new glimmer-map -b @glimmer/blueprint --web-component ``` + generating the needed scaffolding for our first Glimmer app. As soon as the Glimmer app is booted up via a simple `ember serve` and we navigate to the typical `http://localhost:4200`, we will find that our first component project is already rendered with a "Welcome to Glimmer!" headline: ![Screenshot of First Page of a New Glimmer App - Headline: Hello Glimmer!](/assets/images/posts/2017-08-30-creating-web-components-with-glimmer/glimmer-map-startup.png) Let's have a look at our project's file structure as well; following the new folder structure planned out in the [module unification RFC](https://github.com/emberjs/rfcs/pull/143) we can already find our `component.ts` and `template.hbs` files for creating our component co-located in the same directory below the `src` directory: - -```html +``` src ├── index.ts ├── main.ts @@ -130,8 +128,8 @@ function initializeCustomElement(app: Application, name: string): void { // ... assignAttributes(customElement, glimmerElement); }); - } - } + }, + }, }); window.customElements.define(name, GlimmerElement); @@ -151,10 +149,9 @@ The class used for generating our `glimmer-map` component is now defined in `src // src/ui/components/glimmer-map/component.ts import Component from '@glimmer/component'; -export default class GlimmerMap extends Component { - -} +export default class GlimmerMap extends Component {} ``` + As we would like to create a map based on `Leaflet.js`, let's also get that dependency installed in our project: ```bash @@ -181,11 +178,8 @@ const commonjs = require('rollup-plugin-commonjs'); module.exports = function(defaults) { let app = new GlimmerApp(defaults, { rollup: { - plugins: [ - resolve({ jsnext: true, module: true, main: true }), - commonjs() - ] - } + plugins: [resolve({ jsnext: true, module: true, main: true }), commonjs()], + }, }); return app.toTree(); @@ -199,9 +193,7 @@ This finally allows us to import our `leaflet` dependency like so: import Component from '@glimmer/component'; import L from 'leaflet'; -export default class GlimmerMap extends Component { - -} +export default class GlimmerMap extends Component {} ``` Now for creating and rendering the map, let's use the component's `didInsertElement` lifecycle hook to ensure that the Glimmer component has been inserted into the DOM already before the map instance is created and rendered: @@ -247,19 +239,20 @@ With this setup, our map already renders for the map points set intially: ![Screenshot of Open Street Map View of First Pass on the Glimmer Map Component](/assets/images/posts/2017-08-30-creating-web-components-with-glimmer/glimmer-map-screenshot.jpg) -We also aim to make our map respond to user input. Specifically, we would like to be able to set the marker to different locations on the map using a property for the longitude (`lon`) and the latitude (`lat`) easily. To allow any property changes to be reflected in the component's DOM, we can make use of the `@tracked` decorator: +We also aim to make our map respond to user input. Specifically, we would like to be able to set the marker to different locations on the map using a property for the longitude (`lon`) and the latitude (`lat`) easily. To allow any property changes to be reflected in the component's DOM, we can make use of the `@tracked` decorator: ```ts // src/ui/components/glimmer-map/component.ts -export default class GlimmerMap extends Component { +export default class GlimmerMap extends Component { @tracked - lon: number = 11.6020; + lon: number = 11.602; @tracked lat: number = 48.1351; - // ... + // ... } ``` + Anytime there is a change to one of the tracked properties' values, a re-render of the component with a newly updated DOM will follow. And promote the changes to these properties via actions by updating our template @@ -272,14 +265,13 @@ And promote the changes to these properties via actions by updating our template ``` - and the respective `component.ts` file: ```ts // src/ui/components/glimmer-map/component.ts export default class GlimmerMap extends Component { @tracked - lon: number = 11.6020; + lon: number = 11.602; @tracked lat: number = 48.1351; @@ -295,7 +287,6 @@ export default class GlimmerMap extends Component { With this we are already done and can get started with distributing our component. - ## Reusing Glimmer components as custom elements As mentioned in the beginning of this article, the current blueprint for Glimmer web components comes with a wrapper for being able to package and reuse our Glimmer components as custom elements. To **create the assets** needed for reusage, let’s build those like we are already used to when building Ember apps: @@ -312,9 +303,12 @@ This will store all the assets needed for reusing our custom element in the `/di - + crossorigin="" + /> @@ -332,7 +326,6 @@ And finally, we can see our Glimmer-based street map being rendered just as seen - ## And the Glimmer Component story is not over yet Many more interesting developments are upcoming around Glimmer and the [glimmer-component](https://github.com/glimmerjs/glimmer-component) and [glimmer-web-component](https://github.com/glimmerjs/glimmer-web-component) modules specifically. For further reading, please check out another great discussion about the implementation of upcoming APIs according to the web component specification on [here](https://github.com/glimmerjs/glimmer-web-component/issues/19), check out [my talk on current state of web components and Glimmer’s place in it](https://www.youtube.com/watch?v=OzFgDBJcWuU) and stay tuned for our future blog post on testing Glimmer components. diff --git a/_posts/2017-09-17-magic-test-data.md b/_posts/2017-09-17-magic-test-data.md index 4253226ab2..14625c81ce 100755 --- a/_posts/2017-09-17-magic-test-data.md +++ b/_posts/2017-09-17-magic-test-data.md @@ -1,14 +1,14 @@ --- title: Magic Data in Tests -author: "Andy Brown" +author: 'Andy Brown' github: geekygrappler twitter: geekygrappler -bio: "Senior Frontend Engineer" -description: "Andy Brown explains the AAA principle for writing good tests and discusses what the negative consequences of not adhering to it are." +bio: 'Senior Frontend Engineer' +description: 'Andy Brown explains the AAA principle for writing good tests and discusses what the negative consequences of not adhering to it are.' topic: misc --- -Often when working on large codebases, my changes break some existing tests. While I would prefer my coding to be perfect, it's highly unlikely that I'll ever achieve the state of coding zen, so it's nice to know I have a test suite to catch me when I fall. Given that the codebase is large and in the majority not written by me, I tend to be introduced to code via the test files. One important principle I've started to follow when writing and refactoring tests is AAA. +Often when working on large codebases, my changes break some existing tests. While I would prefer my coding to be perfect, it's highly unlikely that I'll ever achieve the state of coding zen, so it's nice to know I have a test suite to catch me when I fall. Given that the codebase is large and in the majority not written by me, I tend to be introduced to code via the test files. One important principle I've started to follow when writing and refactoring tests is AAA. @@ -48,16 +48,17 @@ export default [ country: 'DE', locale: 'de', region: 'europe', - name: 'Germany' + name: 'Germany', }, { country: 'GB', locale: 'en', region: 'europe', - name: 'United Kingdom' - } + name: 'United Kingdom', + }, ]; ``` + We need to write a function that adds a url pointing to an image of the country flag so that the component can display that flag image. So we write a component, here we're using Ember, but the principle is similar for any JS framework or vanilla JS. @@ -68,7 +69,7 @@ import Component from '@ember/component'; /* the countries!! */ import COUNTRIES from 'config/countries'; -export default Component.extend({ +export default Component.extend({ displayCountries: function() { return COUNTRIES.map(country => Object.assign({}, country, { flag: `/assets/images/flags/${country.country}.png` })); }) @@ -106,28 +107,28 @@ export default [ country: 'BG', locale: 'bg', region: 'europe', - name: 'Bulgaria' + name: 'Bulgaria', }, { country: 'DE', locale: 'de', region: 'europe', - name: 'Germany' + name: 'Germany', }, { country: 'GB', locale: 'en', region: 'europe', - name: 'United Kingdom' + name: 'United Kingdom', }, ]; ``` The test will now fail without us having changed the code. No code change, no behaviour change, but failing tests. The definition of a brittle test. The test relies on magic data from an external file, namely `COUNTRIES`. It may only take the original writer of the test minutes to figure out why the test fails, but it might take any one new to the code unit a bit longer to figure out why. -What you should do is *Arrange* the data. When you do this it becomes clear that the function is simply adding a key, and it won't break due to external data changes. All you care about is that given a starting set of data, after applying your function you get the resultant set of data, as clearly defined in the test. +What you should do is _Arrange_ the data. When you do this it becomes clear that the function is simply adding a key, and it won't break due to external data changes. All you care about is that given a starting set of data, after applying your function you get the resultant set of data, as clearly defined in the test. -First we need to stop using a constant directly in our component. This way we can override the `countries` property in the test and *arrange* the data. +First we need to stop using a constant directly in our component. This way we can override the `countries` property in the test and _arrange_ the data. ```js import Component from '@ember/component'; @@ -137,7 +138,7 @@ import COUNTRIES from 'config/countries'; export default Component.extend({ countries: COUNTRIES, - + displayCountries: function() { let countries = this.get('countries'); // This is equivalent to `this.countries` but for Ember objects. return countries.map(country => Object.assign({}, country, { flag: `/assets/images/flags/${country.country}.png` })); @@ -154,8 +155,8 @@ test('displayCountries will add a flag key to a country object', function(assert country: 'SK', locale: 'we', region: 'westeros', - name: 'Seven Kingdoms' - } + name: 'Seven Kingdoms', + }, ], }); @@ -165,12 +166,12 @@ test('displayCountries will add a flag key to a country object', function(assert /* (Write out my expectation for aesthetics) */ const expectedResult = [ { - country: 'SK', - locale: 'we', - region: 'westeros', - name: 'Seven Kingdoms', - flag: '/assets/images/flags/SK.png' - } + country: 'SK', + locale: 'we', + region: 'westeros', + name: 'Seven Kingdoms', + flag: '/assets/images/flags/SK.png', + }, ]; /* Assert */ diff --git a/_posts/2017-10-24-high-level-assertions-with-qunit-dom.md b/_posts/2017-10-24-high-level-assertions-with-qunit-dom.md index 54934c194b..19f0246c78 100755 --- a/_posts/2017-10-24-high-level-assertions-with-qunit-dom.md +++ b/_posts/2017-10-24-high-level-assertions-with-qunit-dom.md @@ -1,10 +1,10 @@ --- title: High Level Assertions with qunit-dom -author: "Tobias Bieniek" +author: 'Tobias Bieniek' github: Turbo87 twitter: tobiasbieniek -bio: "Senior Frontend Engineer, Ember CLI core team member" -description: "Tobias Bieniek introduces qunit-dom, an extension for qunit that allows writing more expressive and less complex UI tests using high level DOM assertions." +bio: 'Senior Frontend Engineer, Ember CLI core team member' +description: 'Tobias Bieniek introduces qunit-dom, an extension for qunit that allows writing more expressive and less complex UI tests using high level DOM assertions.' topic: javascript --- @@ -33,7 +33,6 @@ for an Ember app that we will write a test for: From the template above you can see that we have essentially two states that we need to test: one with and one without a `username` property being set. - ## Status Quo An acceptance test for such a template could look roughly like this: @@ -61,7 +60,6 @@ matches our expectation. Next we will `fill` an `` field with a custom `username` like "John Doe" `andThen` finally we will check if the welcome message was updated correctly. - ## Promise chains If you've used [Ember.js](https://emberjs.com/) for some time you will probably @@ -76,15 +74,17 @@ look like this: ```js test('frontpage should be welcoming', function(assert) { - return visit('/').then(function() { - assert.equal(find('h1.title').textContent.trim(), 'Welcome to Ember!'); - assert.notOk(find('h1.title').classList.contains('has-username')); - - return fillIn('input.username', 'John Doe'); - }).then(function() { - assert.equal(find('h1.title').textContent.trim(), 'Welcome to Ember,\n John Doe!'); - assert.ok(find('h1.title').classList.contains('has-username')); - }); + return visit('/') + .then(function() { + assert.equal(find('h1.title').textContent.trim(), 'Welcome to Ember!'); + assert.notOk(find('h1.title').classList.contains('has-username')); + + return fillIn('input.username', 'John Doe'); + }) + .then(function() { + assert.equal(find('h1.title').textContent.trim(), 'Welcome to Ember,\n John Doe!'); + assert.ok(find('h1.title').classList.contains('has-username')); + }); }); ``` @@ -93,7 +93,6 @@ code here, it also make the code a little harder to read. Which is one of the reasons why a lot of Ember developers still prefer the `andThen` blocks over using `Promise` chains. - ## async/await In one of the recent changes to the JavaScript language (or ECMAScript to be @@ -133,7 +132,6 @@ The assertions (the lines starting with `assert.`) however are still quite hard to read, and it takes a short while to figure out what the intent of that assertion was. - ## chai and chai-dom If you're using [Mocha](https://mochajs.org/) and [Chai](http://chaijs.com/) @@ -160,7 +158,6 @@ expect(find('h1.title')).to.have.class('has-username'); restart Ember CLI and now you can use the additional assertions that `chai-dom` provides. - ## qunit-dom While `ember-cli-chai` also works with QUnit it is essentially just a hack @@ -189,7 +186,7 @@ Compared to what we started with this: `#ember-testing` element) based on the selector passed into the `dom()` function - collapses whitespace according to the HTML spec to get rid of the irrelevant - `\n ` part of the expected string + `\n` part of the expected string - provides readable high level assertions for the most common checks on DOM elements @@ -203,7 +200,6 @@ itself into the build pipeline of your projects, so all you need to do is You can find examples of what assertions are available in the [README](https://github.com/simplabs/qunit-dom#qunit-dom) of the project and even more information in the [API reference](https://github.com/simplabs/qunit-dom/blob/master/API.md). - ## qunit-dom-codemod During the EmberFest conference we realized that while a lot of people would @@ -230,7 +226,6 @@ and then run the codemod e.g. on your `tests` folder: jscodeshift -t https://raw.githubusercontent.com/simplabs/qunit-dom-codemod/master/qunit-dom-codemod.js ./tests/ ``` - ## Conclusion Moving the tests to `async/await` and `qunit-dom` makes them a lot more diff --git a/_posts/2017-11-17-ember-test-selectors-road-to-1-0.md b/_posts/2017-11-17-ember-test-selectors-road-to-1-0.md index 5b861c087c..d8dc285baf 100755 --- a/_posts/2017-11-17-ember-test-selectors-road-to-1-0.md +++ b/_posts/2017-11-17-ember-test-selectors-road-to-1-0.md @@ -1,10 +1,10 @@ --- -title: "ember-test-selectors: The road to 1.0" -author: "Tobias Bieniek" +title: 'ember-test-selectors: The road to 1.0' +author: 'Tobias Bieniek' github: Turbo87 twitter: tobiasbieniek -bio: "Senior Frontend Engineer, Ember CLI core team member" -description: "Tobias Bieniek goes through what happened in ember-test-selectors during the past year and what the roadmap towards a 1.0 release is." +bio: 'Senior Frontend Engineer, Ember CLI core team member' +description: 'Tobias Bieniek goes through what happened in ember-test-selectors during the past year and what the roadmap towards a 1.0 release is.' topic: ember --- @@ -67,7 +67,6 @@ attributes and always declare them with a value: We will deprecate the usage of valueless `data-test-*` attributes on components in an upcoming release and remove the transform completely for the v1.0.0 release. - ## v0.3.0: Support for Babel 6 This year (2017) the Ember ecosystem finally moved from Babel 5 to Babel 6, @@ -92,7 +91,6 @@ which version of `ember-cli-babel` you use. Based on that information we inject different Babel transforms into the build pipeline so that we can support both major Babel versions with the same addon. - ## v0.3.4: Support for test-selectors in nested addons In April we were notified by [Luke Melia](https://github.com/lukemelia) that @@ -106,7 +104,6 @@ problem. Using his reproduction we have been able to fix the issue and it is now possible to also use `ember-test-selectors` as a nested addon dependency and rely on correct test-selector stripping depending on the build environment. - ## v0.3.7: Deprecation of the `testSelector` helper function A few weeks later [Kelly Selden](https://github.com/kellyselden) triggered a @@ -140,8 +137,7 @@ there is a third-party [codemod](https://github.com/lorcan/test-selectors-codemo that might be able to save you a few minutes or hours by converting the code for you automatically. - -## v0.3.8: Support for Ember 1.11 and 1.12 +## v0.3.8: Support for Ember 1.11 and 1.12 Only a few weeks ago [Chris Garrett](https://github.com/pzuraq/), better known as @pzuraq, approached us about supporting Ember 1.11 and 1.12 in @@ -221,7 +217,6 @@ Once we had that conditional in place all our tests were 🍏 again and we were able to adjust our range of supported Ember versions all the way down to Ember 1.11. - ## Conclusion A lot of things have happened on the project this year and we will continue to diff --git a/_posts/2017-12-04-enginification.md b/_posts/2017-12-04-enginification.md index 1a2a3df4d3..7694890282 100755 --- a/_posts/2017-12-04-enginification.md +++ b/_posts/2017-12-04-enginification.md @@ -1,10 +1,10 @@ --- title: Enginification -author: "Clemens Müller" +author: 'Clemens Müller' github: pangratz twitter: pangratz -bio: "Full-Stack Engineer, Ember Data core team member" -description: "Clemens Müller gives an overview of Ember Engines and shows how they can be used to reduce the footprint of big applications for an improved startup time." +bio: 'Full-Stack Engineer, Ember Data core team member' +description: 'Clemens Müller gives an overview of Ember Engines and shows how they can be used to reduce the footprint of big applications for an improved startup time.' topic: ember --- @@ -88,12 +88,10 @@ Since the component is now located within the `addon` folder, we need to modify import layout from '../templates/components/loading-indicator'; export default Component.extend({ - // this is needed so the template within addon/templates/components is used layout, // previous code of the component - }); ``` @@ -124,7 +122,7 @@ from the common components and helpers. By this all the styles from the ```scss // lib/common/app/styles/common.scss -@import "common/components/loading-indicator"; +@import 'common/components/loading-indicator'; ``` Within the app we import the style definitions for the common addon, so all the @@ -134,14 +132,13 @@ and `mixins` defined in the common addon. ```scss // app/styles/app.scss -@import "common"; +@import 'common'; ``` After all that is done, we now have all common components and helpers, as well as common style definitions moved into the addon. The next step is to finally create the engine, which will contain most of the application code. - ## Move functionality into engine An engine is basically an Ember.js addon. Since the engine won't be reused @@ -171,9 +168,7 @@ can re-use the common elements within the engine. Let's take a look at }, "ember-addon": { - "paths": [ - "../common" - ] + "paths": ["../common"] } } ``` @@ -213,8 +208,8 @@ definitions. And since we namespaced the files in the `common` addon under the ```scss // lib/booking-flow/addon/styles/components/booking-button.scss -@import "common/colors"; -@import "common/mixins/button"; +@import 'common/colors'; +@import 'common/mixins/button'; .booking-button { @include button-rounded-mixin; diff --git a/_posts/2018-01-24-ember-freestyle.md b/_posts/2018-01-24-ember-freestyle.md index 017cfd5372..21aa68caa1 100755 --- a/_posts/2018-01-24-ember-freestyle.md +++ b/_posts/2018-01-24-ember-freestyle.md @@ -1,28 +1,27 @@ --- -title: "Using ember-freestyle as a component playground" -author: "Tobias Bieniek" +title: 'Using ember-freestyle as a component playground' +author: 'Tobias Bieniek' github: Turbo87 twitter: tobiasbieniek -bio: "Senior Frontend Engineer, Ember CLI core team member" -description: "Tobias Bieniek gives an overview of how ember-freestyle can be used in Ember.js applications for building and testing components in isolation." +bio: 'Senior Frontend Engineer, Ember CLI core team member' +description: 'Tobias Bieniek gives an overview of how ember-freestyle can be used in Ember.js applications for building and testing components in isolation.' topic: ember --- A component playground is an application that you can use to test out and play around with your custom components in isolation from the rest of your -project. In the React and Vue ecosystem [Storybook] is a quite popular project +project. In the React and Vue ecosystem [Storybook][storybook] is a quite popular project that implements such a component playground as part of your app. In the Ember ecosystem we have the [`ember-freestyle`][ember-freestyle] addon that can be used for this purpose. This blog post will show you how to install `ember-freestyle` in your app and how to use it to build and test components in isolation. -[Storybook]: https://storybook.js.org/ +[storybook]: https://storybook.js.org/ [ember-freestyle]: http://ember-freestyle.com/ - ## Component Playgrounds You might be wondering "Why would would I need something like this? I can just @@ -47,7 +46,6 @@ by Dominic Nguyen that explains the benefits very well. [ui-component-explorers]: https://blog.hichroma.com/the-crucial-tool-for-modern-frontend-engineers-fb849b06187a - ## Installing `ember-freestyle` Installing and configuring `ember-freestyle` correctly is unfortunately a @@ -132,8 +130,8 @@ module.exports = function(defaults) { let app = new EmberApp(defaults, { addons: { - blacklist: pluginsToBlacklist - } + blacklist: pluginsToBlacklist, + }, }); return app.toTree(); @@ -190,7 +188,6 @@ production. While we could put in some effort to remove those too the situation is good enough and we can finally focus on putting content into our new component playground! 🎉 - ## Using `ember-freestyle` The good news is that **using** `ember-freestyle` is much easier than setting @@ -216,7 +213,7 @@ In addition to that we'll add some styles for this button to our .styled-button { border: 1px solid #eee; border-radius: 3px; - background-color: #FFFFFF; + background-color: #ffffff; cursor: pointer; font-size: 15px; padding: 3px 10px; @@ -260,7 +257,7 @@ playground. In a follow-up post we will soon discuss how to extract subsections into components and how to automatically discover and inject them into the main template. -Finally I would like to thank [Chris LoPresto] and the other contributors for +Finally I would like to thank [Chris LoPresto][chris-lopresto] and the other contributors for working on `ember-freestyle` and would encourage you to give it a try! -[Chris LoPresto]: https://github.com/chrislopresto +[chris-lopresto]: https://github.com/chrislopresto diff --git a/_posts/2018-02-14-handling-webhooks-in-phoenix.md b/_posts/2018-02-14-handling-webhooks-in-phoenix.md index d89a128246..47ce94ce93 100755 --- a/_posts/2018-02-14-handling-webhooks-in-phoenix.md +++ b/_posts/2018-02-14-handling-webhooks-in-phoenix.md @@ -1,10 +1,10 @@ --- title: Handling Webhooks in Phoenix -author: "Niklas Long" +author: 'Niklas Long' github: niklaslong twitter: niklas_long -bio: "Backend Engineer, author of Breethe API" -description: "Niklas Long introduces an effective and simple approach for handling incoming webhook requests in Phoenix applications with advanced routing." +bio: 'Backend Engineer, author of Breethe API' +description: 'Niklas Long introduces an effective and simple approach for handling incoming webhook requests in Phoenix applications with advanced routing.' topic: elixir --- @@ -24,9 +24,9 @@ I.e., Let's restate the problem: -* all requests are being sent to the same webhook callback url -* there are many different possible request payloads -* application requires different computation depending on payload +- all requests are being sent to the same webhook callback url +- there are many different possible request payloads +- application requires different computation depending on payload Let's say we're receiving webhooks which contain an `event` key in the request body. It describes the event which triggered the webhook and we can use it to determine what code we are going to run. @@ -128,8 +128,8 @@ The core components of a Phoenix application are plugs. This includes endpoints, Let's examine the code above, you'll notice there are two functions already defined: -* `init/1` which initializes any arguments or options to be passed to `call/2` (executed at compile time) -* `call/2` which transforms the connection (it's actually a simple function plug and is executed at run time) +- `init/1` which initializes any arguments or options to be passed to `call/2` (executed at compile time) +- `call/2` which transforms the connection (it's actually a simple function plug and is executed at run time) Both of these need to be implemented in a module plug. Let's modify `call/2` to match the `addition` event in the request payload and change the request path to the route we defined for addition: @@ -155,9 +155,9 @@ end This strategy isn't great, however. We are placing code in the endpoint, which will be executed no matter what the request path is. Furthermore, the endpoint is only supposed to (from the [docs](https://hexdocs.pm/phoenix/Phoenix.Endpoint.html#content)): -* provide a wrapper for starting and stopping the endpoint as part of a supervision tree -* define an initial plug pipeline for requests to pass through -* host web specific configuration for your application +- provide a wrapper for starting and stopping the endpoint as part of a supervision tree +- define an initial plug pipeline for requests to pass through +- host web specific configuration for your application Interfering with the request to map it to a route at this point would be unidiomatic Phoenix. It would also make the app slower, and harder to maintain and debug. diff --git a/_posts/2018-05-30-a-little-encouragement-goes-a-long-way-in-2018.md b/_posts/2018-05-30-a-little-encouragement-goes-a-long-way-in-2018.md index 7f48824b42..c89bbd49a5 100755 --- a/_posts/2018-05-30-a-little-encouragement-goes-a-long-way-in-2018.md +++ b/_posts/2018-05-30-a-little-encouragement-goes-a-long-way-in-2018.md @@ -1,14 +1,14 @@ --- title: A Little Encouragement Goes a Long Way in 2018 -author: "Jessica Jordan" +author: 'Jessica Jordan' github: jessica-jordan twitter: jjordan_dev -bio: "Senior Frontend Engineer, Ember Learning core team member" -description: "Jessica Jordan shares her thoughts for Ember.js in the coming year in response to the Ember 2018 Roadmap Call for Blog Posts." +bio: 'Senior Frontend Engineer, Ember Learning core team member' +description: 'Jessica Jordan shares her thoughts for Ember.js in the coming year in response to the Ember 2018 Roadmap Call for Blog Posts.' topic: ember --- -In May 2018, Ember Core team member Katie Gengler published [Ember's 2018 Roadmap: A Call for Blog Posts](https://www.emberjs.com/blog/2018/05/02/ember-2018-roadmap-call-for-posts.html). With this call-to-action she invites the community to give feedback on their hopes and wishes for Ember moving forward. +In May 2018, Ember Core team member Katie Gengler published [Ember's 2018 Roadmap: A Call for Blog Posts](https://www.emberjs.com/blog/2018/05/02/ember-2018-roadmap-call-for-posts.html). With this call-to-action she invites the community to give feedback on their hopes and wishes for Ember moving forward. In this context, I also want to share some of my own thoughts on Ember and what I'd be excited to see in its nearest future. @@ -37,7 +37,6 @@ Instead when I think about what I wish for Ember, _visibility_ comes to my mind. I'd love both Ember's core characteristics and its cutting-edge features to be understood by even more people; even by those who haven't had much experience in using it to see its worthwhile benefits in the long-run of a project. I believe Ember's bottleneck for adoption and community growth isn't about technical aspects, it's about visibility and outreach. - ## A Gap in Visibility As any other open-source project, the development of Ember lives and thrives through the time and amount of work that is being put into it. In contrast to some other major JavaScript frameworks, Ember is not being backed up by one single major company - neither financially nor contribution-wise. diff --git a/_posts/2018-06-05-ember-component-playground.md b/_posts/2018-06-05-ember-component-playground.md index 37bd9040dc..048c29a9bb 100755 --- a/_posts/2018-06-05-ember-component-playground.md +++ b/_posts/2018-06-05-ember-component-playground.md @@ -1,10 +1,10 @@ --- -title: "Autodiscovery for the Ember.js component playground" -author: "Tobias Bieniek" +title: 'Autodiscovery for the Ember.js component playground' +author: 'Tobias Bieniek' github: Turbo87 twitter: tobiasbieniek -bio: "Senior Frontend Engineer, Ember CLI core team member" -description: "Tobias Bieniek introduces a mechanism for automatically discovering new components in Ember.js applications and showing them in an ember-freestyle playground." +bio: 'Senior Frontend Engineer, Ember CLI core team member' +description: 'Tobias Bieniek introduces a mechanism for automatically discovering new components in Ember.js applications and showing them in an ember-freestyle playground.' topic: ember --- @@ -18,7 +18,6 @@ discovering new components and showing them in the playground. - ## Status Quo You may remember that last time we closed with our `freestyle.hbs` template @@ -50,7 +49,7 @@ other template in Ember.js we can tackle that problem by extracting components. In this case we can create a `lib/freestyle/app/templates/components/usage/styled-button.hbs` file that -looks like this: +looks like this: ```handlebars {{#freestyle-usage 'styled-button-example-1'}} @@ -77,7 +76,6 @@ the situation has gotten a lot more manageable. But there is always a way to improve the current situation so let's play around a little... - ## Autodiscovery What if we could ask Ember what "usage components" it knows about and then @@ -95,9 +93,10 @@ components". We can do so by using the `.filter()` method and checking for the right prefix: ```js -let usageComponents = Object.keys(require.entries) - .filter(path => path.indexOf('myapp/templates/components/usage/') === 0); -``` +let usageComponents = Object.keys(require.entries).filter( + path => path.indexOf('myapp/templates/components/usage/') === 0, +); +``` This should produce a list that looks roughly like this: @@ -106,7 +105,7 @@ This should produce a list that looks roughly like this: 'myapp/templates/components/usage/styled-button', 'myapp/templates/components/usage/country-flag', 'myapp/templates/components/usage/form-input', -] +]; ``` To be able to use those as component names in our template we will have to do a @@ -164,7 +163,6 @@ If we visit the `/freestyle` route now, we should see a list of all our "usage components" that Ember.js knows about and usage examples for all of them. - ## Summary Just like Ember.js is able to automatically find components, models, and other diff --git a/_posts/2018-06-11-actix.md b/_posts/2018-06-11-actix.md index 8401316996..6a6d8c63e1 100755 --- a/_posts/2018-06-11-actix.md +++ b/_posts/2018-06-11-actix.md @@ -1,34 +1,34 @@ --- -title: "actix – an actor framework for the Rust programming language" -author: "Tobias Bieniek" +title: 'actix – an actor framework for the Rust programming language' +author: 'Tobias Bieniek' github: Turbo87 twitter: tobiasbieniek -bio: "Senior Frontend Engineer, Ember CLI core team member" -description: "Tobias Bieniek gives an overview of actix, an actor framework written in the Rust programming language." +bio: 'Senior Frontend Engineer, Ember CLI core team member' +description: 'Tobias Bieniek gives an overview of actix, an actor framework written in the Rust programming language.' topic: rust og: image: /assets/images/posts/2018-06-11-actix/og-image.png --- -While we mostly focus our work around [Ember.js], [Rails] and [Elixir], we -sometimes experiment with other technologies for internal projects. This time -we tried out [Rust], and more specifically the [actix] actor framework. This -blog post is a short intro into what we've discovered so far. +While we mostly focus our work around [Ember.js][ember], [Rails][rails] and +[Elixir][elixir], we sometimes experiment with other technologies for internal +projects. This time we tried out [Rust], and more specifically the +[actix][actix] actor framework. This blog post is a short intro into what we've +discovered so far. -[Ember.js]: https://emberjs.com/ -[Rails]: https://rubyonrails.org/ -[Elixir]: https://elixir-lang.org/ -[Rust]: https://rust-lang.org/ +[ember.js]: https://emberjs.com/ +[rails]: https://rubyonrails.org/ +[elixir]: https://elixir-lang.org/ +[rust]: https://rust-lang.org/ [actix]: https://actix.rs/ - ## Rust What is Rust? -Aside from being "an iron oxide" according to [Wikipedia], Rust is also the +Aside from being "an iron oxide" according to [Wikipedia][wikipedia], Rust is also the name of a programming language that was originally invented by Graydon Hoare back in 2006. You can think about it as an interesting mix of high-level languages like JavaScript or Ruby and low-level high-performance languages @@ -57,10 +57,7 @@ languages if raw speed is needed, it can compile to WebAssembly, and most of all, it is much safer than C++, which would also be a candidate for all the previous points. -How to get started? Follow the instructions at - -[Wikipedia]: https://en.wikipedia.org/wiki/Rust - +How to get started? Follow the instructions at [rustup.rs][rustup] ## Actors @@ -104,18 +101,15 @@ is usually hidden in the actor frameworks that are used to run these types of primitives in the end. One example of such an actor framework is [actix], which we will have a closer look at now. -[Erlang]: https://www.erlang.org/ - - ## actix -[actix] is the low-level actor framework that powers [actix-web], a -[high-performance](https://www.techempower.com/benchmarks/#section=data-r15&hw=ph&test=plaintext) -web framework for the [Rust] programming language. +[actix] is the low-level actor framework that powers [actix-web][actix-web], a +[high-performance][performance] web framework for the [Rust][rust] programming +language. -While [actix-web] is interesting and worth another blog post, we will focus on -the low-level primitive [actix] for now as it is vital to understanding the -higher level concepts. +While [actix-web][actix-web] is interesting and worth another blog post, we +will focus on the low-level primitive [actix][actix] for now as it is vital to +understanding the higher level concepts. To get started with actix, let's port our `CounterActor` above to Rust: @@ -180,14 +174,11 @@ defined earlier. In the `handle()` method we increment the `count` state and then return the new value to tell actix that this is our response to the message. -[actix-web]: https://github.com/actix/actix-web - - ### Running our `CounterActor` While building the actor was relatively easy, running it is unfortunately still a little hard while Rust figures out its version of `async/await` (see -[`futures-await`]). +[`futures-await`][futures-await]). The following code will startup our actor, send a message, wait for the response, send another message, wait for the response and finally exit the @@ -217,15 +208,15 @@ Arbiter::handle().spawn(result); sys.run(); ``` -The first thing to do when using actix actors is to set up a [`System`], that -handles all those actor interactions for us. We do so by calling -`actix::System::new()` and passing it a name. +The first thing to do when using actix actors is to set up a +[`System`][system], that handles all those actor interactions for us. We do so +by calling `actix::System::new()` and passing it a name. -Next we start an [`Arbiter`] in a new thread, that runs our `CounterActor`. If -that sounds like a foreign language to you, don't worry, I had the same feeling -at first. For now all you need to know is that `Syn` means that it is running -in a separate thread, and that the `Arbiter` is the thing that controls that -thread. +Next we start an [`Arbiter`][artiter] in a new thread, that runs our +`CounterActor`. If that sounds like a foreign language to you, don't worry, I +had the same feeling at first. For now all you need to know is that `Syn` means +that it is running in a separate thread, and that the `Arbiter` is the thing +that controls that thread. The `Arbiter::start()` call returns an `Addr` (short for address), that we can use to talk to the actor. The `Addr` struct has methods like `send()` that can @@ -258,14 +249,18 @@ To start the `Future` that we have assembled we use the once everything is wired up correctly to block the current thread until all actors have finished running. -[`futures-await`]: https://github.com/alexcrichton/futures-await -[`System`]: https://actix.rs/actix/actix/struct.System.html -[`Arbiter`]: https://actix.rs/actix/actix/struct.Arbiter.html - - ## Summary This blog post covered some of the basic concepts of writing actors using the actix framework for Rust. In a follow-up post we will look into writing a small TCP client using these primitives, which can for example be used to forward traffic to websocket clients or just log the received messages to the console. + +[rustup]: https://rustup.rs/ +[wikipedia]: https://en.wikipedia.org/wiki/Rust +[erlang]: https://www.erlang.org/ +[performance]: https://www.techempower.com/benchmarks/#section=data-r15&hw=ph&test=plaintext +[actix-web]: https://github.com/actix/actix-web +[futures-await]: https://github.com/alexcrichton/futures-await +[system]: https://actix.rs/actix/actix/struct.System.html +[arbiter]: https://actix.rs/actix/actix/struct.Arbiter.html diff --git a/_posts/2018-06-18-intl-polyfill-loading.md b/_posts/2018-06-18-intl-polyfill-loading.md index a06dd69418..c077117928 100755 --- a/_posts/2018-06-18-intl-polyfill-loading.md +++ b/_posts/2018-06-18-intl-polyfill-loading.md @@ -1,27 +1,26 @@ --- -title: "ember-intl data loading patterns" -author: "Tobias Bieniek" +title: 'ember-intl data loading patterns' +author: 'Tobias Bieniek' github: Turbo87 twitter: tobiasbieniek -bio: "Senior Frontend Engineer, Ember CLI core team member" -description: "Tobias Bieniek shows how to load the necessary polyfills for the Intl API in older browsers most effectively when using ember-intl." +bio: 'Senior Frontend Engineer, Ember CLI core team member' +description: 'Tobias Bieniek shows how to load the necessary polyfills for the Intl API in older browsers most effectively when using ember-intl.' topic: ember --- -At simplabs we ❤️ [ember-intl] and use it for all our projects where +At simplabs we ❤️ [ember-intl][ember-intl] and use it for all our projects where translations or other localizations are needed. ember-intl is based on the -native [Intl] APIs that were introduced in [all newer browsers] a while ago. +native [Intl APIs][intl] that were introduced in [all newer browsers][browsers] a while ago. Unfortunately some users are still using browsers that don't support them and this blog post will show you our preferred way to load the necessary polyfill and the associated data. [ember-intl]: https://github.com/ember-intl/ember-intl -[Intl]: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Intl -[all newer browsers]: https://caniuse.com/#feat=internationalization +[intl]: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Intl +[browsers]: https://caniuse.com/#feat=internationalization - ## Loading Translations Let's first start with how translations are loaded in ember-intl. By default @@ -89,7 +88,6 @@ If we now open our app in the browser and look at the "Network" tab of the browser we should see the app making an AJAX request for the translations before it starts. 🎉 - ## Loading the Intl.js polyfill As mentioned in the intro [some browsers](https://caniuse.com/#feat=internationalization) @@ -157,7 +155,7 @@ async beforeModel() { } ``` -If you visit the app in your regular browser now you should *not* see any +If you visit the app in your regular browser now you should _not_ see any request for the `intl.min.js` file. But if you open the app in IE10 (e.g. via ) you should see the polyfill being loaded. @@ -209,14 +207,13 @@ In case you're wondering what "locale data" actually means: it includes information for the Intl.js polyfill on how to format dates, time and numbers and several other things that are handled in a locale-aware way in the Intl API. - ## Summary In this blog post we have learned how to reduce our bundle size in several ways -when using the [ember-intl] addon. We are now loading only the code and data -that we actually need for the specific browser. Most users don't pay the extra -cost of loading the polyfill and related data, and for the browsers that do -need it, it's available on demand. +when using the [ember-intl][ember-intl] addon. We are now loading only the code +and data that we actually need for the specific browser. Most users don't pay +the extra cost of loading the polyfill and related data, and for the browsers +that do need it, it's available on demand. If you have any questions about these patterns or need help implementing them in your apps feel free to [contact us](/contact/). diff --git a/_posts/2018-06-27-actix-tcp-client.md b/_posts/2018-06-27-actix-tcp-client.md index 739580a7d5..cafe0d0b03 100755 --- a/_posts/2018-06-27-actix-tcp-client.md +++ b/_posts/2018-06-27-actix-tcp-client.md @@ -1,10 +1,10 @@ --- -title: "actix – a basic TCP client" -author: "Tobias Bieniek" +title: 'actix – a basic TCP client' +author: 'Tobias Bieniek' github: Turbo87 twitter: tobiasbieniek -bio: "Senior Frontend Engineer, Ember CLI core team member" -description: "Tobias Bieniek shows how actix, the actor framework written in Rust, can be used to build a basic TCP client." +bio: 'Senior Frontend Engineer, Ember CLI core team member' +description: 'Tobias Bieniek shows how actix, the actor framework written in Rust, can be used to build a basic TCP client.' topic: rust og: image: /assets/images/posts/2018-06-27-actix-tcp-client/og-image.png @@ -16,7 +16,7 @@ client with actix. [last post]: https://simplabs.com/blog/2018/06/11/actix.html [actix]: https://actix.rs/ -[Rust]: https://rust-lang.org/ +[rust]: https://rust-lang.org/ @@ -32,12 +32,12 @@ TCP server, listens for new messages and forwards them to another `recipient` actor. For simplicity we'll assume that the server is using a line break after each message. - ## Project Setup In our last post we explained that the easiest solution to install Rust is -currently . Once you have followed the instructions there -you should have access to the `cargo` build tool on your command line. +currently [rustup.rs](https://rustup.rs/). Once you have followed the +instructions there you should have access to the `cargo` build tool on your +command line. Creating a new project for Rust is easy with `cargo`: @@ -52,8 +52,11 @@ binary projects have a `main()` function and get compiled to executables, while library projects are used to share code and functionality at [crates.io](https://crates.io). -Fun fact: did you know that [crates.io](https://github.com/rust-lang/crates.io) -is an open-source project itself and built with Rust and [Ember.js](https://emberjs.com/)? +Fun fact: did you know that [crates.io][crates] is an open-source project +itself and built with Rust and [Ember.js][ember]? + +[crates]: https://github.com/rust-lang/crates.io +[ember]: https://emberjs.com/ Now that we have created the project let's see if it runs: @@ -65,12 +68,11 @@ If everything went well you should see `cargo` compiling the project and afterwards running it, resulting in "Hello, world!" being printed in your Terminal. - ## The Actor Before we can start to implement our TCP client actor we need to tell `cargo` about the `actix` dependency. For that we open the `Cargo.toml` file in the -project and add the following line *after* the `[dependencies]` declaration: +project and add the following line _after_ the `[dependencies]` declaration: ```toml actix = "0.5" @@ -146,7 +148,6 @@ implemented nothing that would stop the actor. First milestone achieved! 🎉 - ## DNS resolution and TCP connection Now that we have setup the basic scaffolding of our `Actor`, let's try to @@ -214,12 +215,11 @@ Great! We have successfully opened up a TCP connection to the `towel.blinkenlights.nl` server and if you're curious what kind of server that is: keep reading! 😉 - ## Reading TCP streams For the next part we'll need two more dependencies: `tokio-io` and -`tokio-codec`. [tokio] is the low-level network IO library that `actix` is -using underneath. +`tokio-codec`. [tokio][tokio] is the low-level network IO library that `actix` +is using underneath. [tokio]: https://tokio.rs/ @@ -265,13 +265,13 @@ Ok(stream) => { ``` Now we have a read part (`r`) that we can call `read()` on and a write part -(`w`) that we could call `write()` on. Unfortunately the [Read] and [Write] -traits only operate on `u8` arrays though, so for our purpose of reading -strings separated by line breaks this isn't quite a user friendly as we would -like. +(`w`) that we could call `write()` on. Unfortunately the [Read][read] and +[Write][write] traits only operate on `u8` arrays though, so for our purpose of +reading strings separated by line breaks this isn't quite a user friendly as we +would like. -[Read]: https://doc.rust-lang.org/std/io/trait.Read.html -[Write]: https://doc.rust-lang.org/std/io/trait.Write.html +[read]: https://doc.rust-lang.org/std/io/trait.Read.html +[write]: https://doc.rust-lang.org/std/io/trait.Write.html Fortunately, `tokio` has a solution for that: @@ -304,7 +304,6 @@ right below the line that constructs the `FramedRead` instance. I won't spoiler anything, but take a bit of time and see what happens now if you start `cargo run` again... 🚀 - ## Forward messages to other actors In case you're still reading, let's implement the final part of our goal: @@ -319,7 +318,7 @@ implementing the `Message` trait, or deriving it automatically in most cases: pub struct ReceivedLine { pub line: String, } -``` +``` Now that we know how the message looks like we should implement a second actor that can receive such messages and print them to the terminal: @@ -358,7 +357,7 @@ The more generic solution is to use the `Recipient` struct of `actix`: struct TcpClientActor { recipient: Recipient, } -``` +``` Afterwards we change the `StreamHandler` implementation to this: @@ -395,7 +394,6 @@ fn main() { After everything is assembled back together let's use `cargo run` again and we will see that everything still works! 🎥 - ## Summary We hope that by now you're a little more comfortable around actors and how to diff --git a/_posts/2018-07-03-building-a-pwa-with-glimmer-js.md b/_posts/2018-07-03-building-a-pwa-with-glimmer-js.md index b3efb544ab..3edd6ee178 100755 --- a/_posts/2018-07-03-building-a-pwa-with-glimmer-js.md +++ b/_posts/2018-07-03-building-a-pwa-with-glimmer-js.md @@ -1,10 +1,10 @@ --- -title: "Building a PWA with Glimmer.js" -author: "Marco Otte-Witte" +title: 'Building a PWA with Glimmer.js' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte dives deep into the details of how simplabs built Breethe, an open source progressive web app, with Glimmer.js." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte dives deep into the details of how simplabs built Breethe, an open source progressive web app, with Glimmer.js.' topic: ember og: image: /assets/images/posts/2018-07-03-building-a-pwa-with-glimmer-js/og-image.png @@ -93,14 +93,14 @@ _setupRouting() { this.router .on('/', () => this.mode = MODE_SEARCH) - + .on('/search', () => this.mode = MODE_SEARCH) .on('/search/:searchTerm', (params) => { this.mode = MODE_SEARCH; this.searchTerm = params.searchTerm; }) - + .on('/location/:locationId/', (params) => { this.mode = MODE_RESULTS; this.searchTerm = params.locationId; @@ -229,16 +229,16 @@ that will usually require a few iterations until the API becomes stable. Some new things that originate in experiments done in Glimmer.js have already found their way back into Ember.js (at least in some form): -* The `@` syntax as shown above that clearly distinguishes properties that are +- The `@` syntax as shown above that clearly distinguishes properties that are set on a component instance vs. attributes that are set on a component's root DOM element - [this PR](https://github.com/emberjs/ember.js/commit/4bd3d7b882484919682ab0cdb57f81584abc503a) enables the feature flag by default. -* The possibility to use ES2015 classes instead of Ember.js' own object model - +- The possibility to use ES2015 classes instead of Ember.js' own object model - see [this blog post](https://medium.com/build-addepar/es-classes-in-ember-js-63e948e9d78e) for more information. -* Template-only components that do not have a wrapping `
` - can be enabled +- Template-only components that do not have a wrapping `
` - can be enabled as an [optional feature](https://github.com/emberjs/ember-optional-features). Eventually it will be possible to seamlessly use Glimmer.js components in diff --git a/_posts/2018-07-24-from-spa-to-pwa.md b/_posts/2018-07-24-from-spa-to-pwa.md index 2da829965f..dfa78a3624 100755 --- a/_posts/2018-07-24-from-spa-to-pwa.md +++ b/_posts/2018-07-24-from-spa-to-pwa.md @@ -1,10 +1,10 @@ --- -title: "From SPA to PWA" -author: "Marco Otte-Witte" +title: 'From SPA to PWA' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte goes over the characteristics of progressive web apps and shows how to make the next step from a SPA to a PWA." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte goes over the characteristics of progressive web apps and shows how to make the next step from a SPA to a PWA.' topic: javascript og: image: /assets/images/posts/2018-07-24-from-spa-to-pwa/og-image.png @@ -64,13 +64,13 @@ for users for several reasons (ultimately leading to the question of how many potential users are lost before they even install the app where they then **could** have been successfully converted): -* users that are on the mobile website already, when asked to install the app, +- users that are on the mobile website already, when asked to install the app, need to leave that website, go to the app store, download the app and continue through the installation process -* once the app is installed, all input that potentially was made on the website +- once the app is installed, all input that potentially was made on the website before is lost and needs to be restored besides users potentially needing to login in the app again -* native apps are +- native apps are [relatively large on average](https://sweetpricing.com/blog/2017/02/average-app-file-size/), leading to long download times which are particularly painful on less reliable mobile connections @@ -112,15 +112,15 @@ over the next coming years**. Progressive Web Apps have some distinct characteristics, the main ones being: -* **Progressiveness**: they work for every user, regardless of their device or +- **Progressiveness**: they work for every user, regardless of their device or browser of choice; depending on the capabilities of that environment, they will enable more or less of their functionality -* **Responsiveness**: they fit any form factor and screen sizes -* **Connectivity Independence**: they work on low quality networks or without +- **Responsiveness**: they fit any form factor and screen sizes +- **Connectivity Independence**: they work on low quality networks or without any network at all (and thus fully offline) -* **App-likeliness**: they offer the rich user experience that users know and +- **App-likeliness**: they offer the rich user experience that users know and love from native apps -* **Installability**: they can be installed on the user's home screen without +- **Installability**: they can be installed on the user's home screen without having to go through an app store We will be focussing on two of these characteristics in this article - @@ -144,7 +144,7 @@ looks like this: { "name": "Breethe", "short_name": "Breethe", - "description":"Air Quality Data for Locations around the World", + "description": "Air Quality Data for Locations around the World", "theme_color": "#002eb8", "background_color": "#002eb8", "display": "standalone", @@ -169,22 +169,22 @@ looks like this: The manifest is presented to the browser via a `meta` tag: ```html - + ``` The main keys in the manifest are `name`, `icons`, `background_color`, `start_url`, `display` and `orientation`: -* `name`: the name of the app that will be shown on the user's home screen once +- `name`: the name of the app that will be shown on the user's home screen once the app is installed -* `icons`: icons in various sizes to use for the app on the home screen, the +- `icons`: icons in various sizes to use for the app on the home screen, the task switcher and elsewhere -* `background_color`: sets the color for the splash screen that is shown when +- `background_color`: sets the color for the splash screen that is shown when the app is started from the home screen -* `start_url`: the URL to load when the app is started from the home screen -* `display`: tells the browser how to display the app when started from the +- `start_url`: the URL to load when the app is started from the home screen +- `display`: tells the browser how to display the app when started from the home screen; this should usually be `"standalone"` -* `orientation`: the orientation to launch the app with if only one orientation +- `orientation`: the orientation to launch the app with if only one orientation is supported Breethe, when installed to the user's home screen on iOS shows up like this: @@ -243,11 +243,12 @@ used to pre-cache resources and thus make them available for later offline use. In the Breethe PWA, we use that event to preload all of the app's essential resources: + ```js const CACHE_NAME = 'breethe-cache-v1'; const PRE_CACHED_ASSETS = [ - '/app.js', - '/app.css', + '/app.js', + '/app.css', '/index.html' ]; @@ -263,7 +264,7 @@ self.addEventListener('install', function(event) { }); return Promise.all(cachePromises); - }) + }), ); }); ``` @@ -287,9 +288,9 @@ self.addEventListener('activate', function(event) { if (cacheName !== CACHE_NAME) { return caches.delete(cacheName); } - }) + }), ); - }) + }), ); }); ``` @@ -308,7 +309,7 @@ self.addEventListener('fetch', function(event) { event.respondWith( fetch(event.request).catch(error => { return caches.match('index.html'); - }) + }), ); } }); @@ -356,7 +357,7 @@ openRequest.onsuccess = function(event) { var objectStore = transaction.objectStore('locations'); var request = objectStore.get('1'); request.onsuccess = function() { - let location = request.result + let location = request.result; alert(`Loaded location ${location.name}`); }; }; @@ -382,11 +383,11 @@ import { ModelDefinition } from '@orbit/data'; export const location: ModelDefinition = { attributes: { label: { type: 'string' }, - coordinates: { type: 'string' } + coordinates: { type: 'string' }, }, relationships: { - measurements: { type: 'hasMany', model: 'measurement', inverse: 'location' } - } + measurements: { type: 'hasMany', model: 'measurement', inverse: 'location' }, + }, }; export const measurement: ModelDefinition = { @@ -395,18 +396,18 @@ export const measurement: ModelDefinition = { measuredAt: { type: 'string' }, unit: { type: 'string' }, value: { type: 'string' }, - qualityIndex: { type: 'string' } + qualityIndex: { type: 'string' }, }, relationships: { - location: { type: 'hasOne', model: 'location', inverse: 'measurements' } - } + location: { type: 'hasOne', model: 'location', inverse: 'measurements' }, + }, }; export const schema: SchemaSettings = { models: { location, - measurement - } + measurement, + }, }; ``` @@ -432,7 +433,7 @@ import JSONAPISource from '@orbit/jsonapi'; const remote = new JSONAPISource({ schema, name: 'remote', - host: 'https://api.breethe.app' + host: 'https://api.breethe.app', }); ``` @@ -445,11 +446,11 @@ import IndexedDBSource from '@orbit/indexeddb'; const backup = new IndexedDBSource({ schema, - name: 'backup' + name: 'backup', }); // when the store changes, sync the changes into the backup source -store.on('transform', (transform) => { +store.on('transform', transform => { backup.sync(transform); }); ``` @@ -512,7 +513,7 @@ describe('when offline', function() { } it('works offline', async function() { - await visit('/', async (page) => { + await visit('/', async page => { // go through the flow online first so we populate IndexedDB await page.type('[data-test-search-input]', 'Salzburg'); await page.click('[data-test-search-submit]'); diff --git a/_posts/2018-11-27-open-source-maintenance.md b/_posts/2018-11-27-open-source-maintenance.md index aaacf0ae90..94d2d074b9 100644 --- a/_posts/2018-11-27-open-source-maintenance.md +++ b/_posts/2018-11-27-open-source-maintenance.md @@ -1,10 +1,10 @@ --- -title: "Open Source Maintenance" -author: "Tobias Bieniek" +title: 'Open Source Maintenance' +author: 'Tobias Bieniek' github: Turbo87 twitter: tobiasbieniek -bio: "Senior Frontend Engineer, Ember CLI core team member" -description: "Tobias Bieniek introduces best practices for simplifying and speeding up his work on open-source and other projects." +bio: 'Senior Frontend Engineer, Ember CLI core team member' +description: 'Tobias Bieniek introduces best practices for simplifying and speeding up his work on open-source and other projects.' topic: misc --- @@ -42,7 +42,7 @@ create a tag on the new commit. For branches we use the default `master` branch as our main development branch. Feature branches are named after the thing that they are implementing or fixing -and ideally only contain isolated, focused changes. Similar to the tag names we +and ideally only contain isolated, focused changes. Similar to the tag names we sometimes keep version branches around to support older releases with names like `v1.x` or `v2.3.x`. @@ -63,8 +63,8 @@ Why is this useful? Because it allows us to define [shell aliases](https://shape for some of the most commonly used git commands for which we don't use a graphical user interface. Here are a few examples of things we regularly use: -| Alias | Command | Description | -| ----------- | ----------------------- | ----------------------------------------------------------------------------------------------------- | +| Alias | Command | Description | +| ----- | ----------------------------- | ----------------------------------------------------------------------------------------------------- | | `cl` | `git clone` | Clones a repository | | `clu` | `git clone --origin=upstream` | Clones a repository, and uses `upstream` as the default remote name | | `ao` | `git remote add origin` | Adds a new remote called `origin` | @@ -87,7 +87,6 @@ gph # click on the link in the console output to open a new GitHub PR for the branch ``` - ## Tests Having a good test suite is very important to be able to change code and be @@ -106,7 +105,6 @@ package manager [cargo], but there are [plenty of crates](https://github.com/rus to make testing even more pleasant, including the very valuable [proptest] crate. - ## Linting Similar to testing frameworks, a lot of ecosystems have tools to run "static @@ -120,7 +118,6 @@ consistent way. Again, similar tools also exist in other ecosystems like Rust ([clippy] and [rustfmt]), Python ([pyflakes] and [black]) or Elixir (`mix format`). - ## Continuous Integration The above points about testing and linting are nice to have, but if nobody runs @@ -143,7 +140,6 @@ publish new releases to [npm] whenever we push a new Git tag to the server. You can find instruction on how to configure this in their official [documentation](https://docs.travis-ci.com/user/deployment/npm/). - ## Semantic Versioning Semantic Versioning (or short "semver") is a way to assign meaning to version @@ -173,7 +169,6 @@ your package [declares](https://docs.npmjs.com/files/package.json#engines) that it works with Node.js 4, and you release a new version that needs at least Node.js 6, then you should increase the **major** version. - ## Dependency Update Services Any sufficiently large open source project has at least a few dependencies that @@ -195,7 +190,6 @@ you whether it is compatible with your code or not. If configured, dependabot can also automatically merge those Pull Requests once CI has finished and the test suite passed. - ## Changelogs While Semantic Versioning helps your users to know if they need to expect @@ -228,38 +222,36 @@ use the same set of labels we use [github-label-sync]. An [example changelog](https://github.com/simplabs/qunit-dom/blob/master/CHANGELOG.md) can be seen on our qunit-dom project. - ## Summary We hope that this blog post helped you improve your processes and speed up your own development. If you need help with any of these topics or if you have questions we encourage you to [contact us](/contact/)! - [git]: https://git-scm.com/ -[GitHub]: https://github.com/ -[Fork]: https://git-fork.com/ -[Ember.js]: https://emberjs.com/ -[Jest]: https://jestjs.io/ -[Mocha]: https://mochajs.org/ -[Rust]: https://www.rust-lang.org/ +[github]: https://github.com/ +[fork]: https://git-fork.com/ +[ember.js]: https://emberjs.com/ +[jest]: https://jestjs.io/ +[mocha]: https://mochajs.org/ +[rust]: https://www.rust-lang.org/ [cargo]: https://doc.rust-lang.org/cargo/ [proptest]: https://github.com/altsysrq/proptest/ -[ESLint]: https://eslint.org/ -[Prettier]: https://prettier.io/ +[eslint]: https://eslint.org/ +[prettier]: https://prettier.io/ [clippy]: https://github.com/rust-lang/rust-clippy [rustfmt]: https://github.com/rust-lang/rustfmt [pyflakes]: https://github.com/PyCQA/pyflakes [black]: https://github.com/ambv/black -[TravisCI]: https://travis-ci.com/ -[GitLab]: https://gitlab.com/ -[CircleCI]: https://circleci.com/ -[AppVeyor]: https://www.appveyor.com/ +[travisci]: https://travis-ci.com/ +[gitlab]: https://gitlab.com/ +[circleci]: https://circleci.com/ +[appveyor]: https://www.appveyor.com/ [npm]: https://npmjs.com/ [yarn]: https://yarnpkg.com/ [hex]: https://hex.pm/ [bundler]: https://bundler.io/ -[Greenkeeper]: https://greenkeeper.io/ +[greenkeeper]: https://greenkeeper.io/ [dependabot]: https://dependabot.com/ [lerna-changelog]: https://github.com/lerna/lerna-changelog/ [github-label-sync]: https://github.com/Financial-Times/github-label-sync/ diff --git a/_posts/2018-12-10-assert-your-style.md b/_posts/2018-12-10-assert-your-style.md index 35a6a11c71..d513eea212 100644 --- a/_posts/2018-12-10-assert-your-style.md +++ b/_posts/2018-12-10-assert-your-style.md @@ -1,10 +1,10 @@ --- -title: "Assert Your Style - Testing CSS in Ember Apps" -author: "Jessica Jordan" +title: 'Assert Your Style - Testing CSS in Ember Apps' +author: 'Jessica Jordan' github: jessica-jordan twitter: jjordan_dev -bio: "Senior Frontend Engineer, Ember Learning core team member" -description: "Jessica Jordan explains approaches and patterns for testing styles in Ember.js applications." +bio: 'Senior Frontend Engineer, Ember Learning core team member' +description: 'Jessica Jordan explains approaches and patterns for testing styles in Ember.js applications.' topic: javascript og: image: /assets/images/posts/2018-12-10-assert-your-style/og-image.png @@ -52,6 +52,7 @@ Using `ember-test-selectors` to apply `data-` attributes to this component, you ```bash ember install ember-test-selectors ``` + Which now allows you to tag your component for testing as follows: ```js @@ -76,10 +77,10 @@ export default Component.extend({ }), }); ``` - To learn more about the rationale behind `ember-test-selectors`, be sure to also [give this introduction a read](/blog/2017/11/17/ember-test-selectors-road-to-1-0). -Using the [HTMLElement.style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) API, we can assert if the correct background color has been applied to our component: +To learn more about the rationale behind `ember-test-selectors`, be sure to also [give this introduction a read](/blog/2017/11/17/ember-test-selectors-road-to-1-0). +Using the [HTMLElement.style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) API, we can assert if the correct background color has been applied to our component: ```js // tests/integration/components/simplabs-logo-tile-test.js @@ -103,13 +104,13 @@ module('Integration | Component | simplabs-logo-tile', function(hooks) { `HTMLElement.style` allows to check for the value of **individual CSS properties**. The same API can also be used to assign new values for any CSS property programmatically. You can read more about [the style attribute in the MDN docs on HTMLElement.style here](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style). -A major **benefit** of testing **CSS properties** directly is that it allows us to derive information on how inline styles are applied to elements in our application. This is particularly useful if we want to test **dynamic styles**. +A major **benefit** of testing **CSS properties** directly is that it allows us to derive information on how inline styles are applied to elements in our application. This is particularly useful if we want to test **dynamic styles**. **Testing inline styles** also has its **drawbacks** though: these styles don't necessarily reflect the way the element is ultimately rendered on the page. In this situation the assertion against an element's **computed style** provides much more insight. ## Testing Computed Styles of a HTML Element -At times you also want to check against the actual **computed value** of your element in your tests. Some CSS definitions require the browser to resolve the basic computation, the actual computed styles will be applied during rendering. For example, in the case of an element with a percentage-based width defined via CSS stylesheets (e.g. `width: 80%`), the computed style will resolve to `width: 800px` if the element happens to be a relatively positioned (`position: relative`) child element of another DOM node with a computed width of `1000px`. If the element's parent element turns out to have a width of `500px` though, the computed width of the "80% wide" child will resolve to a mere `400px`. +At times you also want to check against the actual **computed value** of your element in your tests. Some CSS definitions require the browser to resolve the basic computation, the actual computed styles will be applied during rendering. For example, in the case of an element with a percentage-based width defined via CSS stylesheets (e.g. `width: 80%`), the computed style will resolve to `width: 800px` if the element happens to be a relatively positioned (`position: relative`) child element of another DOM node with a computed width of `1000px`. If the element's parent element turns out to have a width of `500px` though, the computed width of the "80% wide" child will resolve to a mere `400px`. Computed styles provide information about the styles that are **ultimately assigned** to an element. Due to [CSS specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity) browsers will decide which CSS property value either defined in a CSS stylesheet or an inline style is most relevant and this value will also be reflected in the element's computed styles. diff --git a/_posts/2018-12-20-factories-best-practices.md b/_posts/2018-12-20-factories-best-practices.md index 5ada69fbde..ab2fca1c54 100644 --- a/_posts/2018-12-20-factories-best-practices.md +++ b/_posts/2018-12-20-factories-best-practices.md @@ -1,10 +1,10 @@ --- -title: "Factories best practices" -author: "Andy Brown" +title: 'Factories best practices' +author: 'Andy Brown' github: geekygrappler twitter: geekygrappler topic: testing -bio: "Senior Frontend Engineer" +bio: 'Senior Frontend Engineer' description: "Andy Brown introduces best practices for using factories in order to make your colleagues' and your own future self's lives easier." --- @@ -27,20 +27,21 @@ In most applications there is often one gnarly model. In ecommerce applications The full API response fixture is the worst kind of fixture and it is a code smell. It means that rather than creating a declarative test that will help future developers (and your future self) understand what parts of this model are important for a particular test and how the model works, you get a 1,203 line json file. If I ever see a fixture like this while fixing a broken test, I have to replace it with a factory. I don't do this because I like creating extra work for myself, believe me, I don't. I do it because I don't understand what specific parts of that model were important to the test by looking at the fixture. In order to fix the test I need to figure out how the model works. I try to build a mental picture of the model and create the data for the test using a factory (often many factories pulled together), so it's clear and concise what parts of the `order` are required for the `orders with multiple deliveries arriving on the same day, only show 1 estimated delivery date not multiple` test. In this test (which is fantastically named), I can expect to see an `order` factory which has a `delivery` factory with more than one delivery, but all planned for the same day. All the other parts of the `order` model are likely not relevant to this test, so don't include them, or leave them as the defaults. Some pseudocode as we're so far into the post without a single line of it. ```js - test('orders with deliveries from different carriers arriving on the same day, only show 1 estimated delivery date', function(assert) { - let tomorrow = Date.tomorrow(); - let dhl = make('delivery', { estimatedDeliveryDate: tomorrow, carrier: 'dhl' }); - let ups = make('delivery', { estimatedDeliveryDate: tomorrow, carrier: 'ups' }); - let order = make('order', { deliveries: [dhl, ups] }); - assert.equal(order.estimatedDeliveryDates.length, 1, 'We should only show one delivery date') - assert.equal(order.estimatedDeliveryDates[0], tomorrow, 'The delivery date is correct') - }) +test('orders with deliveries from different carriers arriving on the same day, only show 1 estimated delivery date', function(assert) { + let tomorrow = Date.tomorrow(); + let dhl = make('delivery', { estimatedDeliveryDate: tomorrow, carrier: 'dhl' }); + let ups = make('delivery', { estimatedDeliveryDate: tomorrow, carrier: 'ups' }); + let order = make('order', { deliveries: [dhl, ups] }); + + assert.equal(order.estimatedDeliveryDates.length, 1, 'We should only show one delivery date'); + assert.equal(order.estimatedDeliveryDates[0], tomorrow, 'The delivery date is correct'); +}); ``` - ## Fun fun factories 🏭 This is the part where young me thinks old me is a boring loser. Don't give your factories fun names 🙅‍♂️. Often with factory libraries you will be able to have a default model, e.g. `user` and you'll be able to have named factories e.g. `specialUser`. What is quite fun is to have themed names for your factories, for example: + ```js //factories/user.js Factory.define({ @@ -50,14 +51,14 @@ Factory.define({ rob_stark: { name: 'Rob Stark', canInherit: true, - wolf: belongsTo('dire-wolf') + wolf: belongsTo('dire-wolf'), }, jon_snow: { name: 'Jon Snow', canInherit: false, - wolf: belongsTo('dire-wolf') - } -}) + wolf: belongsTo('dire-wolf'), + }, +}); ``` Now while it's fun to have Game of Thrones characters in your testing code base, it is a terrible practice. At the moment the difference between Jon and Rob is quite clear, and if I was writing a test about inheritance of Winterfell, it would be clear with a quick scan of the factory file, what the difference between Jon and Rob is, regardless of whether I've watched GoT or not. However, even though I know nothing about this ficticious GoT app, I can promise you one thing, the model will grow with time and the list of attrs will start to grow for both of our heroes. Let me give one scenario for what could happen: @@ -78,19 +79,29 @@ test('bastards cannot inherit', function(assert) { test('Leaders can raise armies', function(assert) { /* let's introduce someone who can tell us interesting things about Jon and Rob */ let threeEyedRaven = make('brandon_stark'); + assert.ok(threeEyedRaven.canRaiseArmies(rob), 'Noble of blood, strong of heart, Rob Stark can raise an army.'); assert.ok(threeEyedRaven.canRaiseArmies(jon), 'Principles command authority, Jon Snow can raise an army.'); }); //Added by me on day 102 -test('Jon is better than Rob', function (assert) { +test('Jon is better than Rob', function(assert) { let threeEyedRaven = make('brandon_stark'); - assert.ok(threeEyedRaven.isVitalToDefeatTheWhiteWalkers(jon), 'We need Jon Snow to defeat the White Walkers (TBC) 😍'); - assert.notOk(threeEyedRaven.isVitalToDefeatTheWhiteWalkers(rob), 'Rob Stark proved his irrelevance to defeating the White Walkers during the Red Wedding 🗡 😭'); + + assert.ok( + threeEyedRaven.isVitalToDefeatTheWhiteWalkers(jon), + 'We need Jon Snow to defeat the White Walkers (TBC) 😍', + ); + + assert.notOk( + threeEyedRaven.isVitalToDefeatTheWhiteWalkers(rob), + 'Rob Stark proved his irrelevance to defeating the White Walkers during the Red Wedding 🗡 😭', + ); }); ``` And now what our simple factories have morphed into: + ```js Factory.define({ default: { @@ -103,7 +114,7 @@ Factory.define({ isBlessedByRhlor: false, charisma: 10, fighting_ability: 10, - alive: false + alive: false, }, jon_snow: { name: 'Jon Snow', @@ -112,9 +123,9 @@ Factory.define({ isBlessedByRhlor: true, charisma: 9, fighting_ability: 10, - alive: true - } -}) + alive: true, + }, +}); ``` Now my example is a bit of fun, but my point is this: If you create wittily themed named factories, the attributes they contain will be unclear to everyone but you (and even to you on day 102). Often you will have a bare minimum number of attributes required for a certain model, e.g. every user must have a name and an email or the app will implode, and these things should live in the `default` factory. If you need to modify the default, and admin users are a good example of this, then keep the modification simple and make it obvious from the name what properties are changing. @@ -124,30 +135,33 @@ Now my example is a bit of fun, but my point is this: If you create wittily them Factory.define({ default: { name: 'A user', - email: 'user@example.com' + email: 'user@example.com', }, admin_user: { name: 'An Admin', email: 'admin@example.com', isAdmin: true, - } -}) + }, +}); ``` + But of course **you should not do this(!)**, even with sensible naming, because it will become a dumping ground for each attribute a test requires if it involves an admin user. Which leads me onto my final topic. ## Declarative factories -Don't use named factories or traits, they're an antipattern*. The admin user is again a good example where you might be tempted to use one of these. If your model is such that `isAdmin` boolean is all that is needed to make someone an admin then your test should be. +Don't use named factories or traits, they're an antipattern\*. The admin user is again a good example where you might be tempted to use one of these. If your model is such that `isAdmin` boolean is all that is needed to make someone an admin then your test should be. -* I think, I'm not really sure what an antipattern is, but humour me. +\* I think, I'm not really sure what an antipattern is, but humour me. ```js test('Admin users are taken to the dashboard on login', async function(assert) { let admin = make('user', { isAdmin: true }); + // Mock a request to the api and return our admin. this.server.get('user', () => [200, {}, admin]); + await visit('/'); assert.equal(window.location, '/admin-dashboard';) @@ -163,15 +177,16 @@ test('Admin users are taken to the dashboard on login', async function(assert) { permissions: ['view', 'edit', 'delete'], sudo: true }); + this.server.get('user', () => [200, {}, admin]); + await visit('/'); assert.equal(window.location, '/admin-dashboard';) }); ``` -If this list becomes quite long and it is used in a lot of places, and I mean very long and *a lot* of places, then maybe you could switch to a named factory or trait, but you would need to police it quite strictly and ensure that nothing else is added to that definition. What will happen is that developers add the attribute they need for their specific test to the definition, rather than writing it in the test, not realising that this attribute will now be created unneccessarily in all tests using this named factory or trait. This won't break those tests (usually), they'll be fine, but it leads to bloated factories and developers unaware of which attributes are strictly relevant to the test they're trying to fix. - +If this list becomes quite long and it is used in a lot of places, and I mean very long and _a lot_ of places, then maybe you could switch to a named factory or trait, but you would need to police it quite strictly and ensure that nothing else is added to that definition. What will happen is that developers add the attribute they need for their specific test to the definition, rather than writing it in the test, not realising that this attribute will now be created unneccessarily in all tests using this named factory or trait. This won't break those tests (usually), they'll be fine, but it leads to bloated factories and developers unaware of which attributes are strictly relevant to the test they're trying to fix. ## Don't use Mirage for tests - Ember bonus topic 🐹 @@ -186,6 +201,7 @@ In my opinion the main use for Mirage is as a rapid prototyping tool. It can als If you want to use Mirage in testing, and you write more than acceptance tests then you will need to use a ['hack' or 'workaround'](http://www.ember-cli-mirage.com/versions/v0.4.x/manually-starting-mirage/) to manually start and stop the mirage server during integration and unit tests. And you definitely should be writing integration and unit tests. #### 2. Test Clarity + Using Mirage in an acceptance test look like this. ```js @@ -200,9 +216,9 @@ test('The page shows me all the foos', async function(assert) { }); ``` -It's not bad, we are at least following AAA, but there is a step that is unclear, between adding foos to the server and them appearing on the page. The details of that are contained in Mirage's config file. Take a look at the equivalent test with Factory Guy for the data and [Pretender](https://github.com/pretenderjs/pretender) for mocking API calls (Mirage uses this under the hood*). +It's not bad, we are at least following AAA, but there is a step that is unclear, between adding foos to the server and them appearing on the page. The details of that are contained in Mirage's config file. Take a look at the equivalent test with Factory Guy for the data and [Pretender](https://github.com/pretenderjs/pretender) for mocking API calls (Mirage uses this under the hood\*). -*Factory Guy also uses pretender under the hood to mock API calls for certain helper functions that you can use with Factory Guy if you want. +\*Factory Guy also uses pretender under the hood to mock API calls for certain helper functions that you can use with Factory Guy if you want. ```js //acceptance/foo-index-test.js @@ -220,13 +236,14 @@ test('The page shows me all the foos', async function(assert) { }); ``` -Here we're being a little more explicit in the test. Put yourself in the shoes of a junior developer. The Mirage test shows me *more* Ember magic, even though they promised me there is a lot less magic now than there used to be in 2012. The factory test is more explicit, it tells us that visiting `/foos` url will trigger a `GET` request to `/api/foos` and we return a list of foos. It's a small difference but will help a junior to realise what +Here we're being a little more explicit in the test. Put yourself in the shoes of a junior developer. The Mirage test shows me _more_ Ember magic, even though they promised me there is a lot less magic now than there used to be in 2012. The factory test is more explicit, it tells us that visiting `/foos` url will trigger a `GET` request to `/api/foos` and we return a list of foos. It's a small difference but will help a junior to realise what ```js model() { return this.get('store').findAll('foo'); } ``` + is actually doing. #### 3. Duplication & Complexity @@ -242,4 +259,3 @@ I've been told it is always good to finish with a strong conclusion. ![Strong gif](/assets/images/posts/2018-12-20-factories-best-practices/strong.gif) I hope you enjoyed this post and will start using factories over fixtures and using named factories / traits more sparingly. - diff --git a/_posts/2019-02-08-ember-js-film-release.md b/_posts/2019-02-08-ember-js-film-release.md index 7116ecfc79..7833c5adcc 100644 --- a/_posts/2019-02-08-ember-js-film-release.md +++ b/_posts/2019-02-08-ember-js-film-release.md @@ -1,14 +1,14 @@ --- -title: "Ember.js: The Documentary - Amsterdam Premiere" -author: "Mark Coleman" +title: 'Ember.js: The Documentary - Amsterdam Premiere' +author: 'Mark Coleman' github: mrmrcoleman twitter: mrmrcoleman topic: ember -bio: "Communications and Community Outreach Specialist" -description: "Mark Coleman announces the release of \"Ember.js: The Documentary\" in which simplabs is featured among other well-known figures from the Ember.js community." +bio: 'Communications and Community Outreach Specialist' +description: 'Mark Coleman announces the release of "Ember.js: The Documentary" in which simplabs is featured among other well-known figures from the Ember.js community.' --- -We're very pleased to announce that simplabs are featured in the new [HoneyPot](https://www.honeypot.io/) film **'Ember: A Mini Documentary'** that will premiere in Amsterdam this evening (2019-02-08). +We're very pleased to announce that simplabs are featured in the new [HoneyPot](https://www.honeypot.io/) film **'Ember: A Mini Documentary'** that will premiere in Amsterdam this evening (2019-02-08). The film is a deep dive into the world of Ember.js and includes an interview with our CEO Marco Otte-Witte. After the screening there will also be a Q&A with Yehuda Katz & Special Guests! @@ -18,12 +18,12 @@ The premiere kicks off at 18:30 at [Pakhuis De Zwijger](https://dezwijger.nl/) a Here's the agenda from the meetup page: -* 18.30 - 19.00: Doors open + drinks & snacks! -* 19.00 - 19.15: Talk "From JavaScript Dabbler to Ember Book Author: My Journey" by Balint Erdi -* 19.15 - 19.30: Intro from Yehuda Katz & documentary makers Honeypot -* 19.30 - 20.00: Premiere of Ember.js: The Documentary -* 20.00 - 20.30: Q&A with Yehuda Katz & special guests -* 20.30 - 22.00: Networking with speakers & attendees +- 18.30 - 19.00: Doors open + drinks & snacks! +- 19.00 - 19.15: Talk "From JavaScript Dabbler to Ember Book Author: My Journey" by Balint Erdi +- 19.15 - 19.30: Intro from Yehuda Katz & documentary makers Honeypot +- 19.30 - 20.00: Premiere of Ember.js: The Documentary +- 20.00 - 20.30: Q&A with Yehuda Katz & special guests +- 20.30 - 22.00: Networking with speakers & attendees Grab a spot while you still can: [https://www.meetup.com/de-DE/Honeypot_Amsterdam/events/258262410/](https://www.meetup.com/de-DE/Honeypot_Amsterdam/events/258262410/) diff --git a/_posts/2019-02-11-ember-js-film-berlin.md b/_posts/2019-02-11-ember-js-film-berlin.md index f81c654820..2db4f48385 100644 --- a/_posts/2019-02-11-ember-js-film-berlin.md +++ b/_posts/2019-02-11-ember-js-film-berlin.md @@ -1,11 +1,11 @@ --- -title: "Ember.js: The Documentary - Berlin Screening" -author: "Mark Coleman" +title: 'Ember.js: The Documentary - Berlin Screening' +author: 'Mark Coleman' github: mrmrcoleman twitter: mrmrcoleman topic: ember -bio: "Communications and Community Outreach Specialist" -description: "Mark Coleman announces the screening of \"Ember.js: The Documentary\" in Berlin, Feb. 11th 2019." +bio: 'Communications and Community Outreach Specialist' +description: 'Mark Coleman announces the screening of "Ember.js: The Documentary" in Berlin, Feb. 11th 2019.' --- Following on from last week's premiere in Amsterdam the new [HoneyPot](https://www.honeypot.io/) film **'Ember: A Mini Documentary'** will be shown in Berlin this evening (2019-02-11). @@ -18,14 +18,14 @@ The event kicks off at 18:30 and there are still a few spots available. Here's the agenda from the meetup page: -* 18.30 - 19.00: Doors open + drinks & snacks! -* 19.00 - 19.15: Intro from documentary makers Honeypot -* 19.15 - 19.45: Screening of Ember.js: The Documentary -* 19.45 - 20.15: Jessica Jordan: Everything They Didn't Tell You About the Ember Community (+Q&A) -* 20.15 - 22.00: Networking, mingling, drinking and partying. +- 18.30 - 19.00: Doors open + drinks & snacks! +- 19.00 - 19.15: Intro from documentary makers Honeypot +- 19.15 - 19.45: Screening of Ember.js: The Documentary +- 19.45 - 20.15: Jessica Jordan: Everything They Didn't Tell You About the Ember Community (+Q&A) +- 20.15 - 22.00: Networking, mingling, drinking and partying. Grab a spot while you still can: [https://www.meetup.com/Honeypot_Berlin/events/258352778/](https://www.meetup.com/Honeypot_Berlin/events/258352778/) Check the trailer here: [https://www.youtube.com/watch?v=V0AC3Z1WIcc](https://www.youtube.com/watch?v=V0AC3Z1WIcc) -We'd like to thank HoneyPot for making this film and we hope the evening is a great success! \ No newline at end of file +We'd like to thank HoneyPot for making this film and we hope the evening is a great success! diff --git a/_posts/2019-03-07-march-monthly-update.md b/_posts/2019-03-07-march-monthly-update.md index a77434b982..70af7dd73e 100644 --- a/_posts/2019-03-07-march-monthly-update.md +++ b/_posts/2019-03-07-march-monthly-update.md @@ -1,11 +1,11 @@ --- -title: "simplabs monthly update - March 2019" -author: "Mark Coleman" +title: 'simplabs monthly update - March 2019' +author: 'Mark Coleman' github: mrmrcoleman twitter: mrmrcoleman topic: simplabs -bio: "Communications and Community Outreach Specialist" -description: "Mark Coleman shares simplabs' monthly update for March 2019, covering EmberConf 2019, the release of \"Ember.js: The Documentary\" and various upcoming events." +bio: 'Communications and Community Outreach Specialist' +description: 'Mark Coleman shares simplabs'' monthly update for March 2019, covering EmberConf 2019, the release of "Ember.js: The Documentary" and various upcoming events.' --- Welcome to our new monthly update. Each month we'll cover the events and activities that have been happening at simplabs. Enjoy. @@ -22,7 +22,6 @@ Before Ricardo joined the team we were pleased to sponsor him through the Ember. Our CEO Marco says _"We are very excited to welcome Ricardo Mendes to simplabs. Being a long-term member of the Ember Core Team, we have been in touch with him for years already. His deep knowledge and teaching ability will be hugely beneficial for our clients."_ - #### EmberConf 2019 ![EmberConf 2019](/assets/images/posts/2019-03-07-march-monthly-update/emberconf-logo.png) @@ -53,7 +52,6 @@ simplabs are involved in two upcoming meetups this month. On March 28th [Chris M On the same day in Munich [Marco](https://twitter.com/marcoow) is hosting the Elixir Munich meetup with talks from [Peer Stritzinger](https://twitter.com/peerstr), [Philipp Neugebauer](https://twitter.com/ppneugebauer) and Sami Buzz. Sign up on the meetup page [here](https://www.meetup.com/Elixir-Munich/events/259526263/). - #### Past Events February was a busy month for events. Jessica gave a talk at [c't webdev](https://twitter.com/ct_webdev) where she spoke about [Crafting Web Comics in Ember](https://ctwebdev.de/programm.html#slot-21). Also, Jessica is co-organized [Ember.js Berlin](https://www.meetup.com/Ember-js-Berlin/events/258984499/) on March 5th with [Rami Al-Rihawi](https://twitter.com/rrihawi_) and [Isaac Ezer](https://twitter.com/isaacezer). diff --git a/_posts/2019-03-13-elixir-umbrella-mox.md b/_posts/2019-03-13-elixir-umbrella-mox.md index 1a37792979..08682a838f 100644 --- a/_posts/2019-03-13-elixir-umbrella-mox.md +++ b/_posts/2019-03-13-elixir-umbrella-mox.md @@ -1,23 +1,23 @@ --- title: Elixir Umbrella Applications and Testing with Mox -author: "Niklas Long" +author: 'Niklas Long' github: niklaslong twitter: niklas_long topic: elixir -bio: "Backend Engineer, author of Breethe API" -description: "Niklas Long shows how Elixir Umbrella applications not only improve the organization of a code base but also allow for an improved testing setup." +bio: 'Backend Engineer, author of Breethe API' +description: 'Niklas Long shows how Elixir Umbrella applications not only improve the organization of a code base but also allow for an improved testing setup.' --- What's the big deal with Elixir umbrellas? -An Elixir umbrella is a container for mix apps; a structure useful to separate -the application's concerns as each app is contained within its own mix project. +An Elixir umbrella is a container for mix apps; a structure useful to separate +the application's concerns as each app is contained within its own mix project. -Why is this cool? +Why is this cool? Because it's like Lego and Lego is cool. -Who's Mox you ask? +Who's Mox you ask? Mox is cool too... Let's dive in! @@ -27,41 +27,43 @@ Mox is cool too... Let's dive in! Throughout this post, we will use [Breethe](https://breethe.app) as an example. Breethe is a Progressive Web App that gives users quick and easy access to air -quality data for locations around the world. Pollution and global warming are -getting worse. The first step to understanding and solving these problems is to -raise awareness by providing everyone with easy access to accurate data. +quality data for locations around the world. Pollution and global warming are +getting worse. The first step to understanding and solving these problems is to +raise awareness by providing everyone with easy access to accurate data. ![Video of the Breethe PWA](/assets/images/posts/2018-07-24-from-spa-to-pwa/breethe-video.gif) The application is [open source](https://github.com/simplabs/breethe-server) -and we encourage everyone interested to look through the source for reference. -The server for this application was implemented using an -[Elixir](https://elixir-lang.org) umbrella application which will be the focus +and we encourage everyone interested to look through the source for reference. +The server for this application was implemented using an +[Elixir](https://elixir-lang.org) umbrella application which will be the focus of this post. The client for Breethe was built with [Glimmer.js](http://glimmerjs.com), which we discussed in previous posts: + - [From SPA to PWA](/blog/2018/07/24/from-spa-to-pwa) - [Building a PWA with Glimmer.js](/blog/2018/07/03/building-a-pwa-with-glimmer-js/) -## Umbrella applications and separating concerns - -When we first started building Breethe, we asked ourselves a simple question -which would dictate the structure of the application and our motivation for -using an umbrella app to organise our code. This question was: what if we want -to change our air quality data provider? It turns out this wasn't just -speculation as we are now in the process of doing just that and our decision to -use an umbrella app will make the process tremendously easy. - -Using an umbrella allowed us to split our server into two very distinct -applications, communicating together by way of rigorously defined APIs. The -first application functions as the data handling entity of the project - named -_breethe_ (see below). It communicates with the air quality data provider -(a third-party API) to gather the data, then processes and caches it for future -use. The second application in the umbrella is the web interface built with -Phoenix - named _breethe_web_. It requests the data from the first application, -serializes it to JSON and delivers the payload to the client in compliance with +## Umbrella applications and separating concerns + +When we first started building Breethe, we asked ourselves a simple question +which would dictate the structure of the application and our motivation for +using an umbrella app to organise our code. This question was: what if we want +to change our air quality data provider? It turns out this wasn't just +speculation as we are now in the process of doing just that and our decision to +use an umbrella app will make the process tremendously easy. + +Using an umbrella allowed us to split our server into two very distinct +applications, communicating together by way of rigorously defined APIs. The +first application functions as the data handling entity of the project - named +_breethe_ (see below). It communicates with the air quality data provider +(a third-party API) to gather the data, then processes and caches it for future +use. The second application in the umbrella is the web interface built with +Phoenix - named _breethe_web_. It requests the data from the first application, +serializes it to JSON and delivers the payload to the client in compliance with the [JSON:API specification](https://jsonapi.org/). Here's an overview of the umbrella structure used for Breethe: + ``` apps ├── breethe @@ -80,18 +82,18 @@ apps └── test ``` -We have defined a clear boundary between the business logic and the webserver. -This is cool because the umbrella becomes modular like Lego and who doesn't like -Lego? Need to change the air quality data provider? No problem, simply change -the data application, leaving the webserver untouched as long as the data app -continues to implement the same interface. The same would work the other way +We have defined a clear boundary between the business logic and the webserver. +This is cool because the umbrella becomes modular like Lego and who doesn't like +Lego? Need to change the air quality data provider? No problem, simply change +the data application, leaving the webserver untouched as long as the data app +continues to implement the same interface. The same would work the other way round if we wanted to change the webserver. -However, for this approach to work well, the APIs used to communicate between -the different applications in the umbrella need to be carefully defined. We -want to keep the interfaces as little as possible to keep complexity contained. -As an example, here are the publicly available functions on the _breethe_ app -in the umbrella: +However, for this approach to work well, the APIs used to communicate between +the different applications in the umbrella need to be carefully defined. We +want to keep the interfaces as little as possible to keep complexity contained. +As an example, here are the publicly available functions on the _breethe_ app +in the umbrella: ```elixir # apps/breethe/lib/breethe.ex @@ -104,32 +106,32 @@ def search_locations(lat, lon), do: # ... def search_measurements(location_id), do: # ... ``` -Equally, these are the only functions the Phoenix web app (or any other app in -the umbrella) can call on the _breethe_ app. These principles are of course not -only applicable at the top level of the application but also within its -internal logical contexts. For example, within the _breethe_ app, we have -isolated the functions explicitly making requests to third-party APIs and -abstracted them away behind an interface. This, again, reduces complexity and -facilitates testing as we can isolate the different components of the business -logic. This philosophy lends itself very well to being tested using Mox. +Equally, these are the only functions the Phoenix web app (or any other app in +the umbrella) can call on the _breethe_ app. These principles are of course not +only applicable at the top level of the application but also within its +internal logical contexts. For example, within the _breethe_ app, we have +isolated the functions explicitly making requests to third-party APIs and +abstracted them away behind an interface. This, again, reduces complexity and +facilitates testing as we can isolate the different components of the business +logic. This philosophy lends itself very well to being tested using Mox. ## Testing domains independently using Mox -[Mox](https://github.com/plataformatec/mox), as the name suggests, is a library -that defines mocks bound to specific behaviours. A behaviour is a set of -function signatures that must be implemented by a module. Consequently, -Mox guarantees the mocks for a module be consistent with the original functions -they replace during testing. This rigidity makes the tests more maintainable and -requires that the behaviours for each module be meticulously defined; precisely -the qualities desired when implementing the APIs within our umbrella. - -For example, let's consider mocking the public API for the _breethe_ application -when testing _breethe_web_. As the bridge between the two is only composed of -the four functions shown in the previous section, mocking the _breethe_ -application's public interface when testing the webserver only requires mocking -those four functions. Naturally, this is only reasonable if we separately test -the _breethe_ application in full, from interface to database. Crucially, it is -the singularity of the interface which allows this degree of separation between +[Mox](https://github.com/plataformatec/mox), as the name suggests, is a library +that defines mocks bound to specific behaviours. A behaviour is a set of +function signatures that must be implemented by a module. Consequently, +Mox guarantees the mocks for a module be consistent with the original functions +they replace during testing. This rigidity makes the tests more maintainable and +requires that the behaviours for each module be meticulously defined; precisely +the qualities desired when implementing the APIs within our umbrella. + +For example, let's consider mocking the public API for the _breethe_ application +when testing _breethe_web_. As the bridge between the two is only composed of +the four functions shown in the previous section, mocking the _breethe_ +application's public interface when testing the webserver only requires mocking +those four functions. Naturally, this is only reasonable if we separately test +the _breethe_ application in full, from interface to database. Crucially, it is +the singularity of the interface which allows this degree of separation between the two applications in the umbrella both in testing and in production. Let's take a look at the controller action for a location search by id: @@ -150,12 +152,12 @@ end The interesting part is in the call to the _breethe_ application: -```elixir +```elixir |> @source.get_location() ``` -The reason we're using the `@source` module attribute is to be able to switch -between the mock and the real function defined on the _breethe_ application; +The reason we're using the `@source` module attribute is to be able to switch +between the mock and the real function defined on the _breethe_ application; this is defined in the config files: ```elixir @@ -166,15 +168,15 @@ config :breethe_web, source: Breethe config :breethe_web, source: Breethe.Mock ``` -By default `@source` points to the `Breethe` module - the _breethe_ -application's public API used in production and development. During testing it -switches to the `Breethe.Mock` module, which defines the mocks. +By default `@source` points to the `Breethe` module - the _breethe_ +application's public API used in production and development. During testing it +switches to the `Breethe.Mock` module, which defines the mocks. -The test for this controller action is meant to check two things. Firstly, that -the router redirects the connection to the appropriate controller action. -Secondly, that the controller action processes the call and queries the -_breethe_ application correctly using the right function defined on the -latter's API - in this case `get_location(location_id)`. +The test for this controller action is meant to check two things. Firstly, that +the router redirects the connection to the appropriate controller action. +Secondly, that the controller action processes the call and queries the +_breethe_ application correctly using the right function defined on the +latter's API - in this case `get_location(location_id)`. ```elixir # apps/breethe_web/test/breethe_web/controllers/location_controller_test.exs @@ -199,36 +201,44 @@ end I've broken it down into its four main parts: 1. It sets up the test data with [ExMachina](https://github.com/thoughtbot/ex_machina).

+ ```elixir location = insert(:location, measurements: []) ``` -2. It defines a mock in the `Breethe.Mock` module for the -`get_location(location_id)` function defined in the _breethe_ application's API -and sets the return value to the location we created at 1. The mock is passed -as an argument to the `expect` clause which verifies the mock is executed -during the test (instead of the real function).

+ +2. It defines a mock in the `Breethe.Mock` module for the + `get_location(location_id)` function defined in the _breethe_ application's API + and sets the return value to the location we created at 1. The mock is passed + as an argument to the `expect` clause which verifies the mock is executed + during the test (instead of the real function).

+ ```elixir Breethe.Mock |> expect(:get_location, fn _location_id -> location end) ``` -As long as we’ve established the callback in the behaviour implemented by the -Breethe module, we don’t need to explicitly define the Breethe.Mock module -(Mox creates it). Here's the callback for this particular function + +As long as we’ve established the callback in the behaviour implemented by the +Breethe module, we don’t need to explicitly define the Breethe.Mock module +(Mox creates it). Here's the callback for this particular function (for reference, it isn't coded in the test). + ```elixir # apps/breethe/lib/breethe.ex defmodule Behaviour do @callback get_location(location_id :: integer) :: %Breethe.Data.Location{} end ``` -3. It builds a connection and makes a call to the webserver's route designed to -handle a location search by id.

+ +3. It builds a connection and makes a call to the webserver's route designed to + handle a location search by id.

+ ```elixir conn = get(build_conn(), "api/locations/#{location.id}", []) ``` -4. It tests the JSON response (abridged for brevity) is correct by asserting on -the attributes of the location created in 1. and returned from the mock in 2.

+4. It tests the JSON response (abridged for brevity) is correct by asserting on + the attributes of the location created in 1. and returned from the mock in 2.

+ ```elixir assert json_response(conn, 200) == %{ "data" => %{ @@ -237,27 +247,27 @@ assert json_response(conn, 200) == %{ } ``` -Using mocks greatly simplifies the testing process. Each test can be smaller -and more specific. Each test is faster as we are not making calls to the -database or external systems; we are only running the anonymous functions that +Using mocks greatly simplifies the testing process. Each test can be smaller +and more specific. Each test is faster as we are not making calls to the +database or external systems; we are only running the anonymous functions that define the mocks. For instance, the mock in our example above only executes: ```elixir fn _location_id -> location end ``` -Finally, each mock is self-contained in the test defining it and the callback -insures the mock matches the original function signature. The result is robust, +Finally, each mock is self-contained in the test defining it and the callback +insures the mock matches the original function signature. The result is robust, fast and modularised tests. ## Conclusion -Elixir umbrella apps shine when structuring projects containing clear -boundaries between their constituent parts. The philosophy they implement -deeply resembles that of functional programming (and Lego), where small -building blocks combine into a larger whole. It is however important to be -precise when defining the internal APIs of the application as they act as the -glue holding everything together. Lastly, Mox is a wonderful tool for testing. -Not only does it make mocking APIs very simple and elegant, it also encourages -best practices such as defining behaviours to keep the code consistent and -robust. +Elixir umbrella apps shine when structuring projects containing clear +boundaries between their constituent parts. The philosophy they implement +deeply resembles that of functional programming (and Lego), where small +building blocks combine into a larger whole. It is however important to be +precise when defining the internal APIs of the application as they act as the +glue holding everything together. Lastly, Mox is a wonderful tool for testing. +Not only does it make mocking APIs very simple and elegant, it also encourages +best practices such as defining behaviours to keep the code consistent and +robust. diff --git a/_posts/2019-03-18-emberconf-update.md b/_posts/2019-03-18-emberconf-update.md index f54cb2f4e0..45c7693361 100755 --- a/_posts/2019-03-18-emberconf-update.md +++ b/_posts/2019-03-18-emberconf-update.md @@ -1,10 +1,10 @@ --- title: simplabs at EmberConf 2019 -author: "Mark Coleman" +author: 'Mark Coleman' github: mrmrcoleman twitter: mrmrcoleman topic: simplabs -bio: "Communications and Community Outreach Specialist" +bio: 'Communications and Community Outreach Specialist' description: "Mark Coleman gives an update on the simplabs team's presence at EmberConf 2019 and our involvement with many events at and around the conference." --- @@ -31,7 +31,7 @@ which promises to be a very fun and informative talk. Lastly, [Chris Manson](https://twitter.com/real_ate) will be giving a lightning talk about [empress-blog](https://github.com/empress/empress-blog). -If you're attending EmberConf 2019 and would like to speak to us about our +If you're attending EmberConf 2019 and would like to speak to us about our work, please [get in touch.](/contact/) See you there! diff --git a/_posts/2019-03-29-qonto-project.md b/_posts/2019-03-29-qonto-project.md index 5eb88f421e..3518880e10 100755 --- a/_posts/2019-03-29-qonto-project.md +++ b/_posts/2019-03-29-qonto-project.md @@ -1,11 +1,11 @@ --- title: Qonto project -author: "Mark Coleman" +author: 'Mark Coleman' github: mrmrcoleman twitter: mrmrcoleman topic: simplabs -bio: "Communications and Community Outreach Specialist" -description: "Mark Coleman announces our new client Qonto, a Paris based VC funded Fintech startup who are \"the ideal banking alternative for freelancers, startups and SMEs.\"" +bio: 'Communications and Community Outreach Specialist' +description: 'Mark Coleman announces our new client Qonto, a Paris based VC funded Fintech startup who are "the ideal banking alternative for freelancers, startups and SMEs."' --- We're very pleased to announce that we've started working with [Qonto](https://qonto.eu/), diff --git a/_posts/2019-04-05-april-monthly-update.md b/_posts/2019-04-05-april-monthly-update.md index f768c734c6..aefbd122d5 100644 --- a/_posts/2019-04-05-april-monthly-update.md +++ b/_posts/2019-04-05-april-monthly-update.md @@ -1,10 +1,10 @@ --- -title: "simplabs monthly update - April 2019" -author: "Mark Coleman" +title: 'simplabs monthly update - April 2019' +author: 'Mark Coleman' github: mrmrcoleman twitter: mrmrcoleman topic: simplabs -bio: "Communications and Community Outreach Specialist" +bio: 'Communications and Community Outreach Specialist' description: "Mark Coleman shares simplabs' monthly update for April 2019, covering new joiners, new clients and a number of upcoming events." --- @@ -72,4 +72,4 @@ simplabs sent a group of people to EmberConf2019 in Portland where Ricardo Mende #### Meetups -And last but not least, Marco hosted [Elixir Munich](https://www.meetup.com/Elixir-Munich/events/259526263/), Chris hosted [Ember.js Dublin](https://twitter.com/emberjsdublin/status/1101080708662132736) and [DublinJS](https://www.meetup.com/DublinJS/events/fbllfpyzgbdb/) and Jessica hosted [Ember.js Berlin!](https://www.meetup.com/Ember-js-Berlin/events/258984499/) +And last but not least, Marco hosted [Elixir Munich](https://www.meetup.com/Elixir-Munich/events/259526263/), Chris hosted [Ember.js Dublin](https://twitter.com/emberjsdublin/status/1101080708662132736) and [DublinJS](https://www.meetup.com/DublinJS/events/fbllfpyzgbdb/) and Jessica hosted [Ember.js Berlin!](https://www.meetup.com/Ember-js-Berlin/events/258984499/) diff --git a/_posts/2019-04-05-spas-pwas-and-ssr.md b/_posts/2019-04-05-spas-pwas-and-ssr.md index 69fa04e614..3442ea2df7 100755 --- a/_posts/2019-04-05-spas-pwas-and-ssr.md +++ b/_posts/2019-04-05-spas-pwas-and-ssr.md @@ -1,11 +1,11 @@ --- -title: "SPAs, PWAs and SSR" -author: "Marco Otte-Witte" +title: 'SPAs, PWAs and SSR' +author: 'Marco Otte-Witte' github: marcoow twitter: marcoow topic: javascript -bio: "Founding Director of simplabs, author of Ember Simple Auth" -description: "Marco Otte-Witte dives deep into different approaches for building web apps like SPAs, PWAs, SSR and how they all can be combined for the best result." +bio: 'Founding Director of simplabs, author of Ember Simple Auth' +description: 'Marco Otte-Witte dives deep into different approaches for building web apps like SPAs, PWAs, SSR and how they all can be combined for the best result.' og: image: /assets/images/posts/2019-04-05-spas-pwas-and-ssr/og-image.png --- @@ -53,7 +53,6 @@ that users get is often this: ![Video of a loading JS app](/assets/images/posts/2019-03-16-spas-pwas-and-ssr/loading.gif) - ## SPAs While JavaScript-heavy apps can be slow to start initially, the big benefit of @@ -117,16 +116,16 @@ that would be almost impossible to build with server side rendering, etc.) A better approach is to run the same single page app that is shipped to the browser on the server side as well as follows: -* the server responds to `GET` requests for all routes the single page app +- the server responds to `GET` requests for all routes the single page app supports -* once a request comes in, the server constructs an application state from the +- once a request comes in, the server constructs an application state from the request path and potentially additional data like a session cookie and injects that into the app -* it then executes the app and renders the app's UI into a string, leveraging +- it then executes the app and renders the app's UI into a string, leveraging libraries like [SimpleDOM](https://github.com/ember-fastboot/simple-dom) or [jsdom](https://github.com/jsdom/jsdom) -* that string is then served as the response to the browser's initial request -* the pre-rendered response still contains all `