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
0
200
ember: when the lights go on
My BSkyB Engineering Offsite presentation about our journey with Ember and beyond
Aaron Chambers
September 24, 2014
Tweet
Share
More Decks by Aaron Chambers
See All by Aaron Chambers
{{should-avoid this.renderModifiers butHow=true}}
achambers
0
190
one command to deploy them all
achambers
0
65
The Legend of Zelda: A {{link-to}} the past
achambers
0
66
ember-cli-deploy
achambers
0
91
git-branching
achambers
0
67
Other Decks in Programming
See All in Programming
Basic Architectures
denyspoltorak
0
670
AI & Enginnering
codelynx
0
110
高速開発のためのコード整理術
sutetotanuki
1
400
15年続くIoTサービスのSREエンジニアが挑む分散トレーシング導入
melonps
2
200
AIで開発はどれくらい加速したのか?AIエージェントによるコード生成を、現場の評価と研究開発の評価の両面からdeep diveしてみる
daisuketakeda
1
2.5k
Rust 製のコードエディタ “Zed” を使ってみた
nearme_tech
PRO
0
180
2026年 エンジニアリング自己学習法
yumechi
0
130
Lambda のコードストレージ容量に気をつけましょう
tattwan718
0
130
OCaml 5でモダンな並列プログラミングを Enjoyしよう!
haochenx
0
140
並行開発のためのコードレビュー
miyukiw
0
160
CSC307 Lecture 02
javiergs
PRO
1
780
CSC307 Lecture 05
javiergs
PRO
0
500
Featured
See All Featured
We Are The Robots
honzajavorek
0
160
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
170
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
3.6k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
55k
Code Reviewing Like a Champion
maltzj
527
40k
The Invisible Side of Design
smashingmag
302
51k
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
240
We Have a Design System, Now What?
morganepeng
54
8k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.7k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
1.9k
Writing Fast Ruby
sferik
630
62k
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
2
420
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