Deploying a
location-aware EmberJS app
Ben Limmer
EmberJS Denver Meetup
5/27/2015
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
deployment can be
scary
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
!==
Dev/Staging Production
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
data/servers can differ
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
data/servers can differ
db sharding
mini/uglification
caching
latency
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
us testing
>
users testing
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
dev/qa/stakeholder testing in prod
>
users testing in prod
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
enter ember-cli-deploy
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
– Michael Klein
“Lightning Fast Deployments of Ember-CLI
Apps”
LevelbossMike
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
– Ben Limmer
“Easy, Lightning Fast, Sleep-Better-at-Night,
Deployments of Ember-CLI Apps”
blimmer
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
how does it work?
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
“abc123”: “<html>...</html>”
what’s current?
contents of abc123
example.org
“current”: “abc123”
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
“def456”: “<html>...</html>”
app-hash.css, app-hash.js, ...
what’s current?
contents of abc123
“abc123”: “<html>...</html>”
example.org
“current”: “abc123”
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
“def456”: “<html>...</html>”
what’s def456?
contents of def456
“abc123”: “<html>...</html>”
example.org/?
secret=def456
“current”: “abc123”
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
“def456”: “<html>...</html>”
what’s current?
contents of def456
“abc123”: “<html>...</html>”
example.org
“current”: “def456”
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
💩!
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
“def456”: “<html>...</html>”
what’s current?
contents of abc123
“abc123”: “<html>...</html>”
example.org
“current”: “abc123”
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
demo project
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
Español English
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
ip address mexican user
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
ember-cli-server-variables
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
Client Stack
• EmberJS 1.12
• ember-i18n
• ember-cli-deploy (redis + s3 adapters)
• ember-cli-server-variables
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
client code
ember-cli-server-variables
config/environment.js
1 module.exports = function(environment) {
2 var ENV = {
3 modulePrefix: 'location-aware-ember',
4
5 serverVariables: {
6 tagPrefix: 'var',
7 vars: ['country']
8 }
9 };
10
11 if (environment === 'development') {
12 ENV.serverVariables.defaults = {
13 'country': 'US'
14 };
15 }
16 }
session initialization
1 export default Ember.Service.extend({
2 serverVariables: Ember.inject.service(),
3
4 countryCode: 'US',
5
6 forceInitialization: function () {
7 const country = this.get('serverVariables.country');
8 if (country) {
9 this.set('countryCode', country);
10 }
11 }
12 });
app/services/session.js
app initialization
app/routes/application.js
1 export default Ember.Route.extend({
2 i18n: Ember.inject.service(),
3 session: Ember.inject.service(),
4
5 beforeModel: function () {
6 this.set(
7 'i18n.locale',
8 languageForCountryCode(this.get('session.countryCode'))
9 );
10 }
11 });
languageForCountryCode
app/utils/language-for-country-code.js
1 export default function languageForCountryCode(countryCode) {
2 switch(countryCode.toLowerCase()) {
3 case 'es':
4 case 'mx':
5 return 'es';
6 case 'gb':
7 case 'us':
8 return 'en';
9 default:
10 return 'en';
11 }
12 }
translations (en)
app/locales/en/translations.js
1 export default {
2 'home': {
3 'greeting': 'Hello!',
4 'secondaryGreeting': 'Thank you for visiting from
{{countryName}}.'
5 }
6 };
translations (es)
app/locales/es/translations.js
1 export default {
2 'home': {
3 'greeting': ‘¡Hola!',
4 'secondaryGreeting': 'Gracias por visitar desde
{{countryName}}.'
5 }
6 };
template
app/templates/index.hbs
1 <div class='row'>
2 <h2 class='text-center' id="greeting">
3 {{t 'home.greeting'}}
4 </h2>
5 </div>
6 <div class='row'>
7 <h4 class='text-center' id="secondary-greeting">
8 {{t 'home.secondaryGreeting' countryName=countryName}}
9 </h4>
10 </div>
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
demo
Server Stack
Hosting
• heroku
• heroku-redis
Server Tech
• express 4
• geoip-lite
• redis
• cheerio
• node-ember-cli-
deploy-redis
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
server code
server routing
index.js
1 app.get('/', function(req, res) {
2 nodeEmberCliDeployRedis.
3 fetchIndex(‘location-aware-ember’, req, client).
4 then(function (indexHtml) {
5 indexHtml = serverVarInjectHelper.
6 injectServerVariables(indexHtml, req);
7 res.status(200).send(indexHtml);
8 }).catch(function(err) {
9 res.status(500).send('Oh noes!n' + err.message);
10 });
11 });
inject server variables
lib/server-var-inject-helper.js
1 var injectServerVariables = function (htmlString, req) {
2 var $ = cheerio.load(htmlString);
3 $('meta[name=var-country]').
4 attr('content', locationHelper.getCountry(req));
5
6 return $.html();
7 };
geocode ip address
lib/location-helper.js
1 var getCountry = function (req) {
2 var ipAddr = req.headers["x-forwarded-for"];
3 if (ipAddr){
4 var list = ipAddr.split(",");
5 ipAddr = list[list.length-1];
6 } else {
7 ipAddr = req.connection.remoteAddress;
8 }
9
10 var geo = geoip.lookup(ipAddr);
11
12 if (geo && geo.country) {
13 return geo.country;
14 } else {
15 return 'US';
16 }
17 };
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
running in prod
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
feature request:
change locale in realtime
git diff
++app/templates/index.hbs
1 <div class='row language-chooser'>
2 <h6>{{t 'home.chooseLang'}}</h6>
3 <a id='set-en' {{action 'setLanguage' 'en'}}>EN</a>
4 <a id='set-es' {{action 'setLanguage' 'es'}}>ES</a>
5 </div>
git diff
++app/routes/application.js
1 export default Ember.Route.extend({
2 i18n: Ember.inject.service(),
3 ...
4 actions: {
5 setLanguage: function (locale) {
6 this.set('i18n.locale', locale);
7 }
8 }
9 });
deloyment config
config/deploy.js
1 module.exports = {
2 "production": {
3 buildEnv: "production",
4 store: {
5 host: 'ec2-107-22-167-67.compute-1.amazonaws.com',
6 port: 6929,
7 password: process.env.REDIS_PW,
8 },
9 assets: {
10 "type": "s3",
11 "accessKeyId": "AKIAIFM7MT2JTIHV6HBA",
12 "secretAccessKey": process.env.S3_SECRET_ACCESS_KEY,
13 "bucket": "location-aware-ember"
14 }
15 }
16 };
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
demo
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
thank you!
blimmer
l1m5
hello@benlimmer.com
ember.party
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
demo project code
• location-aware-ember
• location-aware-ember-server
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
demo project libs (client)
• ember-cli-deploy
• s3 adapter
• redis adapter
• ember-cli-server-variables
• ember-i18n
Ben LimmerEmberJS Meetup - 5/27/2015 ember.party
demo project libs (server)
• express js
• geoip-lite
• redis
• cheerio
• node-ember-cli-deploy-redis

Deploying a Location-Aware Ember Application