This repository was archived by the owner on Nov 18, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathindex.html
More file actions
839 lines (677 loc) · 51.4 KB
/
Copy pathindex.html
File metadata and controls
839 lines (677 loc) · 51.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta content='IE-edge,chrome=1' http-equiv='X-UA-Compatible'>
<meta content='width=device-width, initial-scale=1.0' name='viewport'>
<title>
Blurb Engineering Blog
</title>
<link href="/stylesheets/all.css" media="screen" rel="stylesheet" type="text/css" />
<script src="/javascripts/all.js" type="text/javascript"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-283660-36', 'blurb.github.io');
ga('send', 'pageview');
</script>
</head>
<body class='index'>
<header class='header sticky' data-min-height='100' data-starting-height='200'>
<nav class='top-bar' data-topbar>
<ul class='title-area'>
<li class='name'>
<h3>
<a href="/"><img alt="Blurb Engineering Blog" src="/images/logo.png" />
</a>
</h3>
</li>
</ul>
<section class='top-bar-section'>
<ul class='nav-links'>
<li>
<a href="http://www.github.com/blurb">Github</a>
</li>
<li>
<a href="/blog">Blog</a>
</li>
</ul>
</section>
</nav>
<div class='row'>
<div class='columns large-8 large-centered'>
<h1>Blurb Engineering Blog</h1>
</div>
</div>
</header>
<section id='main'>
<section id='main'>
<div class='row'>
<div class='columns small-12'>
<div class='articles'>
<article>
<h2>
<a href='/2014/12/01/starting-a-meetup-group.html'>
Starting a Meetup Group
</a>
</h2>
<h6>
By
<a href="https://twitter.com/chicagoing">Estella Madison</a>
(
<a href="http://www.twitter.com/chicagoing">@chicagoing</a>
)
<time>
December 1, 2014
</time>
</h6>
<p>In February 2014, after moving to Manhattan and being unable to find
a New York City network for <a href="https://www.womenwhocode.com/">Women Who Code</a>
(WWCode), I decided to start one. Ten months later, we have a strong <a href="meetup.com/Women-Who-Code-NYC/">NYC
network</a> with over 1500 members. We’re
growing by 50+ members a month and have no signs of losing steam.</p>
<h2 id="what-has-contributed-to-our-success">What has contributed to our success?</h2>
<p>We owe much of our success to our San Francisco-based parent
organization. They made introductions to technology companies with whom
they’d already built relationships. The Women Who Code brand was also a
big advantage. Many of our members and partners already know about our
organization, and thus are more likely to want to be involved. Not
every meetup group will have a parent organization, so this is very
unique.</p>
<p>Another important factor was having a co-founder. Starting a meetup
group is, of course, a huge commitment. With a co-founder, we can split
a lot of the work required to organize hosts, speakers, and all the
email correspondence that comes along with starting a network.</p>
<p>A strong and committed membership base has been vital. Our members want
to be involved and help. Of course, organizing volunteers (we prefer to
call them <em>event organizers</em>) is a lot of work, and luckily my co-founder
is great at this. She quickly scouted event organizers to run our
Front-End Developer Study Group, which is a popular weekly event.</p>
<h2 id="what-are-our-biggest-challenges">What are our biggest challenges?</h2>
<h3 id="finding-hosts">Finding hosts</h3>
<p>With up to six events a month, finding organizations to host our events
is possibly the most challenging aspect of running our meetup group.</p>
<p>We found the host for our first event by approaching the organizer of
another meetup group, <a href="https://twitter.com/lukemelia">Luke Melia</a> of
<a href="http://www.meetup.com/EmberJS-NYC/">Ember NYC</a>. He was generous and made
several introductions to the CTOs of NYC-based companies. This helped us
get the ball rolling.</p>
<p>Being a part of the wider Women Who Code network has obviously helped.
We get many introductions and leads from our parent organization, our
advisor <a href="https://twitter.com/sashalaundy">Sasha Laundy</a>, and affiliated
networks. Our members also help us find hosts—usually their employers or
friends.</p>
<p>Being part of the WWCode organization combined with our large membership
means many organizations <em>approach us</em> to host. While we welcome this,
we tread carefully. For instance, recruiting agencies and for-profit
educational institutions often want to host us. We always give them a
shot, but we ensure our members aren’t being targeted too heavily.
Additionally, we have to be picky about space. If the wifi was
unavailable for most of the meetup or the environment was simply not
conducive to productivity, we won’t have an event there again.</p>
<p>Cold calling, mainly on Twitter, is something we often do. We send
tweets to CTOs, recruiters, and software engineers at various companies.
This is most successful if the organization has special diversity
initiatives. For example, the WWCode CEO alerted us to the <a href="http://blog.fogcreek.com/announcing-the-fog-creek-fellowship/">Fog Creek
Fellowship</a>.
We sent a tweet to a top recruiter there and quickly booked them for a
Lightning Talks event. However, we’re not always this lucky. Tweets
sometimes go unanswered and we move on.</p>
<p><img alt="Joel Spolsky gave an entertaining presentation at the Lightning Talks that Fog Creek hosted" src="/images/2014/12/wwcnyc-joel-spolsky.jpg" /></p>
<p>LinkedIn has more recently proven to be an important resource. Both my
co-founder and I get lots of messages from recruiters. If the recruiter
works at a technology company (versus a recruiting agency), then we ask
if they might be interested in hosting an event for WWCode. While this
tactic has only worked 15% of the time, it’s garnered important partners
like Facebook and LinkedIn.</p>
<h3 id="finding-speakers">Finding speakers</h3>
<p>Finding speakers is similar to finding hosts. We find speakers through
our parent organization, who often recommend potential speakers or make
introductions. We also encourage our members to speak, especially those
with interesting side projects or aspirations to become speakers. We
always invite our hosts to have someone from their organization speak.
We do our best to keep tabs on people we read about or meet at
conferences or other meetups, particular those that are NY-based. And
sometimes women approach us to speak at our events.</p>
<p>In addition, our hosts have been instrumental in finding speakers. For
example, last Fall we teamed up with Condé Nast for a panel called
“<a href="http://www.meetup.com/WomenWhoCodeNYC/events/180115572/">Finding Your Voice and Bringing Ideas to Life</a>.”
They found the moderator and most of the panelists.</p>
<h2 id="some-tips">Some tips</h2>
<ul>
<li>Get to know your meetup attendees. I ask about the type of
programming they’re doing, their interests, their personal projects,
and if they have any feedback.</li>
<li>Find a co-founder if at all possible to share responsibilities.</li>
<li>Fill up your meetup calendar with events at least six months out.
This gives the impression that your group is active and helps potential
members determine if the group is right for them.</li>
<li>Book hosts well ahead of time. There’s nothing more stressful than
trying to find a host for an event that’s just a few days away.</li>
<li>Always have food at your events. Otherwise, people will arrive late
or leave early to get dinner.</li>
<li>Open up RSVPs about 20 days before the date of your event. We made
the mistake of allowing people to register for all of the events on
our calendar, including those six months out. These people simply
forgot about the events, resulting in lower attendance rates.</li>
<li>Do a head count at every event to find patterns and determine your
average attendance rate. 50% is typical for us, but it also depends
on the host. A meetup at Facebook or Fog Creek will have a higher
attendance rate than one at a lesser known organization. We also
found that our attendance rates dip in the Fall and Winter, probably
due to the colder weather and shorter days.</li>
<li>When approaching potential hosts, keep your email message short
and to the point. We have a standard template that includes the
requirements for hosting (space, food, and internet for 20–50 women),
a list of upcoming events that need hosts, and a sample schedule.</li>
</ul>
<p>Starting the Women Who Code NYC network is perhaps the most rewarding
experience I’ve ever had. It’s a lot of work and while it can be
tiresome, helping women improve their programming and
tech skills is amazing. I’ve learned a lot about time management
and event organization, and have met amazing people along the way.</p>
<blockquote class="twitter-tweet" lang="en"><p>Congrats <a href="https://twitter.com/chicagoing">@chicagoing</a> on tonight's <a href="https://twitter.com/WomenWhoCodeNYC">@womenwhocodenyc</a> event! <a href="https://twitter.com/hashtag/womenwhocodenyc?src=hash">#womenwhocodenyc</a> <a href="https://twitter.com/hashtag/changetheratio?src=hash">#changetheratio</a> <a href="https://twitter.com/hashtag/morevoices?src=hash">#morevoices</a> <a href="http://t.co/sJhDGOO7dF">pic.twitter.com/sJhDGOO7dF</a></p>— NataliaObertiNoguera (@nakisnakis) <a href="https://twitter.com/nakisnakis/status/486668956145840129">July 9, 2014</a></blockquote>
<script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<h5>
Tagged: community
</h5>
</article>
<article>
<h2>
<a href='/2014/11/20/environment-customizations.html'>
Environment Customizations
</a>
</h2>
<h6>
By
Tim Herd
(
<a href="http://www.twitter.com/eqdw">@eqdw</a>
)
<time>
November 20, 2014
</time>
</h6>
<h4 id="environment-customizations">Environment Customizations</h4>
<p>You're at your friend's house, chillin', maybe watching something on the
TV, when suddenly you're hit with inspiration. "OH MY GOD, THAT'S THE
PERFECT STARTUP IDEA!" you think. Visions of Sand Hill dance through
your head as you envision the world changed by your brilliant new idea
for a social network for crows, but there is one problem. You left your
computer at home. You ask to borrow your buddy's machine, but you find
it unusable. You sit there, staring blankly at a command line, filled
with gibberish.</p>
<p><img alt="Command line gibberish" src="/images/2014/11/commandline.png" /></p>
<p>You turn to your friend. "Uhhhh…. how do I get an editor?" A flurry of
random letters, and you're in vim. But something's wrong. DVORAK!</p>
<p>Has this ever happened to you? If so, you've fallen victim to someone
else's over-customized environment! But don't despair. I'm here to show
you some useful ones.</p>
<h4 id="why-customize-or-vanilla-bash-oughtta-be-good-enough-for-everyone">Why Customize? (or, Vanilla Bash Oughtta Be Good Enough For Everyone)</h4>
<p>Three reasons: efficiency, comfort, and developer happiness. All three of
these come together to let you be more productive. Having an
environment that you're comfortable in, that empowers you to do things
more easily, and that removes your pain points, will help you in the
long run. A well-configured environment will give you two kinds of
efficiencies:</p>
<p>The first kind of efficiency is the straightforward automation
efficiency that engineers work all day to create for businesses. It's
the kind of automation talked about in one of my favourite XKCDs:</p>
<p><a href="http://xkcd.com/1205/"> <img alt="Is it worth the
time?" src="http://imgs.xkcd.com/comics/is_it_worth_the_time.png" /></a></p>
<p>You might think it is a waste of time to customize your tools. Maybe you
think you're getting things done fast enough as it is. But you're
probably blind to just how many times you do a given thing. Even if you
shave 10 seconds a day off of your interactions with Git, how many times
do you commit in a day? (hint: it should be a lot). Take, for example,
my usage graph from <a href="http://www.alfredapp.com/">Alfred</a> (more on this
later)</p>
<p><img alt="Twenty Times A Day" src="/images/2014/11/alfred.png" /></p>
<p>According to the XKCD graph, using Alfred 20 times a day, assuming the
smallest possible time gain (1 second), entitles me to work on it for
approximately 10 hours before I break even. Consider, then, that I've
been carrying most of these customizations with me since college, and
I've already shaved a <em>full day</em> off of executing programs that the rest
of you are spending clicking through Finder. </p>
<p>However, the second kind of efficiency is the more important one. I'm
sure that you've heard of "flow" before.
<a href="http://en.wikipedia.org/wiki/Flow_(psychology)">Flow</a> is the state of
mind in which you're fully engaged on the task at hand, bringing to bear
the full might of your impression mental faculties. Flow is the
difference between killing an hour on
<a href="http://reddit.com/r/roombaww">r/roombaww</a> waiting for 5PM, and missing
your train because woah how'd it get to be 8PM, oh heck did I forget to
eat dinner again? Flow is where you do your best work, and as an
engineer, you should prioritize getting into it as much as possible,
every day. </p>
<p>One of the biggest enemies of flow is interruption. Ask most engineers
about this, and they're likely to tell you about <a href="http://www.paulgraham.com/makersschedule.html">all the ways in which
Management interrupts them and wastes their
time</a>. But this is not
the most common interruption. The most common interruptions are the
small ones. The five- or ten-second cognitive delays you have to make in
tricky situations when you drop out of autopilot and have to think
"<a href="https://twitter.com/iamdevloper/status/529623481592586242">What's the syntax for ln
again?</a>".
Seriously. Every single time I'm making a symlink I have to do the same
thing:</p>
<p><img alt="Source Target" src="/images/2014/11/symlink-syntax.png" /></p>
<p>And then, I <a href="2014/11/cat.jpg">cat</a> both foo and bar
just to make absolutely sure that they both contain "foo".
"That's right. It's source <em>then</em> target. I'll never forget again. Until
next week.</p>
<p>By customizing your environment, you can remove as many of those
potential future ln -s's out of your path right now. You can replace
long, involved commands with shorter, more automatic keystrokes. You
can make aliases for complex or often-error'd interactions. Each of
these steps may on its own seem pointless, but the sum of all the
individual optimizations is a more frictionless working environment. </p>
<h4 id="alright-show-me-what-you-got">Alright. Show Me What You Got</h4>
<p>By now you should be sold on the virtues of making your environment your
own. By the time you're done with it, it should be as comfortable as
Homer Simpson's Butt Groove</p>
<p><img alt="Butt Groove" src="/images/2014/11/butt-groove.jpg" /></p>
<p>But where should you start? Well, I started by borrowing from my
friend's customizations, so in the interest of paying it forward, I'll
share my l33t environment haxxz. </p>
<p>Unless otherwise stated, all of the following customizations are
available at <a href="http://github.com/eqdw/dotfiles">my Dotfiles repo on
Github</a></p>
<h4 id="the-command-prompt">The Command Prompt</h4>
<p>I use a custom prompt generator for my prompt. This is primarily because
implementing it in shell script was too slow. However, this has made it
portable, and as I've moved from Bash to Zsh to Fish, this has been
helpful. </p>
<p>My terminal is generated by a program called <a href="http://github.com/eqdw/wunderprompt">Wunderprompt, available on
my Github</a>. </p>
<p><img alt="Example prompt" src="/images/2014/11/example-prompt.png" /></p>
<p>This is an example of my prompt. There is a lot of information packed
into this.</p>
<p>First, "source". This is the name of the current directory. It is colour
coded based on which server I'm on. I currently have it set up as
follows: <span style="color:blue">Blue</span> denotes my work machine.
<span style="color:yellow">Yellow</span> is my personal laptop. <span style="color:magenta">Magenta</span> is the terminal on my website, and
everything else defaults to <span style="color:red">Red</span>. Ever
accidentally run a command on prod? I won't; I can check the colour of
the path. </p>
<p>The next section is git info. It begins with a nag counter. The red 2h
means it has been 2 hours since my last commit. This starts as a green 0
and slowly counts up minutes. At 30 minutes it turns yellow. Two hours,
red. Above two hours it stops displaying minutes and starts displaying
the count in hours. And above 24 hours, in days. A friendly reminder to
commit early and often.</p>
<p>Next, 'env-post', refers to the current branch. This will be * for
master, and otherwise the branch name. In this case, I'm on the env-post
branch of our blog repo. The branch name will be green for a clean
branch, and magenta otherwise.</p>
<p>Speaking of branch cleanliness, env-post is magenta. The reason is the
next few characters. They give me an at-a-glance idea of the current
branch status. The D means a file was deleted. The A means a file was
added. The ? means there are unstaged changes. And the 7 counts the
total number of changes that a <code>git status</code> would return. </p>
<p>The last element of the prompt is the %. This tells me I'm <em>not</em> a
superuser. If I was logged in as root, it would be a # instead. It's
green. This means the last command was successful. If it was not, it
would be red, and prefixed with the exit code from the failing command. </p>
<h4 id="shell-customizations">Shell Customizations</h4>
<p>I use the <a href="http://fishshell.com/">Fish shell</a> for my work. This is a
shell with a much friendlier working environment. On the one hand, this
means it's not encumbered by 30 years of UNIX baggage. On the other
hand, it is <em>not</em> Bash compatible, so I often have to debug little
issues with install scripts. In any case, I have a large number of shell
modifications , which you can find in ` .config/fish/fish.config ` in my
dotfiles repo. I'd like to highlight a few of my more common
optimizations here</p>
<p>Near the top, I have a 'misc' section. It has some useful commands</p>
<p><code>function def</code> Used as <code>% def foo</code>, this does a full text search of the current dir and
all subdirs for a ruby method definition of the passed in method (in
this case, 'foo'). Useful to find where some code is defined</p>
<p><code>function pyserv</code> This creates a simple http server in the current
directory, serving up the files. It has its uses, if nothing else, as an
easy way to fileshare on a local network. </p>
<p><code>function psag</code> This runs the process list and searches it for a
given string.</p>
<p><code>the 'u' functions</code> "cd .." is a weird way to go <em>u</em>p one level. "cd
../../../../../.." is a weird way to go <em>uuuuuu</em>p six levels</p>
<p><code>tarx</code> and <code>tarc</code> Tar, like ln, is lost arcana, known only to the last
remaining Level 27 Unix Masters. Well, until I wrote down the most
common way to <em>c</em>reate and e<em>x</em>tract tarballs</p>
<p><code>function ls</code> I override ls with gnu ls and several custom flags.</p>
<p>Without this:
<img alt="Boring ls" src="/images/2014/11/boring-ls.png" /></p>
<p>With this:
<img alt="Fun ls" src="/images/2014/11/fun-ls.png" /></p>
<h5 id="bundler-and-rails-section">Bundler and rails section</h5>
<p>Most of these commands are shortenings of normal usage. A few stand out</p>
<p><code>function bers</code> and <code>function berc</code> Rails 2 invokves commands via <code>script/foo</code>. Rails 3+
use <code>rails foo</code>. This is my attempt to harmonize both. </p>
<p><code>function rRg</code> Ever wanted to search for a specific route? Grep the
output of rake routes</p>
<p><code>function rlc</code> and <code>function rlv</code> Will grep the server output for words
that indicate controllers and views respectively. Useful for tracking
down execution in unfamiliar code</p>
<h5 id="git-section">Git section</h5>
<p>There's a lot of writing on the internet about how the basic interface
to git is mega confusing. I agree. That's why I made my own. Common
operations get their own three (or sometimes four) -letter shortcuts. My
normal workflow consists of the following:</p>
<p><code>gcom</code> (git checkout master). <code>gpum</code> (git pull upstream master). <code>gps</code>
(git push (to my branch)). <code>gcob WT-1234</code> (git checkout -b with the name
of whatever jira ticket). And so on and so on. </p>
<p>Finally, I have several local customizations for ssh'ing into work
machines (sorry, the details are secret). I don't type <code>ssh
username@sillynamingconvention.12.23.32.232.blurb.com</code>, I simply type
<code>webteam</code>. Takes me straight to our team's staging server. </p>
<h4 id="vim">Vim</h4>
<p>I use Vim as my standard editor, along with Yehuda Katz'
<a href="https://github.com/carlhuda/janus">Janus</a> framework. I don't use the
majority of the extensions (I don't even know what they are!) but I'm
slowly learning them. Most of them are commonsense things that I would
go looking for anyway. Check it out for details!</p>
<h4 id="alfred">Alfred</h4>
<p>I mentioned above, I use a launcher program called <a href="http://www.alfredapp.com/">Alfred</a>
to manage most of my gui workflows. At first glance, it looks like
nothing more than a replacement for OSX's spotlight, but it is much,
much more. I don't even use it to the full extent; I mainly use it for
invoking programs, switching windows, and controlling my music. A simple
tap on ⌘-space brings up the Alfred window, where I can type a few
characters and it will intelligently fuzzy-match it to recently used
commands.</p>
<p><img alt="Alfred" src="/images/2014/11/alfred-launcher.png" /></p>
<p>I also use it to control my music. By typing 'it' I activate the itunes
client, which lets me do a bunch of things, most with a single text
command</p>
<p><img alt="iTunes via Alfred" src="/images/2014/11/alfred-itunes.png" /></p>
<p>Alfred comes with a visual scripting environment that lets you make all
kinds of custom control scripts. Maybe some day I'll make some of them.
For example, I've a friend who has Alfred scripts to control his Rails
development environments. It's saved him oodles of time!</p>
<h4 id="chrome">Chrome</h4>
<p>What kind of web developer would I be if I didn't have Chrome
customizations? I've only a few, and they're only useful in niches, but
dang if this job doesn't have a ton of niches. </p>
<h6 id="advanced-rest-clienthttpschromegooglecomwebstoredetailadvanced-rest-clienthgmloofddffdnphfgcellkdfbfbjeloo"><a href="https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo">Advanced Rest Client</a></h6>
<p>I often need to interact with and debug APIs, but Curl forever eludes
me. So I got this instead. It's a pretty straightforward REST request
manager. It lets you set headers with a convenient form, specify HTTP
verbs, save requests for later. It's pretty useful</p>
<h6 id="edit-this-cookiehttpwwweditthiscookiecom"><a href="http://www.editthiscookie.com/">Edit This Cookie</a></h6>
<p>Speaking of APIs, who hasn't had to debug cross-domain cookies that get
shared between API calls. What's that, it's only me? :(</p>
<p>This is a very useful cookie inspector/editor that makes it a lot easier
to debug those rare cookie-based bug</p>
<h6 id="page-rulerhttpschromegooglecomwebstoredetailpage-rulerjlpkojjdgbllmedoapgfodplfhcbnbpn"><a href="https://chrome.google.com/webstore/detail/page-ruler/jlpkojjdgbllmedoapgfodplfhcbnbpn">Page Ruler</a></h6>
<p>Does what it says on the tin. Good for those pixel-perfect jobs</p>
<h6 id="visual-eventhttpschromegooglecomwebstoredetailvisual-eventpbmmieigblcbldgdokdjpioljjninaim"><a href="https://chrome.google.com/webstore/detail/visual-event/pbmmieigblcbldgdokdjpioljjninaim">Visual Event</a></h6>
<p>This extension gives you a button that, once clicked, will highlight
every html element with events bound to it, and show you the javascript
that will run on a given event trigger when you mouse over the
highlights. This is super useful for JS heavy applications.</p>
<h6 id="search-shortcut-abuse">Search Shortcut Abuse</h6>
<p>One final technique I've come across is abusing search shortcuts.
Nominally a search shortcut is just that: say I type "wikipedia" in the
url bar, that tells the browser to search wikipedia with whatever else
I type. But how does it do this? By crafting the correct query URL on
wikipedia. For instance, searching Google for "test" really just means
navigating to http://google.com/search?q=test. Using this lets me abuse
this functionality on other sites. For instance, I have every repo at
work set up to let me effortlessly search for a commit on github. If I
type <code><repo> <sha></code> it will expand to <code>http://<internal
git>/therd/<repo>/commit/<sha></code>, showing me the commit I want. I've set
up a similar tool for Jira tickets. </p>
<h4 id="closing-thoughts">Closing thoughts</h4>
<p>You should customize your environment. Customizing your environment can
speed up common tasks. It can encapsulate complexity into simple things
that don't break your concentration. It can provide new functionality
that you can make into a core part of your workflow.</p>
<p>If nothing else, it stops coworkers from borrowing your machine :D</p>
<blockquote>
<p>Tim is a Software Engineer on the Web Team. He's spent entirely too
much time customizing his development environments, and hiding cat
pictures in the work he creates</p>
</blockquote>
<h5>
Tagged: engineering
</h5>
</article>
<article>
<h2>
<a href='/2014/10/30/how-we-work.html'>
How we work.
</a>
</h2>
<h6>
By
<a href="http://www.g9labs.com">Andrew Hao</a>
(
<a href="http://www.twitter.com/andrewhao">@andrewhao</a>
)
<time>
October 30, 2014
</time>
</h6>
<h4 id="a-quick-glimpse-into-developer-life-at-blurb">A quick glimpse into developer life at Blurb:</h4>
<p>I get into the office around 9AM and check email (and make my daily
coffee – can't miss it). Great. I usually spend some time cleaning up
correspondence between some product owners and our tech leads discussing
new ideas around new product X or feature Y. A quick scan of New Relic and Airbrake reveal that the site's humming along well.</p>
<p>I log into <a href="http://www.slack.com">Slack</a>, our central communication hub,
and quickly get a dump of the conversations by our teammates in the
morning and last night. Surprisingly, something as simple as a central
chatroom has helped break down some communication walls in our teams.
Furthermore, having a continual log of team conversations have helped
our remote workers keep abreast of continual updates.</p>
<h4 id="agile--blurb">Agile @ Blurb</h4>
<p>I log into JIRA and scan the board to check our team status. Looks like
we still have a lot of in-progress stories. I'm currently not assigned
anything.</p>
<p>At our daily standup, Chris, a senior engineer on my team, is working on
story Z. I decide I'm going to ask Chris how he's doing
and see if I can help.</p>
<p>Chris tells me that he needs some help writing a few tests for his
feature, so I pull up a chair and we jump right into it – he implements
the feature, I write the tests. Time passes. We finish our work and we push
our branch up to the Github server for some code review.</p>
<h4 id="code-reviews">Code reviews</h4>
<p>Code review on our team is done via the pull request system – we ping
our teammates on Slack and ask for a code review. The comments begin
flowing in, and we respond.</p>
<p>Simulataneously, we're looking at <a href="http://www.codeclimate.com">Code
Climate</a> to give us feedback on how our code
fared against its static analysis tools. Hmm, we seem to have bumped
code complexity a bit in this class, so let's sit down and refactor some
more. We push a change and we ask folks in our chatroom to help give
comments once more.</p>
<p><img alt="Pull requests" src="/images/2014/10/sample_pull_request.png" /></p>
<p>(Lunch rolls around. We grab some coworkers to stretch our legs and go to lunch at
<a href="https://twitter.com/senorsisig">Senor Sisig</a>, which
is probably the best food truck in the world.)</p>
<p>Our team works on a 2-"+1" code review system. When we make code
changes, we need at least two other people on the team to approve our
code. It sounded a little daunting at first, but it's been a good way to
make sure that the right conversations are happening around the code.</p>
<h4 id="robots-make-life-better">Robots make life better.</h4>
<p>Our <a href="https://github.com/github/hubot">hubot</a> sits in the chatroom and
tries to be a little helpful. Here, hubot chimes in and lets us know
that two people have given their approvals.</p>
<p><img alt="Slack chat #dayinthelife" src="/images/2014/10/slack_window.png" /></p>
<h4 id="continous-integration--blurb">Continous integration @ Blurb</h4>
<p>We merge the pull request and wait for Jenkins to run the unit test
build. We keep a big ol' build light on a bunch of build monitors in our work
area. If you break the build, the big red light will radiate waves of
Shame upon you until you fix the build.</p>
<p><img alt="Build light: green is good." src="/images/2014/10/build_light.jpg" /></p>
<p>Once the build passes, it automatically deploys the app to an internal
staging environment – the whole process happens within 15 minutes.</p>
<p>Sheree, a test engineer on our team, has already run ahead of us and
written some RSpec integration tests prior to our implementation. Chris
has already sat down with Sheree prior to beginning the story and
defined some of the stories and use cases, BDD-style. We work together
to get the integration tests running and working.</p>
<h4 id="all-in-a-days-work">All in a day's work.</h4>
<p>A big reason I enjoy working here is because our daily development
workflow encourages teamwork and collaboration, and you can get feedback
on your changes very quickly. And we're making it better! Stay tuned as
we continue to write more about our engineering practices.</p>
<blockquote>
<p>Andrew is an Engineering Manager on the Web Team. He has long ago
acknowledged the superiority of Senor Sisig burritos and looks forward
to them all week.</p>
</blockquote>
<h5>
Tagged: engineering, day in the life
</h5>
</article>
<article>
<h2>
<a href='/2014/08/05/i18n-js-ember-and-ember-validations.html'>
Tips for Making I18n-js Play Nicely with Ember and Ember Validations
</a>
</h2>
<h6>
By
Chris Zhang
<time>
August 5, 2014
</time>
</h6>
<p>Ember's handlebars helpers don't like having too many arguments (it allows you to have one argument and an options hash). It also doesn't like nested calls, such as having one helper that takes the result of another helper as an argument. </p>
<p>A while ago, we added a helper method for i18n-js that allows us to translate things in the view layer. In order to make the helper method handle string interpolation with nested translations (for example: translating "If you want to learn more, click #{here}", and then <code>here</code> is itself a translated link to somewhere), we make the translate helper recognize a special suffix. We called it <code>Key</code>, so any translation key that ends in <code>Key</code> represents a nested translation (such as <code>hereKey</code> for the earlier example).</p>
<p>Then, we translate the key before passing it in for string interpolation as part of the original translation.</p>
<p>The final method looks like this:</p>
<p><a class="jsbin-embed" href="http://emberjs.jsbin.com/ricimiciwe/1/embed?js">Ember Starter Kit</a></p>
<p>We also had some trouble using the message field in ember-validations, as it overwrites i18n and appeared to be setting the message value before we set our i18n-js locale, causing all our error messages to be stuck in english.</p>
<p>We fixed it by changing the message from trying to translate the string to just returning the translation key and changing the view accordingly to handle the translation there instead.</p>
<p><a class="jsbin-embed" href="http://emberjs.jsbin.com/zagapajisi/1/embed?html,js">Ember Starter Kit</a>
<script src="http://static.jsbin.com/js/embed.js"></script></p>
<h5>
Tagged: ember.js, i18n-js, ember-validations
</h5>
</article>
<article>
<h2>
<a href='/2014/06/23/ember-data-sideloading-for-objects-with-nonstandard-primary-keys.html'>
Ember Data Sideloading for Objects with Nonstandard Primary Keys
</a>
</h2>
<h6>
By
Chris Zhang
<time>
June 23, 2014
</time>
</h6>
<p>This builds off the sideloading <code>ApplicationSerializer</code> introduced in <a href="http://mozmonkey.com/2013/12/loading-json-with-embedded-records-into-ember-data-1-0-0-beta/">this article</a> and adapts it to handle JSON objects that don't have "id" as their primary key. Several instances where this might come up are when handling currencies or countries.</p>
<p>Let's start with data that looks like:</p>
<p><a class="jsbin-embed" href="http://emberjs.jsbin.com/majone/3/embed?js">Sample Data</a></p>
<p>If we import this using the previously defined sideloading serializer, we end up with four currencies, each with a generated id, which can quickly start to clutter our store when we start handling dozens or hundreds of prices in multiple different currencies.</p>
<p>In order to fix this, we need to look at the <code>sideloadItem</code> method. </p>
<p><a class="jsbin-embed" href="http://emberjs.jsbin.com/jises/6/embed?js#J:L14-20">Original</a></p>
<p><code>primaryKey</code> in this case is always set to "id", because <code>this</code> in our case refers to the <code>ApplicationSerializer</code> rather than the serializer for <code>item</code>. Ember Data always needs an id for its records, so what we can do instead of letting each currency generate a new one is set the id here based on some other primary key.</p>
<p>At this point, <code>type</code> is the class of the item (<code>App.Currency</code>) and <code>item</code> is the payload of the json, which should look something like this (note that it hasn't been run through the serializer for its class yet):</p>
<pre><code>{ iso_id: "USD", symbol: "$", delimiter: "," }
</code></pre>
<p>So what we can do is shove an id into the item before pushing it into the sideload array, by checking to see if the class has a nonstandard primary key that we want to store as the id of the item.</p>
<p><a class="jsbin-embed" href="http://emberjs.jsbin.com/jises/5/embed?js#J:L17-20">new</a></p>
<p>And to set the <code>rawPrimaryKey</code> (raw because we want the key the json gives us, because we haven't yet normalized it to the class's actual ) as a class attribute, we need to reopen the it, as so:</p>
<pre><code>App.Currency.reopenClass({ rawPrimaryKey: "iso_id" })
</code></pre>
<p>Now, loading the data gives us four prices and two currencies, with the added bonus that comparing the currencies of two prices with the same currency should now work as expected without needing a custom compare method.</p>
<p>The final file looks like:</p>
<p><a class="jsbin-embed" href="http://emberjs.jsbin.com/jises/8/embed?js">Final</a></p>
<script src="http://static.jsbin.com/js/embed.js"></script>
<h5>
Tagged: ember.js, ember-data, sideloading
</h5>
</article>
<article>
<h2>
<a href='/2014/04/23/sorting-ember-arraycontroller.html'>
Sorting Ember ArrayController
</a>
</h2>
<h6>
By
<a href="https://twitter.com/chicagoing">Estella Madison</a>
(
<a href="http://www.twitter.com/chicagoing">@chicagoing</a>
)
<time>
April 23, 2014
</time>
</h6>
<blockquote>
<p>Last April, I presented at the <a href="https://twitter.com/EmberNYC">Ember NYC meetup</a>
to discuss different approaches for sorting an ArrayController. This is a
short synopsis, you can learn more in the meetup video and slides.</p>
</blockquote>
<p>To sort an Ember <code>ArrayController</code> you may have used the included
SortableMixin <code>sortProperties</code> and <code>sortAscending</code> properties. These
are great and work really well for sorting on one property or sorting on
multiple properties all in the <em>same direction</em>. But that's a huge caveat,
<code>SortableMixin</code> does not allow you to sort by multiple properties in different
directions.</p>
<p>For example, suppose you have a list of books you want to display, with the
most recent at the top. Using the <code>SortableMixin</code>, this is done by setting
<code>sortProperties</code> and <code>sortAscending</code>, and then referencing the <code>arrangedContent</code>
property in our template or view:</p>
<p><a class="jsbin-embed" href="http://emberjs.jsbin.com/nibiq/latest/embed?js">Ember Starter Kit</a></p>
<p>While <code>sortProperties</code> lets us specify more than one property to order by, you'll notice
<code>sortAscending</code> is a boolean value and gets applied to <em>all</em> properties.
But what if we wanted to sort by the publish date first in descending order,
then by title in ascending order? We're unable to sort in different directions without applying a custom
sort function (which can get cumbersome).</p>
<h2 id="welcome-embercomputedsort">Welcome <code>Ember.computed.sort</code></h2>
<p>The Computed Property Macros have several methods for creating common types
of computed properties. <code>Ember.computed.sort</code> allows us to:</p>
<ul>
<li>Sort properties by different criteria</li>
<li>Use a custom comparison function when needed</li>
<li>Sort by computed properties</li>
</ul>
<p><code>Ember.computed.sort</code> has two parameters:</p>
<ol>
<li><code>dependentKey</code>: a reference to the content we're sorting</li>
<li><code>sortDefinition</code>: either a string referencing the property key containing the sort definition or a custom sort function</li>
</ol>
<p>In the example below, <code>sortDefinition</code> is the property key <code>propertiesToSortBy</code>,
which is an array of property keys and the corresponding sort direction. By
default, properties are sorted in ascending order, but by appending ":desc" to the
key we can change the sort direction. This allows to specify a different
sort direction for each property:</p>
<p><a class="jsbin-embed" href="http://emberjs.jsbin.com/fidiy/latest/embed?js">Ember Starter Kit</a></p>
<p>Your template or view will need to reference <code>sortedBooks</code> to reflect the
sorted content. <a href="http://emberjs.jsbin.com/fidiy/6/edit?html,js,output">See the complete JS Bin example</a>.</p>
<p>Don't forget to check out the slides below for links to more examples.</p>
<h2 id="emberjs-nyc-meetup-presentation">Ember.js NYC Meetup Presentation</h2>
<iframe width="576" height="331" src="//www.youtube.com/embed/dWIJ5Wk3LG4" frameborder="0" allowfullscreen=""></iframe>
<iframe src="//slides.com/estellagonzalezmadison/deck/embed" width="576" height="420" scrolling="no" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
<script src="http://static.jsbin.com/js/embed.js"></script>
<h5>
Tagged: ember.js
</h5>
</article>
</div>
</div>
</div>
</section>
</section>
<footer>
<hr>
<div class='row'>
<div class='columns large-12'>
<ul class='inline-list'>
<li>
<a href='http://www.blurb.com'>Blurb</a>
</li>
<li>
<a class='secondary' href='http://www.github.com/blurb'>Github</a>
</li>
</ul>
</div>
</div>
</footer>
</body>
</html>