Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ember: when the lights go on
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Aaron Chambers
September 24, 2014
Programming
210
0
Share
ember: when the lights go on
My BSkyB Engineering Offsite presentation about our journey with Ember and beyond
Aaron Chambers
September 24, 2014
More Decks by Aaron Chambers
See All by Aaron Chambers
{{should-avoid this.renderModifiers butHow=true}}
achambers
0
200
one command to deploy them all
achambers
0
70
The Legend of Zelda: A {{link-to}} the past
achambers
0
73
ember-cli-deploy
achambers
0
93
git-branching
achambers
0
78
Other Decks in Programming
See All in Programming
「Linuxサーバー構築標準教科書」を読んでみた #ツナギメオフライン.7
akase244
0
1.4k
GoogleCloudとterraform完全に理解した
terisuke
1
160
NakouPAY説明用
annouim0
0
270
実用!Hono RPC2026
yodaka
2
270
TiDBのアーキテクチャから学ぶ分散システム入門 〜MySQL互換のNewSQLは何を解決するのか〜 / tidb-architecture-study
dznbk
1
190
Lightning-Fast Method Calls with Ruby 4.1 ZJIT / RubyKaigi 2026
k0kubun
3
1.3k
(Re)make Regexp in Ruby: Democratizing internals for the JIT
makenowjust
3
720
YJITとZJITにはイカなる違いがあるのか?
nakiym
0
250
アーキテクチャモダナイゼーションとは何か
nwiizo
19
5.5k
10年分の技術的負債、完済へ ― Claude Code主導のAI駆動開発でスポーツブルを丸ごとリプレイスした話
takuya_houshima
0
2.6k
GNU Makeの使い方 / How to use GNU Make
kaityo256
PRO
16
5.6k
Terraform言語の静的解析 / static analysis of Terraform language
wata727
1
110
Featured
See All Featured
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
540
The Spectacular Lies of Maps
axbom
PRO
1
710
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
270
KATA
mclloyd
PRO
35
15k
Darren the Foodie - Storyboard
khoart
PRO
3
3.3k
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
180
What's in a price? How to price your products and services
michaelherold
247
13k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.2k
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
150
Discover your Explorer Soul
emna__ayadi
2
1.1k
VelocityConf: Rendering Performance Case Studies
addyosmani
333
25k
Transcript
ember:&when&the&lights&go&on @grandazz github.com/achambers
None
how$ember$saved$my$life @grandazz github.com/achambers
how$ember$saved$my$life @grandazz github.com/achambers
ember:&when&the&lights&go&on @grandazz github.com/achambers
Wenguins
None
Noah%update
None
this%is%not an#ember#is#be(er#than#everything#else presenta(on
browser'dev'boiled'down (shamelessley)borrowed)from)@ryanflorence) interac(ng*with*data*stores rendering'data'to'ui responding*to*user*interac/on router&&&urls project(stuff:(file(organisa3on,(build(tools(etc
None
our$journey$with ember
lightbulb!moments
we#didn't#just#jump#in
we#needed#to validate the$switch
None
None
Service'Status
None
// app/assets/javascripts/application.js function bindViewMoreDetailsLinks() { $('.more-details').hide(); $('.view-more').click(function(e) { e.preventDefault(); var
viewMoreLink = $(this); var moreDetails = viewMoreLink.parents('.issue').find('.more-details'); if (moreDetails.is(':visible')) { moreDetails.hide(); viewMoreLink.text('More details'); } else { moreDetails.show(); viewMoreLink.text('Less details'); } viewMoreLink.toggleClass('opened'); }); }
// app/assets/javascripts/application.js function toggleElementsForEditors() { $.ajax({ url: '/sessions/current', dataType: 'json',
success: function(data) { var loggedInStatus = data.logged_in; toggleNewIssueLink(loggedInStatus); toggleHeader(loggedInStatus); toggleAdditionalLinks(loggedInStatus); toggleSidebar(loggedInStatus); toggleFooter(loggedInStatus); toggleEditIssueLinks(loggedInStatus); toggleResolveIssueLinks(loggedInStatus); } }); }
// app/assets/javascripts/application.js function toggleHeader(loggedInStatus) { $('#skycom-masthead-wrapper').toggleClass('hidden', loggedInStatus); } function toggleAdditionalLinks(loggedInStatus)
{ $('.additional-links').toggleClass('hidden', loggedInStatus); } function toggleSidebar(loggedInStatus) { $('.sidebar').toggleClass('hidden', loggedInStatus); } function toggleFooter(loggedInStatus) { $('#skycom-footer-wrapper').toggleClass('hidden', loggedInStatus); } function toggleEditIssueLinks(loggedInStatus) { $('.btn.edit-issue').toggleClass('hidden', !loggedInStatus); } function toggleResolveIssueLinks(loggedInStatus) { $('.btn.resolve-issue').toggleClass('hidden', !loggedInStatus); }
None
data$based$ui$manipula.on
// app/views/issues/index.html.haml function microfilterCheck() { var deferred = $.Deferred(); $.getJSON('/sessions/current',
function(json) { if (json.logged_in) { deferred.resolve() } else { Hub.runMicrofilterTest() .done(function() { deferred.reject(); }) .fail(function() { deferred.resolve(); }); } }); return deferred; }
// app/views/issues/index.html.haml function restrictionCheck() { var deferred = $.Deferred(); $.getJSON('/customers/current',
function(json) { if (json.customer.has_debt_restriction) { deferred.reject('debt'); } else if (json.customer.has_high_spend_restriction) { deferred.reject('spend'); } else { deferred.resolve(); } }, function() { deferred.resolve(); }); return deferred; }
// app/views/issues/index.html.haml $(function() { var microfilterDeferred = microfilterCheck() var restrictionDeferred
= restrictionCheck() $.when(microfilterDeferred, restrictionDeferred).always(function() { var args = Array.prototype.slice.call(arguments); if (restrictionDeferred.state() == 'rejected') { if (args.indexOf('spend') !== -1) { $('.high-spend-restriction-alert').removeClass('hidden'); } else if (args.indexOf('debt') !== -1) { $('.debt-restriction-alert').removeClass('hidden'); } } else if (microfilterDeferred.state() == 'rejected') { $('.microfilter-alert').removeClass('hidden'); } showIssues(); }); });
then%the%very%next%commit...
// app/views/issues/index.html.haml $(function() { IssuesIndexController.init(); });
None
controllers
// app/assets/javasctipts/User.js var User = { isAnEditor: function() { var
deferred = $.Deferred(); $.ajax({ dataType: 'json', url: '/sessions/current', success: function(data) { deferred.resolve(data.logged_in); }, error: function() { deferred.reject(); } }); return deferred.promise(); } }
// app/assets/javascripts/Diagnostics.js var Diagnostics = { run: function() { //run
some diagnostics goodness } } // app/assets/javascripts/CBS.js var CBS = { run: function() { //run some monstrosity of a service, //return a massive payload we don't need //and take forever to do so } }
None
models & interac(ng*with*data*stores
// app/assets/javascripts/views/IssueView.js (function() { window.IssuesView = { render: function() {
this._showIssues(); this._toggleStatusMessage(); } // ...snip... }; })(); // app/assets/javascripts/views/DebtAlertView.js (function() { window.DebtAlertView = { render: function() { this._showAlert(); this._hideProducts(); this._hideSidebar(); this._informScreenReader(); } // ...snip... }; })();
None
views
{!-- app/assets/javascripts/templates/outages.hbs --}} {{#each outages}} <article class="issue"> <h3>{{description}}</h3> <p> <span
class="label">Date issue was reported</span> </br> <strong>{{startedOn}}</strong> </p> </article> {{/each}}
None
templa'ng
we#started#to#build#our#own#framework
we#started#to#build#our#own#framework BADLY
Use$a$framework,$or$don't.$Either$ way,$you$will. —"Someone"Awesome
conven&on'over'configura&on this.resource('posts', { path: '/posts' }) PostsRoute PostsController PostsView posts.hbs
no#more#globals var Diagnostics = { run: function() { // do
stuff } }; window.IssuesIndexController = { init: function() { // do stuff; } };
no#more#globals ServiceStatus.GoIssuesController = Em.ArrayController.extend({ status: function() { //do stuff }.property('hasProblem')
}); ServiceStatus.Outage = DS.Model.extend({ //attributes here }); ServiceStatus.SsAccordionComponent = Em.Component.extend({ collapsed: false, //other properties here });
data$binding // models/tv_issue.js ServiceStatus.TvIssue = DS.Model.extend( name: DS.attr('string') ); //
controllers.tv_issues.js ServiceStatus.TvIssuesController = Em.ArrayController.extend({ }); <!-- templates/tv_issues.hbs --> <ul> <#each> <li>{{name}}</li> </each> </ul>
lightbulb(moments didn't&stop there
None
None
leave%your%assump-ons at#the#door
None
None
None
None
None
None
None
don't&fight&the&framework
If#something#feels#harder#than#it# should#be,#it#probably#is.#You're# probably#doing#it#wrong. —"Someone"Awesome
None
None
ui#error#logging
ui#error#logging Ember.onerror = handleError; Ember.RSVP.on('error', handleError); function handleError(error) { Em.$.ajax('/error',
{ type: 'POST', data: { stack: error.stack } }); }
None
None
build&tools
ember%cli npm install -g ember-cli
ember%cli file$organisa,on ember new my-project - /app - /components -
/controllers - /helpers - /models - /routes - /styles - /templates - /views - app.js - index.html - router.js - /config - /public - /tests
ember%cli es6$modules
ember%cli tes$ng
ember%cli asset%pipeline
ember%cli generators
ember%cli h"p$mock)server
ember%cli addons
None
None
separa&on)of ui#and#api
clearer&separa)on&between&data and$user$interac,on
think¬&of&your&api&in&terms&of&ui& interac2ons,&but&in&terms&of&data& and&rela2onships
your%app%is%a client'of'your'api
None
None
deployment
deploying*to*heroku and$serving$from$rails is#cool#and#all
but$we've$been$doing$that$for a"long"'me
we#can#do be#er
None
None
index.html app#1234.js app#5678.css
None
lets%push%this one$step$further
None
None
deployment*becomes*super*quick
just%push%some%sta,c%assets%to%S3 and$push$index.html$into$redis
not$to$men'on$cu*ng$over
None
None
pact%file%tes*ng
how$do$we$test$that$changes to#the#api
how$do$we$test$that$changes to#the#api don't&break&the&ui?
pact%(noun) A%formal%agreement%between% individuals%or%par8es. —%The%Google
the$rela(onship$of$the$web$app$consists$of: the$consumer$(ui) the$provider$(api)
Step%1%'%Define%the%consumer%expecta4ons
Step%2%'%Verify%expecta0ons%on%provider
imagine'if CBS verified'their'services'against'our'pact'files every%&me%they%released?
None
Thats&a&wrap
@grandazz github.com/achambers
any$ques)ons? @grandazz github.com/achambers
thankyou