Interesting Thingsurn:uuid:af0d77cb-e4c2-3d31-8c8b-b0fc39ae4f8e2017-09-23T00:00:00ZDavid BaumgoldA blog of whatever I find interesting. Mostly tech-related, but not entirely.Lektor Atom PluginMoving to Amsterdam!urn:uuid:e4dc6200-90f9-3399-81e4-4200783265f12017-09-23T00:00:00ZDavid Baumgold<p>I've done a fair amount of travel in my life, and I've often wondered what it
would be like to actually live in another country, rather than just visiting
as a tourist. Well, I've decided to find out. I've been planning and preparing
for the last few months, and last week I packed up my belongings, hopped on
a plane, and moved to Amsterdam!</p>
<p>I've taken a job as a senior front-end developer with an amazing startup
company called <a href="https://www.impraise.com/">Impraise</a>. Their main office is
right in Amsterdam, although they are a
<a href="https://www.impraise.com/team/">stunningly international team</a>:
of the forty or so employees, only perhaps 5 are Dutch! The rest come from
all over the world: Germany, France, Uzbekistan, Russia, Poland, New Zealand,
Chile, South Africa, America, and many other places as well. I've already
had my first week at the company, and things seem to be going very well.
I like my coworkers, and I've already made a few pull requests to the codebase.
I'm one of the most senior developers on the team, and I'm really looking
forward to teaching others what I know — as well as learning from them,
since there's always more to learn! This is my first job working primarily
on the front-end; every other web development job in my career has been focused
on back-end development, so while I think I can contribute a lot of knowledge
and experience about quality software development principles, I'm quite sure
that there's still plenty for me to learn, as well.</p>
<p>Although I've been telling everyone that I live in Amsterdam now, it's not
technically true. At the moment, I'm staying with some good friends of mine
who live in <a href="https://en.wikipedia.org/wiki/Leiden">Leiden</a>, a very cute city
not far from Amsterdam. I'm very grateful to them for hosting me, since I
didn't want to rent an apartment sight unseen! I've been going through all the
steps required to be a proper <a href="https://en.wikipedia.org/wiki/Expatriate">expat</a>
in the Netherlands: registering with the government, getting a Dutch bank
account, signing up for a local phone plan, and so on. I've been told that
finding an apartment in Amsterdam is difficult, so it's really nice to have
a place to stay while I figure out how to make that happen. It also means I've
started making social connections already: I've met a few Dutch friends,
and even participated in my first
<a href="https://en.wikipedia.org/wiki/Escape_room">escape room</a> (and we all escaped!).
As nice as Leiden is, though, I'm looking forward to finding a place of my own,
if for no other reason than a shorter commute to work.</p>
<p>Hopefully I'll be blogging a bit more, since it's a good way to organize my
thoughts, and I suspect I'll need to do that quite a lot as I adapt to a new
culture and new situations. However, if past experience is any indication,
updates might still be a rare occurance. If you want to hear more from me,
<a href="https://twitter.com/singingwolfboy">follow me on Twitter</a>!</p>
Oppose HB2796urn:uuid:99772e38-88f5-3a60-8172-37dbce26d4b22017-07-16T00:00:00ZDavid Baumgold<p>There’s a lot of scary stuff happening in United States politics these days,
to the point that it’s easy to lose track of it all.
There’s a bill in committee right now that is frankly terrifying,
and it’s getting very little attention in the press because it targets a minority that’s easy and popular to oppress: transgender people. The bill is <a href="https://legiscan.com/US/bill/HB2796/2017">HB2796</a>, and here's the official summary:</p>
<blockquote class="blockquote">
This bill prohibits the word "sex" or "gender" from being interpreted
to mean "gender identity," and requires "man" or "woman" to be
interpreted to refer exclusively to a person's genetic sex,
for purposes determining the meaning of federal civil rights laws or
related federal administrative agency regulations or guidance.
No federal civil rights law shall be interpreted to treat gender identity
or transgender status as a protected class, unless it expressly designates
"gender identity" or "transgender status" as a protected class.</blockquote><p>This bill would essentially strip all civil protections from transgender people.
It would leave them open to discrimination in terms of healthcare, housing,
employment, and literally all aspects of society.
Several of my friends are transgender, and they are terrified of this
bill passing.</p>
<p>If that’s not enough, the bill makes reference to a person’s “genetic sex”
for purposes of determining how the laws of this country are applied.
That little phrase could result in mandatory genetic testing of all US citizens.
Genetic variations and random mutations mean that not everybody has
XX or XY chromosomes — biology is a lot more messy and complicated
than that. <a href="https://www.nature.com/news/sex-redefined-1.16943">This article from the scientific journal Nature
has more information about intersex conditions.</a>
What happens to people whose “genetic sex” is neither male nor female?</p>
<p>HB2796 is currently being debated by the Republican-dominated
<a href="https://judiciary.house.gov/subcommittee-on-the-constitution-and-civil-justice/">House Subcommittee on the Constitution and Civil Justice</a>.
The Democrats have <a href="https://democrats-judiciary.house.gov/subcommittees/constitution-and-civil-justice-115th-congress">their own website for this
subcommittee</a>. These are the representatives
on this subcommittee:</p>
<ul>
<li>Chairman <a href="https://steveking.house.gov/contact">Steve King</a> (R-IA-04; King is also one of HB2796′s sponsors) - <a href="tel:2022254426">(202) 225-4426</a></li>
<li>Vice Chairman <a href="https://desantis.house.gov/offices">Ron DeSantis</a> (R-FL-06) - <a href="tel:2022252706">(202) 225-2706</a></li>
<li>Rep. <a href="https://franks.house.gov/contact-me">Trent Franks</a> (R-AZ-08; Franks is also one of HB2796’s sponsors) - <a href="tel:2022254576">(202) 225-4576</a></li>
<li>Rep. <a href="https://gohmert.house.gov/contact/">Louie Gohmert</a> (R-TX-01) - <a href="tel:2022253035">(202) 225-3035</a></li>
<li>Rep. <a href="https://handel.house.gov/contact/offices">Karen Handel</a> (R-GA-06) - <a href="tel:2022254501">(202) 225-4501</a></li>
<li>Ranking Member <a href="https://cohen.house.gov/contact">Steve Cohen</a> (D-TN-09) - <a href="tel:2022253265">(202) 225-3265</a></li>
<li>Rep. <a href="https://raskin.house.gov/">Jamie Raskin</a> (D-MD-08) - <a href="tel:2022255341">(202) 225-5341</a></li>
<li>Rep. <a href="https://nadler.house.gov/contact-me">Jerry Nadler</a> (D-NY-10) - <a href="tel:2022255635">(202) 225-5635</a></li>
</ul>
<p>If you live in the United States, ask your representative to oppose this bill.
If your representative is on this subcommittee, it's doubly important that you
do so. If you're not sure what to say, here's a simple script:</p>
<blockquote class="blockquote">
Hello, my name is <code>NAME</code> from <code>TOWN</code>,
<code>CITY</code>, <code>ZIP CODE</code>.
I'm calling to ask that <code>REPRESENTATIVE</code>, as a member of the
House Subcommittee of the Constitution and Civil Justice,
oppose the bill HB2796. This bill is fundamentally harmful to
transgender people. It would allow the rampant discrimination
that is already affecting the trans community to grow even
stronger. Please do not allow this bill to pass. I plan to take
<code>REPRESENTATIVE</code>'s actions on this bill into consideration
during the next Congressional election. Thank you.</blockquote><p>Politics is scary and stressful, but it's important to make your voice heard.</p>
Tutorial: Automated Tests for Node.jsurn:uuid:737616a1-08b3-3396-96a0-a9817b14f06f2016-12-30T00:00:00ZDavid Baumgold<p>The company that I'm currently <a href="/consulting">consulting</a> for has a codebase
with a large, complex front-end written in
<a href="https://facebook.github.io/react/">React</a>. Not only does this codebase
actually have automated tests, but their test coverage is pretty good, and
every new feature needs to include automated tests before it can be merged.
This is <em>incredible</em>, and every software company should work like this --
sadly, very few of them do. This means that in order to do my job, I need to
know how to write and debug automated tests for Node. I've been learning how
to do this very quickly, and there have been a few stumbles and hurdles
on the way.</p>
<p><a href="/tutorials/automated-tests-node/">I decided to take all the knowledge I've learned about automated testing
in a modern JavaScript codebase, and pull it together into one tutorial.</a>
It is by no means exhaustive, and the JavaScript community is moving so fast
these days that most of what's in this tutorial will probably be out of date
in six months or so. But I've already found myself wishing that I had written
more notes to myself about JavaScript testing in the past, so hopefully
this blog post will help me out in the future. If you find it useful as well,
let me know!</p>
PyDX 2016urn:uuid:e9b60bdd-b50b-3003-885c-8dafd6c3ae2a2016-10-04T00:00:00ZDavid Baumgold<p>I'm back in Portland, land of the hipsters. <a href="http://pydx.org/">PyDX</a> was this
past weekend, and I had a great time! To be honest, I was looking for an excuse
to visit the Pacific Northwest again after
<a href="/blog/2016/06/pycon-us-2016/">going there for PyCon</a>,
so when PyDX accepted my talk proposal, I knew I had found the right excuse. :)</p>
<h2 id="about-pydx">About PyDX</h2><p>PyDX is a pun on "PDX", the abbreviation for Portland. It bills itself as a
"Community-Driven Python Workshop in Portland", with "Post-Ironic Artisanal
Code from Local <del>Farmers</del> Programmers" and "Small-Batch Gluten-free Vegan
Workshops and Talks". Direct quotes from the conference website. Clearly,
the conference organizers know their target audience.</p>
<p>PyDX is a small, local conference. The conference sold out at 150 attendees,
which felt like a good amount. There were two speaker tracks plus
a large area for hanging out and chatting: the "hallway track",
as it's often called. I spent most of my time attending talks,
which I kind of regret — I should have pushed myself to be more social
and spent more time on the hallway
track. Oh well, there's always the next conference!</p>
<h2 id="day-1">Day 1</h2><p><a href="https://github.com/jarrighi">Juliana Arrighi's</a> keynote was about
creating "learning adventures": managing risk to find a balance between
low-risk low-reward guided instruction,
and high-risk high-reward personal challenges. Learning new topics by yourself
involves a lot of "unknown unknowns": roadblocks that you don't know about
ahead of time. It's hard to find the motivation and perseverance to succeed,
but if you do, you'll end up with some valuable knowledge that you won't
soon forget! I really enjoyed Jules' keynote. :)</p>
<p>The other presentations were also good. I liked
<a href="https://madeinsonoma.org/">Josh Simmons'</a> presentation on building community
in open source, and <a href="https://chris.neugebauer.id.au/">Chris Neugebauer's</a>
presentation on organizing tech conference. (A presentation <em>at</em> a tech
conference <em>about</em> tech conferences — it was very meta.) I also really
enjoyed <a href="http://www.offermann.us/">Tom Offermann</a> talking about the
<a href="https://docs.python.org/3/library/trace.html"><code>trace</code></a> module in Python,
taking a complex topic and making it approachable and understandable. And
<a href="http://jeremytanner.com/">Jeremy "Penguin" Tanner</a> had a fantastic presentation
about using Python to make smart picks on daily fantasy sports websites.
He's so engaging that he could make <em>any</em> topic interesting, and while I have
no interest in sports or fantasy leagues, I thoroughly enjoyed myself.</p>
<p>The conference provided free pizza for lunch, and after that,
<a href="http://www.philipjohnjames.com/">Philip James</a> led an expedition to the
nearby <a href="http://www.portlandsaturdaymarket.com/">Portland Saturday Market</a>,
which was so quintisenntially Portland. Full of handmade crafts, artisinal
what-nots, and delicious streetfood, and populated by stand-out locals and
blend-in tourists. I was only there for a few minutes, but I had a great time,
and <a href="https://twitter.com/singingwolfboy/status/782307593075077121">took a few pictures</a>.</p>
<p>Dinner was an interesting affair, as well. We all divided into five groups
and went to five different restaurants, and the conference paid for everyone's
dinner, including tip! <a href="http://www.thursdaybram.com/">Thursday Bram</a>, who helped
organize PyDX and was in my dinner group, explained that it was actually cheaper
to pay for everyone's dinner at a restaurant than to organize a catered event,
and they wanted to encourage attendees to eat with and talk with each other.
Small groups also makes it easier for people to talk, and the groups were
organized in a "Birds of a Feather" style: anyone could propose a topic,
and others who were interested in that topic could sign up to join. Our
group's topic was "static site generators," proposed by yours truly (because
I'll take any opportunity I can to talk about
<a href="https://www.getlektor.com">Lektor</a>!), but the conversation ranged across
many different topics. We went to
<a href="https://www.yelp.com/biz/sushi-ichiban-portland">Sushi Ichiban</a>, which was
not only delicious, but had an adorable little choo-choo train that went around
the main bar carrying small plates of sushi. It was the most creative take I've
ever seen on conveyor-belt sushi!</p>
<h2 id="day-2">Day 2</h2><p><a href="http://www.thegeekyhustle.com/">Portia Burton's</a> keynote on Bitcoin,
Etherium, and blockchain technology was a really interesting start to the second
day of PyDX. She created a new cryptocurrency called "Snake Dollars" during
the presentation! Seems like cryptocurrencies aren't going away, although
they haven't risen to prominence yet, either. Portia also talked about
Etherium's "smart contracts", which personally I'm very skeptical about,
but that's another topic for another blog post.</p>
<p>Next, <a href="http://www.heidiwaterhouse.com/">Heidi Waterhouse</a> talked about applying
principles of garbage collection to documentation: a brilliant idea! The older
and less referenced a piece of documentation is, the more likely it is to be
outdated and incorrect, so why not mark it as such? After that, I heard from
<a href="http://jeromecomeau.com">Jerome Comeau</a> about the role of support in technical
organizations: informative, but bleak. He claimed that one out of every four
people in tech suffers from migranes, and that for people in support positions,
the ratio rises to one in two. Shocking, and yet not unbelievable; I'd love to
see a source for those claims. <a href="https://twitter.com/oboechick_">Nicholle James</a>
gave a presentation about how to run a workshop and help people learn;
she mentioned a few principles I plan to put into action the next time
I teach a class, including pairing up more
experienced people with less experienced people.
<a href="https://tilde.town/~vilmibm/">Nate Smith</a> talked about some basic optimization
techniques for Python: I didn't learn anything new, but it was nice to see
how to put basic knowledge about IO and Python types put to work gracefully.
Finally, <a href="https://twitter.com/gizm0_0">Sev Leonard</a> did an awesome presentation
about getting started with hardware: he built the "Internet of Cats", an
<abbr title="Internet of Things">IoT</abbr> device that displays cat pictures
in response to HTTP requests. His excitement was infectious, and I really
enjoyed hearing about hardware, a topic that I still know comparatively little
about.</p>
<p>After all that, I gave my presentation on Advanced Git. It was the same
presentation I gave at PyCon, OSCON, and PyGotham, and I think word is getting
around, because the room was <em>packed</em>. The presentation went off without
a hitch, and the attendees asked some good questions, so I count it as a great
success. A lot of people seemed interested in my <a href="/book">book</a>, as well: I guess
I need to hurry up and finish writing the damn thing!</p>
<h2 id="other-thoughts">Other Thoughts</h2><p>PyDX was the first "dry" tech conference I've ever attended: there was no
alcohol allowed. The <a href="http://blog.pydx.org/code-of-conduct/">code of conduct</a>
stated that if anyone brought alcohol into the conference venue, or showed up
drunk, they would be asked to leave. While I don't think that <em>every</em> tech
conference should be dry, I'm really glad that there are at least a few that
are trying it out. I thought it worked quite well, and certainly made me feel
more comfortable, since I don't drink. Alcohol is deeply entrenced in tech
culture, and being in a space where choosing not to imbibe was a complete
non-issue was really nice.</p>
<p>I heard <em>many</em> people talking about how much they enjoyed
<a href="http://www.philipjohnjames.com/">Philip James'</a>
talk: "Frog and Toad Learn About Django Security." Apparently, it was
given in the style of Arnold Lobel's
<a href="https://en.wikipedia.org/wiki/Frog_and_Toad">Frog and Toad</a> books, inspired
by the blog
<a href="https://medium.com/frog-and-toad-are-cofounders">Frog and Toad are Cofounders</a>.
I really want to see this talk when the video is available online!</p>
<p>I learned more about the <a href="http://archive.org/">Internet Archive</a> from
<a href="http://www.vmbrasseur.com/">Vicky Brasseur</a>, and particularly about its
capabilities for hosting slides and video for talks. She even has a great
<a href="http://anonymoushash.vmbrasseur.com/2016/07/25/uploading-a-video-to-internet-archive/">blog post about how to upload video to the Internet Archive</a>,
which I plan to reference when finding and uploading my OSCON video. I also
learned from Barbara Miller that
<a href="https://developers.archive.org/">the Internet Archive provides APIs</a>, and
provides infinite storage for free, including
<a href="https://developers.archive.org/services/upload-v1/">an S3-like API</a>!
This is <em>definitely</em> something I'll need to investigate futher.
In the past, I've hosted my slide decks on
<a href="https://speakerdeck.com/singingwolfboy/">SpeakerDeck</a>, which has a very
pretty interface. However, moving forward, I think I'll be using the
Internet Archive.</p>
<p>Thursday Bram, one of the conference organizers, declared that <a href="https://twitter.com/thursdayb/status/781977516336488448">she would get
a PyDX tattoo if the conference sold
out</a>. Well, the
conference sold out, and <a href="https://twitter.com/thursdayb/status/782300335792529408">she's very excited to be getting that
tattoo!</a> That's
awesome, and I look forward to seeing it when it's done!</p>
<p>I'll still be in Portland for a few more days. If you'd like to say hello
while I'm in the area, please let me know! I love hanging out with people and
making new friends. :)</p>
Tutorial: Deploying Static Sites on AWSurn:uuid:48cfe637-c446-31e9-88d0-cc885651b5a62016-08-08T00:00:00ZDavid Baumgold<p>I've <a href="/blog/2016/05/learning-lektor/">blogged</a> about how
<a href="https://github.com/singingwolfboy/davidbaumgold.com">the contents of my website</a>
are maintained with <a href="https://www.getlektor.com/">Lektor</a>, a static content
management system. However, websites are about more than just content.
<a href="https://en.wikipedia.org/wiki/Software_deployment">Deployment</a> is a deceptively
difficult problem, and a lot of software developers struggle with learning
how to deploy their software effectively. It's a large enough problem that
there's an entire field dedicated to it:
<a href="https://en.wikipedia.org/wiki/Information_technology_operations">operations</a>,
or <a href="https://en.wikipedia.org/wiki/DevOps">DevOps</a>.
I deploy my website using <a href="https://aws.amazon.com/">Amazon Web Services</a> (AWS),
and I've gotten some requests to write a <a href="/tutorials">tutorial</a> for how
to do that.</p>
<p>Well, your wish is my command. <a href="/tutorials/host-static-site-aws-s3-cloudfront/">There's a shiny new tutorial for how to deploy
a static website on AWS</a>,
and although it's focused on Lektor-based websites, the tutorial can be
easily adapted to work with any static website or static site generator.
This tutorial will take you from AWS newbie to running a bulletproof static
website that no amount of traffic can overwhelm. Your site will run on
<a href="https://https.cio.gov/faq/">HTTPS</a> for added security and no additional cost,
which is good because
<a href="https://https.cio.gov/everything/">every website should use HTTPS</a>.
You'll also learn how to set up an email address at your domain, like I have
with mine: <a href="mailto:david@davidbaumgold.com"><code>david@davidbaumgold.com</code></a>.
(So fancy!) And the best part is,
running a static website on AWS can be shockingly cheap! The whole thing
only costs about $20 per <em>year</em>.</p>
<p>What are you waiting for?
<a href="/tutorials/host-static-site-aws-s3-cloudfront/">Read the tutorial</a>,
and don't forget to give me some feedback! I love comments, emails, and tweets,
but will accept smoke signals and messenger pidgeons as well.
The only way to improve is by getting feedback!</p>
PyGotham 2016urn:uuid:8dd78cfe-8090-3b45-9810-f0005fa961b52016-07-19T00:00:00ZDavid Baumgold<p>I attended <a href="https://2016.pygotham.org/">PyGotham</a>, a regional
<a href="http://www.pycon.org/">Python conference</a> in New York City.
This year, it was held in the
<a href="http://visit.un.org/content/location">United Nations conference building</a>,
which was <em>really cool!</em> This was my first regional Python conference, and
I'm really glad I went: it had the same
<a href="/blog/2016/06/pycon-us-2016/">spirit of PyCon</a>,
but at a much smaller scale (around 500 people or so).</p>
<h2 id="new-york-city">New York City</h2><p>New York City is always fun to visit — it wasn't my first time, and it won't
be my last. Since PyGotham is a weekend conference (Saturday and Sunday), I
headed down to NYC the day before the conference started, and decided to stay
a few extra days to visit friends in the city.
I had spent the prior week in Provincetown, MA for
<a href="http://ptownbears.org/">Bear Week</a> (which was also tons of fun in a
<em>completely</em> different way), so I spent most of Friday taking the
<a href="http://baystatecruisecompany.com/">ferry</a> from Provincetown to Boston and the
<a href="https://amtrak.com/">train</a> from Boston to NYC. I found a simple and
affordable Airbnb that was a 20 minute walk from the conference location,
so I stayed there for the weekend, and my friend Ken agreed to host me for
my remaining time in the city.</p>
<p>I didn't get a whole lot of time to walk around the city on this trip, because
it was so full of things to do and people to meet with. However, I spent some
time exploring Queens and Brooklyn, and rediscovered just how diverse NYC really
is. People walk down the street chatting in every language under the sun, and
some of them rush from place to place while others stroll much more leisurely.
I like New York, though I'm not sure I'd want to live there. But who knows?
I've been in Boston for a long time now, and maybe it's time for a change...</p>
<h2 id="arrival">Arrival</h2><p>But on to the conference! I arrived at the United Nations building early on
Saturday morning, ready to get some breakfast and meet other Pythonistas.
However, there had been a miscommunication with the security guards, and
they weren't letting anyone in until 9 AM, which was when the opening keynote
was scheduled to begin! There were hundreds of people lined up outside the
building, wondering what was going on and hoping that the rigorous security
wouldn't cause them to miss their <del>flight</del> talk.</p>
<p>But by some luck, I happened to run into
<a href="https://twitter.com/freakboy3742">Russell Keith-Magee</a> and
<a href="https://twitter.com/kcunning">Katie Cunningham</a>, and we chatted while
waiting in line. I am continually amazed by how the technical community of
Australia manages send attendees to tech conferences in the United States:
Russell is from Perth, and not only did he attend PyGotham, he's also heading
to <a href="https://2016.djangocon.us/">DjangoCon</a> in Philadelphia and
<a href="http://www.pyohio.org/">PyOhio</a> in Ohio! (I also ran into him at PyCon in
Portland, although we didn't really chat.) Russell had <a href="https://twitter.com/PyBeeWare/status/750489112499539968">promised me a
Challenge Coin</a> for
<a href="https://github.com/lektor/lektor/pull/240">resolving a Lektor issue</a>, and
happily, <a href="https://twitter.com/singingwolfboy/status/754306894488039424">he delivered</a>!
Meanwhile, it turned out that Katie was part of
<a href="https://katieconf.xyz/">KatieConf</a>, a tech conference where all
the speakers are named Katharine (or similar). Unsurprisingly, she is also a
friendly and knowledgable person, and was knitting almost constantly as we
talked.</p>
<p>Shortly before 9, the security guards finally started letting people in --
and we found that we had to go through a metal detector and get our bags
scanned. (It wasn't as bad as the TSA airport security rigamarole, but it
was close.) Finally, we made it inside, and everyone swarmed the registation
booth to get their badges and a bite to eat. Fortunately, the conference
organizers deftly shifted the schedule to account for the disruption, changing
the start time of the opening remarks from 9 AM to 9:45 AM. It helped that
there were no printed schedules, which meant that everyone was relying on
checking the conference website on their phone. When the organizers updated
the website, all of a sudden everyone knew about the updated schedule!</p>
<h2 id="day-one">Day One</h2><p>I ended up attending very few talks at PyGotham: it wasn't quite as extreme
as <a href="/blog/2016/06/pycon-us-2016/">my PyCon experience</a>, but it was close.
Instead, I immediately declared a Birds of a Feather session on the topic
of Docker, and scoped out the BoF room in advance. In the process, I met
<a href="https://twitter.com/incunabulista">Laura</a>, who was new to programming but
eager to learn. We chatted for awhile, I told her about BoFs and why they're fun,
and I encouraged her to run one. (I found out the next day that she took my
advice!) I learned a bit about
<a href="https://2016.pygotham.org/talks/321/why-and-how-to-graphql/">GraphQL</a>, and then
walked back to the BoF room, hoping for a good turnout. Two other people
showed up. 😞 The three of us chatted for a bit, and then headed our separate
ways.</p>
<p>I then dropped in on a talk about
<a href="https://2016.pygotham.org/talks/255/making-games/">making games in Python</a>,
and managed to catch most of it. It's a nifty topic, and although the idea
of making games has never gotten me fired up the way that making websites does,
it's something I wanted to learn more about. I learned about the
<a href="https://github.com/pathunstrom/pursuedpybear">PursuedPyBear</a> game engine, which
is <em>way</em> up there on the list of "most adorable names," and maybe I'll try it
out some day.</p>
<p>After that was lunch, which was another organizational nightmare. PyGotham
wasn't the only conference happening at the UN that day:
<a href="https://2016.nyc.wordcamp.org/">WordCamp NYC</a> was going on at the same time,
which was pretty interesting. PyGotham and WordCamp were supposed to
have staggered lunch schedules, so that there wouldn't be a thousand people
all descending on the food at once. Unfortunately, something got screwed up
once again and both cons got lunch at the same time, which meant tremendously
long lines. Even worse, lunch was outside, and the searing summer sun combined
with the lack of shady spots meant that none of the computer nerds were
particularly happy — and both conferences were filled with computer nerds.</p>
<p>After lunch, I went to Russell's BoF about mental health, and learned about
the <a href="http://www.mentalhealthfirstaid.org/">mental health first aid certification</a>,
which I really need to investigate further. Mental health is a real issue that
most people don't want to address, and it can lead to some truly scary outcomes
like suicide. I'm really glad that organizations like <a href="https://osmihelp.org/">OSMI</a>
are starting to appear, giving people resources to talk about mental health
issues and remove stigma.</p>
<p>After the BoF, I saw a really interesting talk on
<a href="https://2016.pygotham.org/talks/226/summarizing-documents/">summarizing documents</a>,
most of which went over my head, but I was able to pick up a few really interesting
points. (The core problem with document summarization is picking out the most
interesting and important sentences in a long document, so this seems apt.)
I also caught Russell's talk on <a href="https://2016.pygotham.org/talks/240/a-tale-of-two-cellphones/">writing native apps on iOS and Android
using Python</a>,
and it made my head explode several times. I'm really keen on checking out
<a href="http://pybee.org/project/projects/libraries/toga/">Toga</a>,
<a href="http://pybee.org/project/projects/bridges/rubicon/">Rubicon</a>, and
<a href="http://pybee.org/project/projects/bridges/voc/">VOC</a>: maybe I'll build an
app or two! 😃</p>
<p>After his talk, I chatted with Russell a bit more, and we talked about funding
open source projects, something that <a href="https://medium.com/@nayafia">Nadia Eghbal</a>
recently wrote an <a href="http://www.fordfoundation.org/library/reports-and-studies/roads-and-bridges-the-unseen-labor-behind-our-digital-infrastructure/">extensive report</a>
about. I still need to read the actual report, but I've read some
<a href="https://storify.com/Lukasaoz/open-source-infrastructure-white-paper">excellent analyses of it</a>, and as a contributing member
of the open source community, I'm familiar with some of the problems. Of course,
Russell knows more about this than I do, but I think I was able to give him
some interesting ideas from my experience with edX.</p>
<p>The talks closed with <a href="https://2016.pygotham.org/talks/231/care-and-feeding-of-pytho/">Ewa Jodlowska's keynote</a>,
which had been originally planned as an opening keynote,
but had been hastily rescheduled due to the morning's timing issues.
The keynote had a lot of interesting data about how the Python Software Foundation
was funding Python development around the world, and it's great to see how
truly global the Python community is.</p>
<p>There was also an after-party, and I chatted with
<a href="https://github.com/llllllllll">Joe</a> and
<a href="https://github.com/ssanderson">Scott</a> for awhile. They're two other Boston-area
Python developers who I admire, and I don't get to hang out with them enough.
They do some crazy, frightening things with the deep guts of Python, and their
<a href="http://pytube.org/pycon-us-2016/scott-sanderson-joe-jevnik-playing-with-python-bytecode-pycon-2016.html">bytecode talk</a>
is <em>really</em> well done. They informed me that
<a href="https://www.quantopian.com/">Quantopian</a>, the company they both work for,
hosts a board game night every week, and now I think I <em>need</em> to show up for
one of those events.</p>
<h2 id="day-two">Day Two</h2><p>Walking into the UN on Sunday morning was a breeze compared with Saturday.
No more ridiculous lines! I saw a talk about neural networks, and then went
on to the real fun part of the morning: the beginner BoF and the lightning talks!</p>
<p>As I mentioned earlier, Laura took me up on my suggestion to run a BoF, and she
decided to focus on changing careers into the software world. I was happy that
I could join and provide some perspective as someone who had been living in the
software world for several years: I wish that I had someone to answer my
questions when I was just getting started, so I try to provide that for others
when I can. There were about eight or nine attendees, which I count as a great
success for a BoF!</p>
<p>After the BoFs were the lightning talks, which are always great fun. I went
first and gave a quick presentation about <a href="https://www.getlektor.com/">Lektor</a>,
which a lot of people seemed interested in. There were several other interesting
talks, but my favorite was one about the
<a href="https://www.jetbrains.com/pycharm/python-developers-survey-2016/">JetBrains Python developer survey</a>, which revealed some really interesting (and
sometimes surprising) insights about how people are using Python today.
Apparently 50% of Python developers use <a href="http://djangoproject.com/">Django</a>
in some capacity or other, and 40% use <a href="http://flask.pocoo.org/">Flask</a>!
That's an <em>incredible</em> amount of developer mindshare for such a
small and young project!</p>
<p>After the lightning talks came lunch, which was (thankfully) smoother than
the prior day's lunch. There were a few more talks and conversations after
that, but nothing particularly noteworthy until my presentation.</p>
<p>I presented my <a href="https://2016.pygotham.org/talks/328/advanced-git/">Advanced Git</a>
talk, and like every other conference I've presented it at, the room was packed
full. Seems like a lot of people want to know about Git! The talk went fairly
smoothly, although I did end up running out of time. (Fortunately, there was
a break immediately after my talk, so I had some flexibilty to go overtime.)
I got a lot of positive feedback afterwards, including one or two people who
said they were interested in hiring me for <a href="/consulting/">contract work</a>!
Hopefully one or both of them actually follows through. :)</p>
<p>Lastly, I had some time to go see <a href="https://github.com/morinted">Ted</a>'s talk about
<a href="https://2016.pygotham.org/talks/271/hacking-typing-writing-at/">stenography</a>
and see him type at over 200 words per minute. I knew nothing about steno
before I saw the talk, but now I'm curious to give it a try — it seems to be
chord-based typing based on the phoenetic pronunciation of words, usually on
specialized keyboards. Tim also described a fascinating evolution of open source
tearing down an existing set of expensive, proprietary systems and replacing
them with better, open systems like <a href="http://plover.stenoknight.com/">Plover</a>.
Anyone know where I can get my hands on a
<a href="https://github.com/openstenoproject/plover/wiki/Supported-Hardware">stenotype machine</a>?</p>
<h2 id="wrap-up">Wrap-Up</h2><p>It's now 1 AM and I'm tired, so I'll make this short. PyGotham was a wonderful
conference, and I'm really glad I decided to make the trip. I plan to come
back next year, and see what else the NYC community has in store!</p>
Making HTTPS Certificatesurn:uuid:5f68b6f9-4db1-3eb5-8bad-232a8d837bb82016-07-02T00:00:00ZDavid Baumgold<p>The latest website I'm working on needs to run over
<a href="https://en.wikipedia.org/wiki/HTTPS">HTTPS</a>, the secure version of
<a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol">HTTP</a>.
Turns out that getting HTTPS set up properly is really difficult!
Even worse, I need to set up HTTPS on not just one domain, but an <em>infinite</em>
number: I need to support
<a href="https://en.wikipedia.org/wiki/Wildcard_DNS_record">wildcard subdomains</a>!
Getting this to work right involved a <em>lot</em> of searching the web, a <em>lot</em>
of wasted time, and a <em>lot</em> of frustration. I'm writing it down to make life
easier for you, me, and everyone else that might run into this problem.</p>
<h2 id="encryption">Encryption</h2><p>Let's start at the beginning. HTTPS is about security, and when you're talking
about computers, security generally means
<a href="https://en.wikipedia.org/wiki/Encryption">encryption</a>.
HTTP is an unencrypted protocol, so anyone who is connected to your network
can see everything you're doing — including all your passwords and
secret information! HTTPS encrypts all of that data before it leaves your
computer, so that even if someone snoops on your connection, they can't
understand what's going on.</p>
<p>Encryption generally means using <a href="https://openssl.org">OpenSSL</a>, a widely-used
open source toolkit for anything and everything involving encryption.
For many years, the gold standard of encryption protocols was "secure sockets
layer", known as SSL, which is how OpenSSL got its name. However, computers
keep getting faster and faster, and security researchers keep finding new ways
to circumvent and break encryption.<sup class="footnote-ref" id="fnref-security-research"><a href="#fn-security-research" rel="footnote">1</a></sup>
As a result, SSL is no longer considered secure, and the new gold standard
is called "transport layer security", or TLS. However, it's too late to change
OpenSSL's name, so we still use it for TLS encryption.</p>
<p>In order to set up HTTPS, we need to use OpenSSL to create a TLS certificate.
This certificate is a special file that is used to encrypt and decrypt
information between the web server and the person viewing the website.</p>
<h2 id="a-basic-certificate">A Basic Certificate</h2><p>OpenSSL has been around for a long time, and its command line interface was
designed before common design patterns around command line interfaces had
been created. As a result, OpenSSL doesn't respect some basic conventions of
the command line, like double dashes in front of long flags and single
dashes in front of single-character flags. This contributes to OpenSSL's
reputation for being hard to use.</p>
<p>Here's a command that will create a basic TLS certificate for your website,
suitable to be used for HTTPS:</p>
<div class="highlight"><pre><span></span>$ openssl req -x509 -nodes -days <span class="m">365</span> -newkey rsa:2048 -sha256 <span class="se">\</span>
-keyout batman.key -out batman.crt -subj <span class="se">\</span>
<span class="s1">'/C=US/ST=New York/L=Gotham/O=Wayne Enterprises/OU=Batman/CN=batman.com/emailAddress=bruce@batman.com'</span>
</pre></div>
<p>Whoa, that command is <em>massive</em>! It should all be run on one line: the line
breaks that you see above are just for clarity. (If you take them out, though,
take out the slashes at the end of each line, as well!)
Let's break down how this command works.</p>
<ul>
<li><code>openssl</code>: the first word in the command tells the computer that we want
to run the <code>openssl</code> executable, and pass the rest of the command to it.</li>
<li><code>req</code>: this tells OpenSSL that we want to it operate in
certificate request mode. Normally, TLS certificates operate with two people
involved: one person to request the certificate, and one person to sign
the request and grant the certificate. However...</li>
<li><code>-x509</code>: this flag tells OpenSSL that actually, one person is going to play
both roles. You are going to request the certificate from yourself, and
then you are going to sign that request and grant yourself a certificate.
I know, it's weird and confusing. We'll come back to this later.</li>
<li><code>-nodes</code>: this isn't the word "nodes", like a node in a graph. This flag
actually means "no DES".
<a href="https://en.wikipedia.org/wiki/Data_Encryption_Standard">DES</a> is another
layer of encryption that OpenSSL generally applies to TLS certificates,
which sets a password on the certificate itself. When the DES layer is
applied, you have to type in that password before the web server can use
this certificate for HTTPS, which is not what we want.</li>
<li><code>-days 365</code>: a certificate has a lifetime that is generally measured in days.
After this lifetime is up, the certificate is no longer considered
trustworthy: the longer a certificate is in use, the more likely it is that
someone has found a way to hack it. (The people who make OpenSSL are paranoid.
They're also right.) This flag sets the certificate lifetime to one year.</li>
<li><code>-newkey rsa:2048</code>: because we're playing both roles (requester and signer),
we need to also make an
<a href="https://en.wikipedia.org/wiki/RSA_(cryptosystem)">RSA encryption key</a>
to sign the request with. This is not wasted work -- when we use this
certificate for HTTPS, the webserver will need this RSA key as well.
The <code>2048</code> refers to the number of "bits" that are used in creating this key.
More bits are more secure, and 2048 is plenty.</li>
<li><code>-sha256</code>: this tells OpenSSL to use the
<a href="https://en.wikipedia.org/wiki/SHA-2">SHA-256</a> hashing function when
constructing the certificate, instead of the default (weaker)
<a href="https://en.wikipedia.org/wiki/SHA-1">SHA-1</a> hashing function.</li>
<li><code>-keyout batman.key</code>: this tells OpenSSL to save the RSA key that it generates
in a file named <code>batman.key</code>. You can put in a different file name, if
you want.</li>
<li><code>-out batman.crt</code>: this tells OpenSSL to save the TLS certificate that it
generates in a file named <code>batman.crt</code>. You can put in a different file name,
if you want.</li>
</ul>
<p>The last part of the command is the <code>-subj</code> flag, which takes a string as an
argument. This string is called a "distinguished name" (don't ask me why),
and it must follow a specific format, that has exactly seven parts separated
by slashes:</p>
<ul>
<li><code>C</code>: short for "country". This is the two-letter country code of the country
you live in.</li>
<li><code>ST</code>: short for "state". This is the name of the state that you live in.</li>
<li><code>L</code>: short for "locality". This is the name of the city, town, or general
area that you live in.</li>
<li><code>O</code>: short for "organization". This is the name of the company you represent,
or if you don't represent a company, then it could be your own name.</li>
<li><code>OU</code>: short for "organizational unit". This is generally your department
within the company you represent. (OpenSSL can get a little too specific.)</li>
<li><code>CN</code>: short for "common name". <strong>This is important!</strong> You <em>must</em> put in the
<a href="https://en.wikipedia.org/wiki/Fully_qualified_domain_name">fully-qualified domain name</a>
of the website you are making this certificate for.</li>
<li><code>emailAddress</code>: an email address. In case someone needs to contact you,
I guess?</li>
</ul>
<p>When you run this command, OpenSSL will create two files for you: <code>batman.crt</code>
and <code>batman.key</code>. To verify that it worked properly, you can ask OpenSSL to
read these files and pull out the information you put in, like this:</p>
<div class="highlight"><pre><span></span>$ openssl x509 -text -in batman.crt
</pre></div>
<p>You'll get some output that looks like this:</p>
<pre><code>Certificate:
Data:
Version: 3 (0x2)
Serial Number:
8c:ef:c4:e3:3d:3c:31:8c
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=New York, L=Gotham, O=Wayne Enterprises, OU=Batman, CN=batman.com/emailAddress=bruce@batman.com
</code></pre>
<p>Your serial number will be different, and this initial data is followed by lots
more data that we don't actually care about.</p>
<h2 id="putting-the-certificate-in-place">Putting the Certificate In Place</h2><p>Now that you have a certificate, the next step is to give it to your web server,
so that it can use it to encrypt and decrypt HTTPS requests. I generally use
<a href="https://www.nginx.com/">nginx</a> as a
<a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a>: an HTTPS request
hits nginx, nginx decrypts the HTTPS into unencrypted HTTP, and forwards
that HTTP request along to another webserver running my application.
(This is a pretty common use-case for Nginx, and it's designed to handle it.)
Fortunately, <a href="http://nginx.org/en/docs/http/configuring_https_servers.html">Nginx has some great documentation on setting up HTTPS</a>,
using the certificate file and the key file that you just generated.</p>
<h2 id="dns">DNS</h2><p>The next step is setting up your domain with <a href="https://en.wikipedia.org/wiki/Domain_Name_System">DNS</a>
or something like it. Your browser will only accept your certificate as valid
if the URL matches the "common name" you created for your certificate. If
you're running your website locally for testing purposes, and you need
<code>batman.com</code> to point to your local computer, you can use the
<a href="https://en.wikipedia.org/wiki/Hosts_(file)">hosts file</a> to make that domain
point to your computer. If you're running on Mac, or Linux, you can run this
command:</p>
<div class="highlight"><pre><span></span>$ <span class="nb">echo</span> <span class="s2">"127.0.0.1 batman.com"</span> <span class="p">|</span> sudo tee -a /etc/hosts
</pre></div>
<p>That will add a line to the <code>/etc/hosts</code> file on your computer. That line
indicates that <code>batman.com</code> should point to <code>127.0.0.1</code>, the IP address for
your local computer.</p>
<h2 id="trust">Trust</h2><p>Great, now we can visit <code>https://batman.com</code> in the browser, and it should
work, right? Nope! Your computer doesn't trust the certificate you've created,
and it will show you a <a href="https://superuser.com/questions/632059/how-to-add-a-self-signed-certificate-as-an-exception-in-chrome">big scary warning</a>
if you try to visit the website. Why does it do that?</p>
<p>Let's get a bit paranoid. (Security people do that a lot.)
What if an attacker found a way to trick your computer into connecting
to the wrong website? What if you were trying to connect to your bank's website,
and some attacker tricked your computer into connecting to a different website
that looks exactly the same, but that the attacker controls? If you put your
bank password into that site, the attacker now knows your password, can log
into your bank account, and steal all your money. The good news is, HTTPS and
TLS are designed to prevent this sort of thing from happening. The bad news is,
if <em>you</em> can make a TLS certificate and set up an HTTPS website, what prevents
an attacker from doing it? After all, all you need in order to get a TLS
certificate is to run a command on your computer. Any attacker with some
technical ability could do the same thing.</p>
<p>There is a solution to this problem, and it's called a
<a href="https://en.wikipedia.org/wiki/Certificate_authority">certificate authority</a>,
or CA. A certificate authority is a company or organization that holds the digital
keys to the internet, and makes it their job to ensure that those keys are
secure. The three biggest CAs are Comodo, Symantec (who owns VeriSign),
and GoDaddy, but there are many others. Web browsers are
set up to trust these CAs by default<sup class="footnote-ref" id="fnref-ca-trust"><a href="#fn-ca-trust" rel="footnote">2</a></sup>,
but <em>not</em> trust just any old certificate.</p>
<p>So what do you do if you're <em>not</em> lucky enough to be a trusted CA, and you
want to get a trusted certificate? Well, do you remember the
certificate request-and-signing thing that I talked about earlier in this
blog post? That's what it's for. Instead of using OpenSSL to generate a
certificate directly, you can ask it to generate something called a
"certificate signing request", or CSR. You send that CSR to a trusted CA,
the CA approves it, and sends you a TLS certificate that is signed by the CA.
Then, web browsers can see that the certificate was issued by a trusted CA,
so therefore the certificate is trustworthy as well.</p>
<h2 id="i-trust-myself">I Trust Myself</h2><p>Well, that's all well and good for other certificates out there on the web,
but I know for a fact that this certificate is trustworthy. I know because I
generated it myself! So how do I tell my web browser to trust the certificate
anyway?</p>
<p>The answer to that question is going to depend on your operating system. I use
a Mac, and for that you can use the "Keychain Access" application, or the
<code>security</code> command line interface to that application. Here's how you indicate
that the certificate should be trusted:</p>
<div class="highlight"><pre><span></span>$ security add-trusted-cert batman.crt
</pre></div>
<p>You know, after all the time and effort figuring out how to generate
the certificate using OpenSSL, I'm shocked at how easy it is to trust it.
Thank goodness!</p>
<h2 id="success">Success</h2><p>Now, you can visit <code>https://batman.com</code> in your browser, and assuming you've
set up your web server correctly, it should work! You'll get a little padlock
in the URL bar and everything. Congrats!</p>
<h2 id="extra-credit:-wildcard-subdomains">Extra Credit: Wildcard Subdomains</h2><p>For you, this might be enough, but my specific task is more difficult. I need
to set up HTTPS not just for one domain, but for an infinite number of them!
I need it to work not just with <code>batman.com</code>, but also with <code>a.batman.com</code>,
<code>b.batman.com</code>, <code>c.batman.com</code>, and so on — and I don't know the full list
in advance! This is called a
<a href="https://en.wikipedia.org/wiki/Wildcard_DNS_record">wildcard subdomain</a>, and
I need to do something a bit different to get this to work.</p>
<p>For starters, I'll need to generate a new certificate, and this will be tricker.
If I just needed to support the wildcard subdomain, then I could just re-run
the command above and replace <code>CN=batman.com</code> with <code>CN=*.batman.com</code>, but if
I do that, the certificate will only be valid for the subdomains, and it
will <em>not</em> be valid for <code>batman.com</code> without any subdomains. We need to go
deeper!</p>
<h2 id="subject-alternate-names">Subject Alternate Names</h2><p>We already have a veritable alphabet soup of cryptographic names and acronyms,
but for this, we need to add two more: "X.509 Extensions" and
"Subject Alternate Names". <a href="https://en.wikipedia.org/wiki/X.509">X.509</a> is
a cryptographic standard that we already used earlier: the ridiculously long
<code>openssl</code> command had a <code>-x509</code> flag in there. This standard uses a few optional
extensions to add new features, and one of those is called <a href="https://en.wikipedia.org/wiki/Subject_Alternative_Name">"Subject Alternate
Names"</a>, or SAN.</p>
<p>SANs are used to indicate that there are multiple different ways to refer to
the same thing. In my case, <code>batman.com</code>, <code>a.batman.com</code>, <code>b.batman.com</code>, and
so on all refer to my website, so they can be seen as alternate names for
the same thing. In order to create a new TLS certificate with OpenSSL that
uses SANs, we need to set up a config file for OpenSSL. Create a file
named "batman.cnf" with the following content:</p>
<div class="highlight"><pre><span></span><span class="k">[req]</span>
<span class="na">x509_extensions</span> <span class="o">=</span> <span class="s">v3_req</span>
<span class="k">[v3_req]</span>
<span class="na">basicConstraints</span> <span class="o">=</span> <span class="s">CA:FALSE</span>
<span class="na">keyUsage</span> <span class="o">=</span> <span class="s">nonRepudiation, digitalSignature, keyEncipherment</span>
<span class="na">subjectAltName</span> <span class="o">=</span> <span class="s">@alt_names</span>
<span class="k">[alt_names]</span>
<span class="na">DNS.1</span> <span class="o">=</span> <span class="s">batman.com</span>
<span class="na">DNS.2</span> <span class="o">=</span> <span class="s">*.batman.com</span>
</pre></div>
<p>As you can see, this file is divided into three sections: <code>req</code>, <code>v3_req</code>,
and <code>alt_names</code>. <code>req</code> references the fact that we are running OpenSSL in
certificate request mode — it's the second part of the long <code>openssl</code> command
we ran earlier. Within that section, we indicate that information about
X.509 extensions is available in the <code>v3_req</code> section.</p>
<p>The <code>v3_req</code> section is mostly copied from the default configuration. I'll be
honest, I have no idea what <code>basicConstraints</code> and <code>keyUsage</code> are for, I just
left them at their default values from the
<code>/System/Library/OpenSSL/openssl.cnf</code> file on my computer. However, the last
line of that section is <code>subjectAltName</code>, which is exactly what we want to
configure. You could pass a comma-separated list of values, but it's clearer
to reference another section, as I've done here with the name <code>@alt_names</code>.</p>
<p>Finally, the <code>alt_names</code> section has all the alternate names that this TLS
certificate should support. They need to be ordered, which is why one of them
is <code>DNS.1</code> and the other is <code>DNS.2</code>. You can also use IP addresses, if you
prefer.</p>
<h2 id="making-the-certificate">Making the Certificate</h2><p>Now that we've made the config file, we need to tell the <code>openssl</code> command
to read it. Fortunately, that's not hard — we just need to add a
<code>-config</code> flag to the command, along with the path to that file we just created.
So, if you're running this command in the same directory as the file you've
created, then it's going to be:</p>
<div class="highlight"><pre><span></span>$ openssl req -config batman.cnf -x509 -nodes -days <span class="m">365</span> -newkey rsa:2048 -sha256 <span class="se">\</span>
-keyout batman-san.key -out batman-san.crt -subj <span class="se">\</span>
<span class="s1">'/C=US/ST=New York/L=Gotham/O=Wayne Enterprises/OU=Batman/CN=batman.com/emailAddress=bruce@batman.com'</span>
</pre></div>
<p>This time, we'll get <code>batman-san.key</code> and <code>batman-san.crt</code>. Let's verify that
it worked properly!</p>
<div class="highlight"><pre><span></span>$ openssl x509 -text -in batman-san.crt
</pre></div>
<p>This time, if you scroll down through the output of that command, you'll see
the following section:</p>
<pre><code>X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment
X509v3 Subject Alternative Name:
DNS:batman.com, DNS:*.batman.com
</code></pre>
<p>Perfect! Put the new certificate in nginx, mark it as trusted in your operating
system, and view your website. Now you've got HTTPS with wildcard subdomains!</p>
<h2 id="wrap-up">Wrap Up</h2><p>Security is hard to understand, and the tools don't make it any easier. Tell me
what is still confusing about this process, and maybe I can make it clearer!
Do <em>you</em> need to set up HTTPS on your site? Have you run into trouble?
Leave a comment on this post! Write your own blog post! Talk about it with
others! The more we communicate and collaborate, the more we all learn.</p>
<div class="footnotes">
<hr>
<ol><li id="fn-security-research"><p>Some people think that anyone trying to break encryption is a criminal.
After all, if someone is trying to get access to your private data,
they're probably out to get you, right? However, security researchers
are actually <em>good</em> people, who try to prove that the encryption techniques
used to keep your information safe are actually secure and effective.
If a criminal discovers that a particular encryption technique
is not effective, that criminal would try to exploit that knowledge to
steal information and cause harm. By contrast, if a security researcher
discovers that a particular encryption technique is not effective, that
researcher will disclose that information to the right people, help them
replace their faulty encryption with better and more secure encryption,
and then let the world know that the encryption technique can no longer
be considered secure.</p>
<p>Criminals will try to steal information and cause harm, regardless of if
researchers try to figure out how they're doing it. Researchers are the
ones who can discover and prevent criminals from stealing information
and causing harm, and they do so by helping people replace bad encryption
with good encryption.<a href="#fnref-security-research" rev="footnote">↩</a></p></li>
<li id="fn-ca-trust"><p>But what happens if a trusted certificate authority shows themselves to
be untrustworthy? Then the other CAs come together and collectively
revoke trust in the untrustworthy CA, and that information flows quickly
from the CAs to every browser in the world. It's actually a pretty nifty
system.<a href="#fnref-ca-trust" rev="footnote">↩</a></p></li>
</ol>
</div>
Learning Reacturn:uuid:f1e02284-40aa-342b-a808-dcfd511284a72016-06-16T00:00:00ZDavid Baumgold<p>I've been working on a new project lately, and I figured it was time to bite the
bullet and finally look into <a href="https://facebook.github.io/react/">React</a>, the
hot new front-end Javascript framework that everyone in the web developer
community is talking about. If you're not familiar with the web developer
community, it truly is <em>very</em> fad driven. I heard about React months ago, but
didn't bother learning it because I figured that in a month or two, everyone
would have moved on to some other popular JS framework. However, React seems to
have some staying power, and since <a href="https://www.getlektor.com/">Lektor's</a>
admin pages are built with it, I decided to finally learn it.</p>
<p>The upshot is, React is very nice! It introduces a few innovative ideas for
Javascript development, but mostly builds on a solid framework of functional
programming and familiar ideas. I was able to get up and running pretty quickly,
and while the tooling was a challenge to set up, the end result works quite
well. The code is clear and understandable, and I can see how everything fits
together. I think I'll be using React a lot more in my future projects.</p>
<h2 id="jsx">JSX</h2><p>The first hurdle to learning React is understanding
<a href="https://facebook.github.io/react/docs/jsx-in-depth.html">JSX</a>.
JSX is a dialect of Javascript that allows you to write HTML directly
into your code. Without JSX, if you wanted to create and return a complex
HTML element, you might do so using <a href="https://jquery.com/">jQuery</a>, like this:</p>
<div class="highlight"><pre><span></span><span class="kd">function</span> <span class="nx">makeInput</span><span class="p">(</span><span class="nx">contents</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">$</span><span class="p">(</span><span class="s2">"<input>"</span><span class="p">,</span> <span class="p">{</span><span class="nx">type</span><span class="o">:</span> <span class="s2">"text"</span><span class="p">,</span> <span class="nx">value</span><span class="o">:</span> <span class="nx">contents</span><span class="p">,</span> <span class="s2">"class"</span><span class="o">:</span> <span class="s2">"form-control"</span><span class="p">})</span>
<span class="p">}</span>
</pre></div>
<p>Incidentally, a lot of people don't know that <a href="https://api.jquery.com/jQuery/#jQuery2">you can use jQuery to create
structed HTML elements</a>. It's quite a
nice API. However, it's not as nice as what JSX allows you to do:</p>
<div class="highlight"><pre><span></span><span class="kd">function</span> <span class="nx">makeInput</span><span class="p">(</span><span class="nx">contents</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"text"</span> <span class="na">value</span><span class="o">=</span><span class="p">{</span><span class="nx">contents</span><span class="p">}</span> <span class="na">className</span><span class="o">=</span><span class="s">"form-control"</span> <span class="p">/></span>
<span class="p">}</span>
</pre></div>
<p>You might look at that snippet and instantly think, "Wait a minute, that's
not valid JavaScript! That won't work at all!" Well, you're right, it's not
valid JavaScript. However, it <em>is</em> valid JSX, and it gets compiled into
valid JavaScript that does the same sort of thing that the first snippet does.
The general term for this idea is <a href="https://en.wikipedia.org/wiki/Syntactic_sugar">syntactic sugar</a>
-- it makes a certain idea clearer or easier to express in a programming language,
without fundamentally changing how the language works.</p>
<p>So why is JSX important for React? Because React makes you define your own
web components in a language that is similiar to HTML, and allows you to
mix and match your custom components with HTML. A classic example is embedding
an interactive map into your website. Before React, you had to do something
like this, as shown in <a href="https://developers.google.com/maps/documentation/javascript/tutorial">the offical Google Maps documentation</a>:</p>
<div class="highlight"><pre><span></span><span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"map-wrapper"</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span><span class="p">></span>Here's a map!<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="p"><</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">"map"</span><span class="p">></</span><span class="nt">div</span><span class="p">></span>
<span class="p"><</span><span class="nt">script</span><span class="p">></span>
<span class="kd">var</span> <span class="nx">map</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">initMap</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">map</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">google</span><span class="p">.</span><span class="nx">maps</span><span class="p">.</span><span class="nx">Map</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'map'</span><span class="p">),</span> <span class="p">{</span>
<span class="nx">center</span><span class="o">:</span> <span class="p">{</span><span class="nx">lat</span><span class="o">:</span> <span class="o">-</span><span class="mf">34.397</span><span class="p">,</span> <span class="nx">lng</span><span class="o">:</span> <span class="mf">150.644</span><span class="p">},</span>
<span class="nx">zoom</span><span class="o">:</span> <span class="mi">8</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p"></</span><span class="nt">script</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
</pre></div>
<p>That's not bad, but what if we could just do this, instead?</p>
<div class="highlight"><pre><span></span><span class="p"><</span><span class="nt">div</span> <span class="na">className</span><span class="o">=</span><span class="s">"map-wrapper"</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span><span class="p">></span>Here's a map!<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="p"><</span><span class="nt">GoogleMap</span>
<span class="na">defaultCenter</span><span class="o">=</span><span class="p">{{</span> <span class="nx">lat</span><span class="p">:</span> <span class="o">-</span><span class="m">34.397</span><span class="p">,</span> <span class="nx">lng</span><span class="p">:</span> <span class="m">150.644</span> <span class="p">}}</span>
<span class="na">defaultZoom</span><span class="o">=</span><span class="p">{</span><span class="m">8</span><span class="p">}</span>
<span class="p">/></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
</pre></div>
<p><code><GoogleMap></code> is a custom React component that will handle all the details of
inserting an interactive Google Map element into your page and setting it up
properly. JSX allows you to ignore the implementation and just <em>use</em> it,
putting the <a href="https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)">principle of encapsulation</a>
to work. And this isn't a contrived example:
<a href="https://github.com/tomchentw/react-google-maps">react-google-maps</a>
is a popular project on GitHub, with a
<a href="http://react-google-maps.tomchentw.com/">live demo</a>
if you're curious.</p>
<p>The only annoyance I've found with JSX so far is that you can't use the
<code>class</code> parameter, because "class" is a
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Reserved_keywords_as_of_ECMAScript_6">reserved keyword in JavaScript</a>, and JSX is
built on JavaScript. Instead, you use <code>className</code>, which is
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/className">part of JavaScript's Element API</a>.
To be honest, I don't understand why JSX can't use "class" as syntactic sugar
for "className", so that the developer doesn't have to think about it.
On the scale of things, it's a very minor quibble.</p>
<h2 id="components">Components</h2><p>Once you have a basic understanding of JSX, you can start using React. React
makes you think about your user interface as a group of "components": logical
pieces of your UI that fit together into larger pieces. There is an <a href="https://facebook.github.io/react/docs/thinking-in-react.html">excellent
blog post about how to do this</a>, but I'll reproduce the relevant parts here. Let's
say you're creating an online store, and you need to build a filterable
table of products, which will look something like this:</p>
<p><img src="/blog/2016/06/learning-react/react-filterable-table.png" alt=""A table of items and prices. There is a search bar at the top, to filter the contents of the table.""></p>
<p>This is a component of a user interface: a logical unit that you could place
on a page. However, this component is itself made up of other components, as
you can see here:</p>
<p><img src="/blog/2016/06/learning-react/react-filterable-table-components.png" alt=""The same image as before, with a box around the search bar, a box around the table, and a box around each row in the table.""></p>
<p>By dividing up each component into simpler and simpler pieces, it becomes easier
to reason about it. Now, instead of implementing the whole filterable table
component at once, we can represent it like this:</p>
<ul>
<li><code>FilterableProductTable</code> (orange)<ul>
<li><code>SearchBar</code> (blue)</li>
<li><code>ProductTable</code> (green)<ul>
<li><code>ProductCategoryRow</code> (turquoise)</li>
<li><code>ProductRow</code> (red)</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Each of these components is smaller, and therefore easier to implement.
We can also make them work together, by passing data between parent components
and children components. For example, when a user types into the <code>SearchBar</code>
component, it tells the parent <code>FilterableProductTable</code> component what the user
typed. Then, the <code>FilterableProductTable</code> component tells the <code>ProductTable</code>
component what data to display, and it only passes data that matches what the
user has typed. The <code>ProductTable</code> component doesn't have to know or care
about how the filtering works — it just renders whatever data it receives!</p>
<h2 id="es6-and-babel">ES6 and Babel</h2><p>Onto the code! Here's an example of a basic "Hello, World!" component in React:</p>
<div class="highlight"><pre><span></span><span class="k">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s">'react'</span><span class="p">;</span>
<span class="kd">class</span> <span class="nx">HelloWorld</span> <span class="k">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p"><</span><span class="nt">p</span><span class="p">></span>Hello, World!<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>Wait a minute, this doesn't even look like JavaScript! Imports? Classes?
What's going on here?!</p>
<p>The answer is, this <em>is</em> JavaScript, but it's not the JavaScript you're used to.
JavaScript is an implementation of a standard called
<a href="https://en.wikipedia.org/wiki/ECMAScript">ECMAScript</a>, and the ECMAScript
standard continues to evolve. The JavaScript that we know today is based on
ECMAScript version 5, and unfortunately, it is an ugly language, full of gotchas
and syntax warts. (I love JavaScript, but it has a lot of problems.)</p>
<p>However, the Ecma standards body has dramatically changed the language with version
6, and it's actually quite nice. As a result,
many people writing React are writing it using ECMAScript version 6, also
known as ES6 or <a href="https://babeljs.io/docs/learn-es2015/">ES2015</a>
(because that version was formalized in the year 2015).
Since <a href="https://kangax.github.io/compat-table/es6/">web browsers don't fully support ES6 yet</a>,
this code is compiled back into ECMAScript 5
(the JavaScript that we all know and <del>love</del> deal with)
before it's sent to the browser.</p>
<p>How does that compilation happen? In a word, <a href="https://babeljs.io/">Babel</a>.
Babel is a pluggable JavaScript compiler that has two very popular preset
plugin bundles:
<a href="https://babeljs.io/docs/plugins/preset-es2015/">es2015</a>, for compiling
ES6 into ES5, and <a href="https://babeljs.io/docs/plugins/preset-react/">react</a>,
for compiling JSX. So, feed your beautiful, future-oriented
ES6 JSX files into Babel, and it will spit out the ugly-but-practical ES5
JavaScript files that today's browsers can use.</p>
<p>As I said, the tooling is a challenge for React — but the end result is that
developers get to write clearer, better code, which I'm all in favor of.
Personally, I think the trade-off is worth it. That being said, you can write
React without using ES6 <em>or</em> JSX, if you really don't want to set up Babel.
But once you've got your tooling set up correctly, writing ES6 JSX is a joy,
and it's surprisingly intuitive.</p>
<h2 id="react">React</h2><p>Finally, we can start talking about React itself! React isn't an
<abbr title="Model-View-Controller">MVC</abbr>
framework per se, it's more of a structure for composable elements that
communicate. There are no templates, unless you count JSX as a template
(which it sort of is). You write your components, wire them together,
and let React handle state changes easily and automatically.</p>
<p>What does that actually <em>mean</em>, though? Well, here's an example:</p>
<div class="highlight"><pre><span></span><span class="kd">class</span> <span class="nx">GreetingBox</span> <span class="k">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
<span class="nx">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="p">();</span>
<span class="c1">// Set up the state of this component. By default, "name" is empty.</span>
<span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="p">{</span><span class="nx">name</span><span class="p">:</span> <span class="s">""</span><span class="p">};</span>
<span class="c1">// Bind the setName() function to this class instance.</span>
<span class="k">this</span><span class="p">.</span><span class="nx">setName</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">setName</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">setName</span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Call the setState() function, which is built-in to React.</span>
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span><span class="nx">name</span><span class="p">:</span> <span class="nx">value</span><span class="p">});</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Return our two sub-components, wrapped in a <div>.</span>
<span class="c1">// This component is the only component that manages state, so we have to</span>
<span class="c1">// inform the children components of the current state of the GreetingBox</span>
<span class="c1">// -- and the NameInput component needs a way to update the GreetingBox's</span>
<span class="c1">// state.</span>
<span class="k">return</span> <span class="p"><</span><span class="nt">div</span><span class="p">></span>
<span class="p"><</span><span class="nt">NameInput</span> <span class="na">name</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span> <span class="na">setName</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">setName</span><span class="p">}</span> <span class="p">/></span>
<span class="p"><</span><span class="nt">Greeting</span> <span class="na">name</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span> <span class="p">/></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">NameInput</span> <span class="k">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
<span class="nx">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="p">();</span>
<span class="c1">// Bind the setName() function to this class instance.</span>
<span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">handleChange</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Call the setName method that GreetingBox passed to this component,</span>
<span class="c1">// with the value from the <input> HTML element.</span>
<span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">setName</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Setting the onChange attribute allows React to bind the handleChange</span>
<span class="c1">// function on this class instance whenever it needs to.</span>
<span class="k">return</span> <span class="p"><</span><span class="nt">label</span><span class="p">></span>
What's your name?
<span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"text"</span> <span class="na">value</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span> <span class="na">onChange</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">handleChange</span><span class="p">}</span> <span class="p">/></span>
<span class="p"></</span><span class="nt">label</span><span class="p">></span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">Greeting</span> <span class="k">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">name</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">name</span><span class="p">.</span><span class="nx">length</span> <span class="o">==</span> <span class="m">0</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p"><</span><span class="nt">p</span><span class="p">></span>I don't know who you are.<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="p"><</span><span class="nt">p</span><span class="p">></span>Hello, <span class="p">{</span><span class="nx">name</span><span class="p">}</span>!<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
<p>When we first run this code, we'll end up with the following HTML:</p>
<div class="highlight"><pre><span></span><span class="p"><</span><span class="nt">div</span><span class="p">></span>
<span class="p"><</span><span class="nt">label</span><span class="p">></span>
What's your name?
<span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"text"</span> <span class="na">value</span><span class="o">=</span><span class="s">""</span><span class="p">></span>
<span class="p"></</span><span class="nt">label</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span><span class="p">></span>I don't know who you are.<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
</pre></div>
<p>When you type in the name field, the greeting will automatically update
to reflect whatever you've written. For example:</p>
<div class="highlight"><pre><span></span><span class="p"><</span><span class="nt">div</span><span class="p">></span>
<span class="p"><</span><span class="nt">label</span><span class="p">></span>
What's your name?
<span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="o">=</span><span class="s">"text"</span> <span class="na">value</span><span class="o">=</span><span class="s">"David"</span><span class="p">></span>
<span class="p"></</span><span class="nt">label</span><span class="p">></span>
<span class="p"><</span><span class="nt">p</span><span class="p">></span>Hello, David!<span class="p"></</span><span class="nt">p</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
</pre></div>
<p>You can even try to type in HTML tags to break it, and React will
automatically escape them for you. It's pretty nifty!</p>
<p>There are a few important things to note about the way that React works, which
I think are pretty cool.</p>
<h3 id="html-is-lowercase-components-are-capitalized">HTML is Lowercase, Components are Capitalized</h3><p>React components have names that begin with a capital letter, which
distinguishes them from HTML tags that are lowercase. This allows you to mix
HTML and components while still easily keeping track of which is which.</p>
<h3 id="one-directional-state">One-Directional State</h3><p>State always flows downward. This helps you to untangle messy, state-driven
UIs: state must always be kept in the highest-level component that encompasses
all other components that <em>use</em> that state. That's why the state in this
example is kept in the GreetingBox component: both sub-elements need to access
that state information. Parents can extend tools to their children that allow
the child to update the parent's state, such as the <code>setName</code> method in the
example, but the child doesn't actually need to maintain any state itself.</p>
<h3 id="automatic-re-rendering">Automatic Re-Rendering</h3><p>Any time the state in a component is modified, that component is re-rendered
automatically, and all of its children are re-rendered as well if necessary.
This is what makes the UI feel snappy and alive: it always displays the latest
information about the state of the world.</p>
<h2 id="what-next?">What Next?</h2><p>The next thing I need to look into is <a href="http://redux.js.org/">Redux</a>, which is
a state container for JavaScript applications. It is often paired with React
applications, although it may be overkill for my use-case.</p>
<p>And just what <em>is</em> that use-case, you ask? What am I building? That's a bit
of a secret right now, but hopefully I'll be able to reveal it soon. I'll
give you a hint, though — it's related to Lektor!</p>
PyCon US 2016urn:uuid:0a59c355-f2ea-3f40-a37e-329e78d6d0ac2016-06-03T00:00:00ZDavid Baumgold<p>PyCon! My favorite conference of the year, full of friends, projects, wacky
ideas, and unexpected connections. I'm glad I had a few days between
<a href="/blog/2016/05/wtd-na-2016/">Write the Docs</a> and PyCon to recover and prepare,
because this year's PyCon was exhausting, inspiring, and wonderful.</p>
<h2 id="about-pycon">About PyCon</h2><p>For those of you who don't know, <a href="http://us.pycon.org/">PyCon</a> is a conference
for programmers who use the <a href="http://python.org/">Python programming language</a>.
There are <a href="http://www.pycon.org/">many different regional PyCons held around the world</a>,
but when people say "PyCon" they're usually referring to PyCon US, which is the
largest one by far. PyCon has about 3000 attendees, so it's a medium-size
conference: large enough to be impressive and to attract sponsorships,
but small enough that you keep on running into your friends over and over again
throughout the convention. The convention sells out every year, and they run
out of tickets faster and faster each time — the conference organizers could
easily make PyCon much larger, but doing so would increase ticket prices,
limit the options for where the conference could be held, and disrupt the feel
of the conference.</p>
<p>And PyCon does have a distinctive <em>feel</em>. The Python community is remarkably
friendly, and cares deeply about diversity and outreach. Most programming
languages have communities that consist almost entirely of white cis-gendered
men, most of whom are straight; women and minorities are nowhere to be found.
By contrast, the Python community is full of women, racially diverse, and has
many prominent LGBT individuals. This year, <a href="https://twitter.com/jessicamckellar/status/737299461563502595">40% of the speakers at PyCon
were women</a>,
which is absolutely unheard of for a tech conference. (Most tech conferences
have a single female speaker, or none at all.) And the community won't be
satisfied until we have gender parity, as well as better representation for
racial diversity and LGBT representation. (During this year's PyCon,
<a href="https://en.wikipedia.org/wiki/Guido_van_Rossum">Guido van Rossum</a>,
the creator of Python and its "benevolent dictator for life",
publicly re-committed to the idea of bringing on at least two female developers
to the currently all-male core development team.) All this diversity makes
for an incredibly welcoming and friendly environment: no matter who you are
or where you come from, you can find friends at PyCon. Differing opinions
and cultures are welcome, and the conference explicitly provides space for
people to organize their own events, known as "open spaces", as I'll describe
later.</p>
<p>PyCon is divided into three parts: two days of tutorials, three days
of talks, and four days of coding sprints. The talks are the main body
of the conference, and many people only attend PyCon for that part of the
conference. There are five tracks of half-hour talks happening simultaneously,
and this year there were 95 talks in all: it's impossible to see everything!
Before the talks comes the tutorials; each tutorials is three hours long and
capped at around twenty students or so, so that attendees can get a deep dive
into their subject matter of choice. After the talks is the coding sprints,
where people collaborate on open source projects and make tremendous progress
on improving the software that the whole world runs on. The three parts form
a nice arc of intensity: ramping up during the tutorials, full excitement
during the talks, and gradually trailing off during the sprints.</p>
<h2 id="tutorials">Tutorials</h2><p>This year, I was an instructor for one of the PyCon tutorials: <a href="https://us.pycon.org/2016/schedule/presentation/1620/">Get Started
With Git</a>. Although I
was a bit nervous going into it, I was well-prepared, and the tutorial went
quite smoothly. I owe a tremendous debt of gratitude towards
<a href="https://twitter.com/gilforsyth">Gil Forsyth</a>, who was a brilliant
teaching assistant and allowed me to get through my entire
<a href="https://speakerdeck.com/singingwolfboy/get-started-with-git">deck of 90 slides</a>
without being derailed by technical issues. The entire tutorial was recorded
and posted on YouTube as well!</p>
<div class="video-container">
<iframe src="<a href="https://www.youtube.com/embed/Qthor07loHM">https://www.youtube.com/embed/Qthor07loHM</a>" frameborder="0" allowfullscreen> </iframe></div><p>If you want me to train your team to use Git better,
<a href="/training">contact me</a>!</p>
<p>After my tutorial was done, I spent most of the rest of the tutorial days
talking to other PyCon attendees. There were some people there that I already
knew, and many that I didn't, and I found that I really enjoyed chatting with
both groups. I spent a significant amount of time chatting with
<a href="https://treyhunner.com">Trey Hunner</a> about <a href="/consulting">consulting</a> and
<a href="/training">training</a>, and he gave me a lot of insight and information that
I hope to apply in my growing freelance endeavors. I also chatted for awhile
with <a href="https://twitter.com/AlSweigart">Al Sweigart</a>, a very accomplished
author of technical books, and we talked about writing for awhile.</p>
<p>Over the course of these two days, I realized that I was getting a <em>lot</em> out
of these conversations — not just enjoyment, but also useful information and
professional contacts. In the past, I've always spent a lot of time attending
talks at PyCon, but this year, I decided to try something very different: I
would focus on the "hallway track" and have conversations with everyone I could
<em>instead</em> of attending talks.</p>
<h2 id="talks-and-open-spaces">Talks and Open Spaces</h2><p>I had a whole list of talks I wanted to see at PyCon, but following the decision
I made during the tutorials, that list went out the window. Instead, I filled
my time with hallway conversations, the expo hall, and some pretty awesome
open spaces.</p>
<p><a href="https://us.pycon.org/2016/events/open-spaces/">Open spaces</a> are ad-hoc groups
that come together to discuss any topic of common interest, Python-related or
not. There are several rooms in the conference center dedicated to open spaces,
and the conference organizers provide a large "open space board"
near the registration desk that has a large, empty grid on it. This grid lists
room numbers along one axis and times along the other. Anyone can declare
an open space by writing a topic on an index card and
pinning it to the open space board, which claims a time and location. Attendees
were constantly clustered around this board, so it became a social hub as
well as an information hub, and it was a great place to meet other people.</p>
<p>I declared two open spaces,
<a href="https://twitter.com/singingwolfboy/status/737037818862698497">one on Lektor</a> and
<a href="https://twitter.com/singingwolfboy/status/737654139782451201">one on Docker</a>.
Both were very popular, and drew perhaps 15 or 20 other attendees, which is a
success as far as I'm concerned. I also attended many, many other open spaces
during the conference, on topics ranging from Flask and GIS to an LGBT
meet-and-greet and acroyoga. I really enjoyed the open space dedicated
to people who wanted to get their first software development job; I talked about
my experiences in the software industry, answered a lot of questions, and
was able to provide some good advice regarding projects and visibility.
I also really liked the "random storytelling" open space, even though I arrived
late; it was simply an open forum for people to tell each-other stories from
their own life experiences. Some people shared some deeply personal and
meaningful stories, talking about moments of great excitement and joy,
sadness and fear, curiousity and discovery. It really brought home the idea
that PyCon is about <em>people</em> more than it is about <em>programming</em>.</p>
<p>I also talked with a lot of interesting people in the hallway and in the
expo hall. I talked with
<a href="https://twitter.com/necaris">Rami</a>, <a href="https://twitter.com/yarkot">Yarko</a>,
<a href="http://www.python-academy.com/">Mike</a>, and <a href="https://twitter.com/Andriod">Andy</a>
about opportunities for consulting, training, and collaboration.
I met <a href="https://www.linkedin.com/in/fvalcarcel">Frank</a> and
<a href="https://www.linkedin.com/in/emily-morehouse-10567459">Emily</a> from
<a href="https://www.cuttlesoft.com/">Cuttlesoft</a>, who started their own Python
consulting company (which I think is <em>very</em> cool).
I learned more about Git internals from
<a href="https://twitter.com/glenjarvis">Glen</a> and <a href="https://twitter.com/doctaphred">Fred</a>.
I met some of the core developers and maintainers of <a href="http://flask.pocoo.org/">Flask</a>,
including <a href="https://twitter.com/davidism">David</a> — not only do we share a name,
but we also share an interest in a beautiful microframework. I helped
<a href="https://twitter.com/ericmjl">Eric</a> and <a href="https://twitter.com/RileyRustad">Riley</a>
with deploying websites. And that's just a slice of the dozens of people
that I got to talk with, however briefly. It was exhausting, but <em>so</em> worth it.</p>
<p>In the end, the only conference talk I attended was my own. In addition to
teaching a tutorial, I also presented a talk about
<a href="https://us.pycon.org/2016/schedule/presentation/1694/">prototyping APIs in Flask</a>,
which also went well.
I was approached by several people afterwards who told me that they
<a href="https://twitter.com/seanmylaw/status/737726142279360512">really</a>
<a href="https://twitter.com/natea/status/737728674976256000">enjoyed</a>
<a href="https://twitter.com/CreedJarrett/status/737728169457881088">my</a>
<a href="https://twitter.com/dfflanders/status/737728593606807552">talk</a>,
and couldn't wait to start building their own APIs
-- and as far as I'm concerned, that's the highest possible praise. Like my
tutorial, the talk was also recorded and uploaded to YouTube for your
viewing pleasure!</p>
<div class="video-container">
<iframe src="<a href="https://www.youtube.com/embed/6RdZNiyISVU">https://www.youtube.com/embed/6RdZNiyISVU</a>" frameborder="0" allowfullscreen> </iframe></div><p>Want to learn more about Flask and APIs? <a href="/training">Contact me</a> to talk about
technical training.</p>
<h2 id="sprints">Sprints</h2><p>I'm currently finishing up the coding sprints, and even though the main part
of the conference is over, the suprises keep coming. I chatted for awhile
with the amazing <a href="https://twitter.com/joshsimmons">Josh Simmons</a>, who gave me
some great advice about training and networking. On top of that, I helped
a fellow attendee
<a href="https://twitter.com/joelgarzatx/status/738487532548030464">land his first open source contribution</a> and I
<a href="https://twitter.com/singingwolfboy/status/738491884373483520">became a maintainer for Lektor</a>,
and frankly it's hard to say which one makes me more proud. I wrote subtitles
for my Flask talk, and found another attendee who was such a fan of my talk
that he agreed to translate those subtitles into Spanish, so that more people
could learn. Amazing!</p>
<p>I haven't actually made much progress on improving open source projects, which
is the supposed goal of the coding sprints. However, if there's one thing I've
learned from this year's conference, it's that there's more than one way
to do a conference well. I've had a fantastic PyCon,
and I look forward to next year's conference, as well as all the Python people
and Python events I run into before then!</p>
Write the Docs 2016urn:uuid:b90a6ecd-f26b-3063-8059-48a7bf054b0c2016-05-25T00:00:00ZDavid Baumgold<p>I attended <a href="http://www.writethedocs.org/conf/na/2016">Write the Docs</a>, a
conference focused on technical documentation and those who write it.
Although I'm not a documentarian (one who works in the field of documentation),
it was an interesting and enlightening experience.</p>
<h2 id="portland">Portland</h2><p>The conference was held at the <a href="http://www.mcmenamins.com/CrystalBallroom">Crystal Ballroom</a>
in Portland, OR. I arrived a few days early to beat the jetlag, and stayed in
an <a href="https://www.airbnb.com/c/dbaumgold">Airbnb</a> nearby. Walking around Portland was
a lot of fun: the city has a very different feel from Boston. Art and individual
expression abound, and I think the locals are required by law to have at
least one tattoo.</p>
<p>I also made sure to check out some of the famous locations in Portland.
I had heard of <a href="http://www.powells.com/">Powell's Books</a>,
but nothing could have prepared me for the experience of walking inside.
Seeing all the books and the patrons enjoying them, feeling the quiet, joyful
vibe of knowledge and reading — it was intense in a way that I can't quite
describe. I saw <a href="https://panic.com/">Panic</a> across the street,
and gleefully tried <a href="http://sign.panic.com/">playing with their sign</a>, only
to discover that <a href="https://twitter.com/singingwolfboy/status/733855640548823040">the website was broken</a>. Hopefully it'll be fixed soon! I got a burger at
<a href="http://theroxydiner.com/">The Roxy</a>, and enjoyed seeing the other customers
-- including one who was dressed head-to-toe in a tight latex bodysuit, pencil
skirt, and platform heels. (This was a perfectly ordinary outfit for the
Roxy, as I understand.)</p>
<h2 id="pre-conference">Pre-Conference</h2><p>Before the conference officially began, there were three pre-conference activities
for attendees: the <a href="http://www.writethedocs.org/conf/na/2016/hike/">hike to Pittock Mansion</a>,
and the <a href="http://www.writethedocs.org/conf/na/2016/writing-day/">Writing Day doc sprints</a>.
I went to both, and they were well worthwhile.</p>
<p>The hike was <a href="https://twitter.com/singingwolfboy/status/734177083627180038">gorgeous</a>,
but more strenuous than I expected. (I'm probably just more out-of-shape than
I thought.) Despite being drenched in sweat by the end of it, I had a very
good time, and had some great conversations with other conference attendees
on the hike. The topic of these conversations kept on shifting back to
<a href="https://www.getlektor.com/">Lektor</a>, since <a href="/blog/learning-lektor">I was all fired up about it</a>
and the people I talked with seemed genuinely interested. I ended up speaking
with the head honchos of documentation for <a href="https://www.linode.com/">Linode</a>,
<a href="https://www.twilio.com/">Twilio</a>, and <a href="https://twitter.com/">Twitter</a>, all of
whom started asking lots of questions about how it could be used. Hopefully
some of them checked out <a href="/blog/2016/05/learning-lektor/">my blog post about it</a>!</p>
<p>During Writing Day, I got into more conversations, and stumbled onto a very
intriguing idea. One of the other conference attendees mentioned that his team
maintained a documentation website using a CMS, but he would generally use
Google Docs for reviewing the content of that website. He would copy the full
text of a documentation page from the CMS into a Google Doc, share the Google
Doc with his team, use Google's collaboration features and commenting system
to get feedback on proposed edits, and then copy the full text back into the
CMS. I knew there had to be a better way to do this, and with Lektor on my
mind, I started brainstorming ways to integrate Google Docs into Lektor.
Over the course of the afternoon, I managed to write
<a href="https://github.com/singingwolfboy/lektor-google-drive">Lektor-Google-Drive</a>,
a Lektor plugin that allows you to embed the contents of a Google Doc
into your static site.
The OAuth flow was still a litle awkward, but it worked, and now I was more
excited than ever.</p>
<p>At 6 PM, the conference opening reception began, and everyone put away
their laptops and broke out the snacks and drinks. I had a lot of fun talking
with even <em>more</em> people (I surprised myself with how social I was), and ended
up meeting <a href="https://twitter.com/singingwolfboy/status/734579695136714752">Ducky</a>,
who is a delightful person and who introduced me to more delightful people
very quickly. Ducky was very good at catching people before they left the
reception and making instant friends, and with her help, I found two friendly
dinner companions who work in the field of video games, and they turned out to be
the best sort of nerds. (We chatted for a long time about tabletop roleplaying
games.)</p>
<h2 id="conference">Conference</h2><p>The next two days were full of conference talks, swag, unconference activities,
and ever-more people to meet. Write the Docs is a small conference of only about
400 attendees, but seeing everyone all together, it felt far bigger than I
expected. I was struck by the energetic atmosphere and the sense of generousity
and caring: not only did everyone genuinely want to attend this conference,
it seemed like everyone did so out of a sense of deep concern for the people
that they write documentation for. Documentarians just want to make the world
a clearer, friendlier, more understandable place, and they were happy to share
knowledge and make friends in the pursuit of that laudable goal.</p>
<p>I found that most of the talks weren't very interesting to me, since they were
aimed at documentarians rather than developers. However, there were two talks
that I found deeply relevant and very enjoyable.
<a href="https://twitter.com/NealKaplan">Neal Kaplan's</a>
talk about uniting documentation and support provided a universal message of
breaking down barriers and working together in pursuit of common goals, and
many people brought up his points again and again throughout the conference.
<a href="https://twitter.com/unruthless">Ruthie BenDor's</a> <a href="https://speakerdeck.com/unruthless/move-fast-and-document-things">talk</a>
about internal technical documentation was engaging in a very specific
way, since she is a developer and her talk was aimed at other developers like
myself. She provided an excellent overview of why companies do, or don't, care
about internal documentation, and how to get companies to care more about it.
I look forward to using some of these insights in the future!</p>
<p>I ended up talking with more people about Lektor and the Google Drive plugin
I wrote, and decided to do a lightning talk at the conference about it. After
the lightning talk (with a live demo that worked pretty well), <em>more</em> people
contacted me wanting to learn more about Lektor, so
I ran an <a href="http://www.writethedocs.org/conf/na/2016/unconference/">unconference session</a>
about it. A few people came and had lots of good questions, and I kept fielding
more and more questions about Lektor over the course of the conference. I enjoyed
showing off <a href="https://github.com/singingwolfboy/davidbaumgold.com">the code behind this website</a>,
and I'm hoping that one or two of these interactions might lead to a
<a href="/consulting">consulting gig</a> to help a client get up and running with Lektor.</p>
<p>Monday evening was another party at the Jack Knife bar. I was tired from doing
so much socializing, so I stayed on a couch for most of the evening. However,
by that point people had started recognizing me as "the Lektor guy", and a few
people sought me out to ask me more questions. There were more wide-ranging
conversations, including a surprisingly in-depth one about
<a href="http://us.battle.net/hearthstone">Hearthstone</a>, and I stayed at the party
longer than I thought I would.</p>
<p>Tuesday evening was the conference wrap-up, and <a href="https://twitter.com/Ducky_Tape">Ducky</a>
grabbed me for post-conference drinks and dinner with her friends
<a href="https://twitter.com/vmbrasseur">VM</a>, <a href="https://twitter.com/parisba">Paris</a>,
<a href="https://twitter.com/the_mcjones">Tim</a>, and
<a href="https://twitter.com/desplesda">Jon</a>. <a href="https://twitter.com/zachorsarah">Zach</a>
joined us at the last minute, and the seven of us had a truly zany dinner,
with conversation topics too varied and incriminating to mention.</p>
<h2 id="post-conference">Post-Conference</h2><p>I am now over-socialized, over-stimulated, and over-excited about documentation.
The people in the documentation community are knowledgeable and care deeply
about making technology understandable and accessible, and I'm glad that I got
to meet so many of them in the past few days. I now have a few days to rest,
recover, and prepare: <a href="https://us.pycon.org/2016/">PyCon</a> is right around the
corner, and that promises to be even bigger and more exciting than Write the Docs!</p>
Learning Lektorurn:uuid:f09c1609-0fd9-35ef-9718-d34a255fb97f2016-05-20T00:00:00ZDavid Baumgold<p>I've spent the last few days learning how to use <a href="https://www.getlektor.com/">Lektor</a>,
a static content management system written in Python
a hybrid between a CMS (like <a href="https://wordpress.org/">Wordpress</a> or
<a href="https://www.drupal.com/">Drupal</a>) and a static website generator
(like <a href="https://jekyllrb.com/">Jekyll</a> or <a href="https://getpelican.com">Pelican</a>).
I like Lektor a lot — so much so that I re-wrote this website from scratch
using Lektor! The code behind this site is
<a href="https://github.com/singingwolfboy/davidbaumgold.com">public on GitHub</a>, if
you're curious. Since I just learned a ton about it in a very short amount of
time, I think it's fitting that my first blog post should be about it.</p>
<h2 id="what-is-lektor?">What is Lektor?</h2><p>Lektor is a "static content management system". What does that mean, exactly?
It's basically a hybrid between a content management system
(like <a href="https://wordpress.org/">Wordpress</a> or <a href="https://www.drupal.com/">Drupal</a>)
and a static website generator
(like <a href="https://jekyllrb.com/">Jekyll</a> or <a href="https://getpelican.com">Pelican</a>).</p>
<p>Content management systems (CMS) are great because they are easy to modify. An editor
can visit an admin page, click a few buttons, type into a few text fields,
and poof: more content has been added to the website. Non-technical users
don't need to edit computer code, they can use a more friendly and familiar
interface. The downside is, CMS websites usually require complex infrastructure
(databases, search indexes, multiple webservers, etc) which is difficult and
expensive to maintain. In addition, CMS websites often have performance problems
on production, especially when they become popular. Caching can mitigate these
performance problems, but caching has its own complexities. Suffice it to say
that running a popular CMS website on production is very, very difficult.</p>
<p>Static websites are great because they are simple and easy to run
on production. A static website consists of files that are
uploaded to a web server and served directly from disk, without going through
any sort of backend programming language like Python or PHP. These files can
be HTML, CSS, JavaScript, images, or anything else that a web browser might
need to display a beautiful, functional website. Because these files are served
directly from disk, static websites are <em>dramatically</em> more performant than
CMS websites, which in turn makes them dramatically cheaper to run. The
downside is that editing static websites is difficult, especially for
non-technical editors. Using a static website <em>generator</em> like
<a href="https://jekyllrb.com/">Jekyll</a> makes it easier, because you can use a
templating system to separate content from presentation and write content
using languages like <a href="http://daringfireball.net/projects/markdown/">Markdown</a>,
which are much easier to write than HTML. However, many non-technical users
aren't comfortable editing text files or deploying files to a web server.
As a result, finding people to write and edit content for a static website
is very, very difficult.</p>
<p>Lektor is a static website generator with an integrated CMS. Lektor produces
static websites that can be run on production easily and cheaply. However,
Lektor also has an integrated admin page where editors can click buttons
and type in text fields in order to create and edit content. Lektor doesn't
use a database like a traditional CMS; instead, it stores content in files
on disk, and when editors create and edit content through the admin page,
Lektor saves those changes to those content files. It's the best of both
worlds! You can even maintain your website using a content management system
like <a href="https://git-scm.com/">Git</a>, since all the information is saved in
text files.</p>
<h2 id="lektor-and-python-3">Lektor and Python 3</h2><p>Lektor was created by <a href="http://lucumr.pocoo.org/about/">Armin Ronacher</a>, a very
prolific developer in the Python community. Armin has <a href="http://lucumr.pocoo.org/2011/12/7/thoughts-on-python3/">a complex view of
Python 3</a>, but my
understanding is that he generally views Python 3 as a mistake. Perhaps as a
result of this, Lektor is not currently compatible with Python 3 — an issue
that struck me right away when I tried to install and use it, and got an error.</p>
<p>Fortunately, porting to Python 3 is finicky but not difficult, so <a href="https://github.com/lektor/lektor/pull/207">I made a
pull request to port Lektor to Python 3</a>.
That pull request has attracted a fair amount of attention: apparently a lot
of other people also want to see Lektor run on Python 3! The pull request is
currently waiting for review, and I'm hoping that it will be reviewed and merged
soon.</p>
<h2 id="using-lektor">Using Lektor</h2><p>The first thing I had to wrap my head around when using Lektor is the "database"
system that it uses. Rather than using a traditional database like
<a href="http://postgresql.com/">PostgreSQL</a> or <a href="https://www.mysql.com/">MySQL</a>,
Lektor saves data in text files on disk, and can run queries over these files.
Although it's unfamiliar, it ends up working quite well.</p>
<p>Every page in your Lektor-powered site requires three files: a model file,
a content file, and a template file. However, many pages will share the same
model file and template file, and only the content files are unique to each page.
In a traditional CMS, the model file is like the database table, while the
content file is like the row or rows in the database that correspond to content
in the page. Template files are used to render content into HTML, the same
way they're used in a traditional CMS.</p>
<p>For example, this page you are viewing right now uses the <code>models/blog-post.ini</code>
model file, the <code>content/blog/learning-lektor/contents.lr</code> content file, and
the <code>templates/blog-post.html</code> template. Lektor mixes them all together to
produce the HTML page you see in front of you: the text of the blog post comes
from the content file, the Markdown formatting happens because the model file
says that the content file is in Markdown format, and the template displays
the rendered Markdown along with the title, sidebar, and everything else on
the page.</p>
<p>Once I understood how these three pieces fit together, I was able to speed
through very quickly, creating models, content, and templates that worked
together cleanly. I occasionally checked out the admin page and tweaked
its layout using the model files, but mostly I worked with Lektor's text
files directly, since I'm very comfortable with plain text.</p>
<h2 id="lektor-plugins">Lektor Plugins</h2><p>Like <a href="https://www.palletsprojects.com/">most of Armin Ronacher's work</a>,
Lektor is designed to be extensible — and in fact, just about any website you
build with Lektor will need at least one or two plugins to make it do what you
want. I found the plugin system to be very well designed, with only one or two
problems so far.</p>
<p>I'm using the official <a href="https://github.com/lektor/lektor-webpack-support">webpack-support plugin</a>
to handle asset management for my site. <a href="https://webpack.github.io/">Webpack</a>
is written in Node.js, and it does way more things than I'll ever understand.
Primarily, though, it bundles multiple JavaScript files into one, resolving
dependencies along the way using <a href="http://www.commonjs.org/">CommonJS</a> or
<a href="https://github.com/amdjs/amdjs-api/blob/master/AMD.md">AMD</a>. It also does the
same sort of bundling for CSS files, and can compile <a href="http://sass-lang.com/">Sass</a>
along the way. It produces <a href="http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/">source maps</a>,
dynamically recompiles files when they change, handles custom font files with
ease, and for all I know there's probably a way to make it cook you dinner
and perform backflips as well. Fortunately, the webpack-support plugin for
Lektor has some good documentation for how to get up and running with a basic
configuration, which was all I needed.</p>
<p>I also spent a lot of time learning how to write my own plugin, since I wanted
to display a table of contents for my tutorials, and the Markdown tools that
Lektor uses can't produce a table of contents on their own. Of course, after
I produced something that mostly worked, I was searching through Lektor's
documentation to find the answer to something else, and discovered that Lektor
also has <a href="https://github.com/lektor/lektor-markdown-header-anchors">an official plugin for created tables of contents</a>, and it was much simpler and better-written
than my hacked-together solution. Oops.</p>
<p>It wasn't wasted effort, though, since I ended up needing to write my own plugin
after all — or more specifically, modify the "table of contents" plugin.
I want to write Markdown without worrying about what other HTML heading tags may
already be present on the page, but I also don't want to have the headings
inside a blog post display the same way that the title of the blog post displays.
That means I needed a way to adjust the headings in the output, so that when
a blog post would normally output a <code><h1></code> tag, it becomes a <code><h2></code> tag instead.</p>
<p>Lektor's plugin system can hook directly into the Markdown processing system,
which is super useful for exactly this type of modification. However, each
part of the Markdown processing system can be modified by only one plugin at a
time, and the "table of contents" plugin was already modifying how Markdown
output headings. (In order to have a correctly linked table of contents, the
header tags need to have an ID attribute, so the links have something to target.)
When I tried writing my own Lektor plugin to adjust the heading level, it wasn't
able to run simultaneously with the "table of contents" plugin. Even worse, it
was a silent failure: Python was simply overwriting one modification with another.</p>
<p>I ended up <a href="https://github.com/singingwolfboy/lektor-markdown-header-anchors/tree/adjust-header-levels">forking the table of contents plugin</a>
to add this feature to the plugin directly. It would be nice if one plugin
could modify the output of another plugin, so that this sort of tight coupling
between plugins was unnecessary.</p>
<h2 id="lektor-is-powerful">Lektor Is Powerful</h2><p>Despite the learning curve, I'm glad I chose to use Lektor for this project.
Even though I probably won't use Lektor's admin interface very much, I'm sure
I'll build other projects with Lektor that are used by non-technical editors.
The combination of high-performance static files and easy editing capabilities
is a powerful one, and one that I expect will make a big difference
when companies select which technology they want to use for their website.</p>
<p>If you'd like me to teach you about Lektor, or build you a website with it,
let me know!</p>